缘由:从SteamGalgame OneDrive – /上面看到了这个界面,既简约又好用,所以从网络上找啊找,看F12,找了几天就只找到了Oneindex,zdir,zfile,CuteOne,sharelist这几个类似的界面。就是没找到一样的,于是感觉到可能是像OneManager-php后台可以更改界面样式的,所以直接使用笨方法,将看似关键的js直接复制后搜索。一捜不知道,向下翻了几行,果然看到了相关的github和图片界面QWQ。
于是,这篇教程在我搭建完成后,诞生了。
功能:
- 支持 国际版, 个人免费版(家庭版), 世纪互联.
- 支持同时列出多个盘的目录.(同时挂载多个网盘或单个网盘挂载成多个
SubPath
,要求每个SubPath
唯一) - 支持文件夹内超过 200 个项目
- 支持后台自动刷新缓存.
- 支持路径中含有特殊字符.
- 支持使用不同目录使用不同账户密码加密(HTTP 401).
- 支持隐藏目录和文件(跳过缓存).
- 支持自定义 ClientID 和 SecretKey .
- 数据储存在内存中,响应更加迅速.
1.准备工作
- 源码:OneList/Rewrite at master · MoeClub/OneList (github.com)
- 一台安装了宝塔面板的服务器(需要安装golang环境)
- 一个域名
- Nginx
2.开始
2.1 授权认证
根据OneDrive的类型选择:
国际版、个人版(家庭版):点击此处
世纪互联版:点击此处
这里注意:授权后会跳转至一个以localhost
开头的链接,请复制整个链接(包括localhost
)到记事本里备用,不关闭这个网页也可以。
2.2 部署OneList
选择一个文件夹来部署OneList,这里以/www/wwwroot
为例
在这个wwwroot
文件夹下新建一个文件夹名为OneList,并进入
在里面选择终端,并根据你的服务器架构选择合适的OneList版本
#64位系统下载
wget https://raw.githubusercontent.com/MoeClub/OneList/master/Rewrite/amd64/linux/OneList
#32位系统下载
wget https://raw.githubusercontent.com/MoeClub/OneList//master/Rewrite/i386/linux/OneList
#arm架构下载
wget https://raw.githubusercontent.com/MoeClub/OneList/master/Rewrite/arm/linux/OneList
如果服务器无法下载,我们通过手动下载后上传来解决:直接框选相应架构wget
后面的链接进浏览器地址栏下载,下载下来后直接上传进服务器。
打开上方的终端,给予OneList权限
chmod +x OneList
2.3 生成配置文件
上面的步骤完成后,我们继续使用终端来生成相关配置文件
#国际版,将url换成你上面复制的授权地址,包括http://loaclhost。
./OneList -a "url" -s "/onedrive01"
#个人版(家庭版),将url换成你上面复制的授权地址,包括http://loaclhost。
./OneList -ms -a "url" -s "/onedrive02"
#中国版(世纪互联),将url换成你上面复制的授权地址,包括http://loaclhost。
./OneList -cn -a "url" -s "/onedrive03"
# 在浏览器地址栏中获取以 http://loaclhost 开头的整个url内容
# 将获取的完整url内容替换命令中的 url 三个字母
# 每次产生的 url 只能用一次, 重试请重新获取 url
# 可以一个盘内的多个文件夹分别映射到多个`SubPath`上
# 此操作将会自动添加的配置文件
# 提示 Success! Add config. '/path/to/config.json' 则成功
输入相应的代码后回车,等待提示Success! Add config.'/path/to/config.json'
信息,则添加成功。
如果出现error! Add config.'/path/to/config.json'
信息,请检查你在2.1复制的链接是否正确,不正确请重新开始2.1的步骤。重新获得以http://loaclhost
开头的链接。
- 注意:授权的Url只能用一次,超过后请重新授权。
- 命令中的/onedrive01参数为指定网盘地址后缀,例如https://xxx.com/onedrive01。
- 授权多个网盘的话,重复授权多次即可,参数均会添加到一个配置文件,且后缀不能重复。
- 地址后缀错填或后期想改,可以在稍后的配置文件中修改。
2.4 配置文件相关提示
[
{
// 如果是家庭版或者个人免费版, 此项应为 true.
"MSAccount": false,
// 如果是中国版(世纪互联), 此项应为 true.
"MainLand": false,
// 授权令牌
"RefreshToken": "1234564567890ABCDEF",
// 单配置文件中,此项要唯一.将此OneDrive中设置为`RootPath`目录映射在`http://127.0.0.1:5288/onedrive` 下.
// (只推荐一个盘位的时候使用根目录"/".)
"SubPath": "/onedrive",
// 读取OneDrive的某个目录作为根目录. (支持根目录"/")
"RootPath": "/Test",
// 隐藏OneDrive目录中的文件夹和文件, 条目间使用 "|" 分割. (跳过缓存设置的条目.)
"HidePath": "/Test/Obj01|/Test/Obj02",
// 使用用户名和密码加密OneDrive目录. 目录和用户名密码间使用 "?" 分割, 用户名密码使用 ":" 分割, 条目间使用 "|" 分割. 无效条目将跳过.
"AuthPath": "/Test/Auth01?user01:pwd01|/Test/Auth02?user02:pwd02",
// 缓存刷新间隔.(所有项目中的刷新时间取最小值为有效刷新间隔)
"RefreshInterval": 900
}
]
如果挂载多个盘符,SubPath参数请不要为/,单个网盘建议为/
2.5 启动OneList
在OneList文件夹下启动终端,运行一下命令,来启动项目。
#下载默认的index.html主题,与config.json同目录,即本文默认的/www/wwwroot/OneList
wget https://raw.githubusercontent.com/MoeClub/OneList/master/Rewrite/index.html
#监听8000地址,自行修改
/www/wwwroot/OneList/OneList -bind 0.0.0.0 -port 8000
注意:这里的终端一旦关闭OneList将停止运行,稍后解决这个问题。
如果你在配置文件里把SubPath
的参数修改为/
时,浏览器地址栏输入<ip:端口号
>即可访问。
如果你在挂载网盘时的时候SubPath
没有修改,而且是世纪互联版的话,输入<ip:端口号/onedrive01
>就可以访问到了。
注意:如果文件路径不存在,则会显示No Found,如果目录下没有文件也会显示No Found。
我们把终端关闭。接下来解决后台运行的问题。
3. 后台运行和开机自启
3.1 安装进程守护管理器
在宝塔面板的软件商店里搜索“进程守护管理器”,点击安装。
安装好后,点击右边的“设置”,点击“添加守护进程”按钮。
名称随便填,然后在启动命令中填入
/www/wwwroot/OneList/OneList -bind 0.0.0.0 -port 8000
进程目录
/www/wwwroot/OneList
当状态显示RUNNING
,就说明配置正确。显示其他状态请检查配置,首先可以看看是否已经在终端启动过OneList了,如果是在终端启动,请直接关闭终端再试。
注意,/**/**/OneList
均需要根据你的安装位置来确定目录,如果不行,可以尝试将目录直接定位到OneList文件,或者在最后添加/
这样就可以通过“进程守护管理器”看到OneList的运行状态了,并且可以自动修复异常情况和开机自启。
4. 设置域名访问
宝塔面板新建网站,找到设置,创建反相XX,在目前URL里填写http://127.0.0.1:8000,点击提交即可
后面就可以用域名打开了,如果要设置SSL,需要先把这关闭,申请好证书再开启。
5. OneList的使用命令
(基本用不到)
Usage of OneList:
-a string
// 初始化配置文件,添加新配置
Setup and Init auth.json.
-bind string
// 绑定IP地址(公网: 0.0.0.0)
Bind Address (default "127.0.0.1")
-port string
// 绑定端口(HTTP:80)
Port (default "5288")
-s string
// 设置 SubPath 项, 需要与 -a 一起使用.
Set SubPath. [unique per account] (default "/")
-c string
// 配置文件
Config file. (default "config.json")
-t string
// Index.html 目录样式文件
Index file. (default "index.html")
-cn
// 开关
// 授权中国版(世纪互联), 需要此参数.
OneDrive by 21Vianet.
-ms
// 开关
// 授权个人版(家庭版), 需要此参数.
OneDrive by Microsoft.
6. 界面样式的更改
直接将OneList目录下的index.html文件替换,然后重启程序即可
1.HaorWu
- 特点
- 支持移动端自适应
- 支持当页搜索
- 支持按文件名,日期,大小排序
- 支持主动查看图片
- 支持在线播放视频
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge, Chrome=1">
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1,viewport-fit=cover">
<title>GalGame记录页</title>
<link href="https://s0.pstatp.com/cdn/expire-1-M/mdui/0.4.2/css/mdui.min.css" rel="stylesheet">
<link href="https://s0.pstatp.com/cdn/expire-1-M/dplayer/1.25.0/DPlayer.min.css" rel="stylesheet">
<style>
body{background-color:#f2f5fa}.nexmoe-item{margin:15px 0 !important;padding:15px !important;border-radius:5px;background-color:#fff;-webkit-box-shadow:0 .5em 3em rgba(161,177,204,.4);box-shadow:0 .5em 3em rgba(161,177,204,.4);background-color:#fff}.mdui-img-fluid,.mdui-video-fluid{border-radius:5px;border:1px solid #eee}.mdui-list{padding:0}.mdui-list-item{margin:0 !important;border-radius:5px;padding:0 10px 0 5px !important;border-bottom:1px solid #eee;margin-bottom:10px !important}.mdui-list-item:last-child{margin-bottom:0 !important}.mdui-toolbar{width:auto}.mdui-appbar .mdui-toolbar{height:56px;font-size:16px}.mdui-toolbar>*{padding:0 6px;margin:0 2px}.mdui-toolbar>.mdui-typo-headline{padding:0 16px 0 0}.mdui-toolbar>i{padding:0}.mdui-toolbar h3.title{padding:0 16px;line-height:30px;border-radius:30px;border:1px solid #eee;opacity:1;background-color:#1e89f2;color:#ffff}.mdui-toolbar>a:hover,a.mdui-typo-headline,a.active{opacity:1}.mdui-list>.th{background-color:initial}.mdui-list-item>a{width:100%;line-height:48px}.mdui-toolbar>a{padding:0 16px;line-height:30px;border-radius:30px;border:1px solid #eee}.mdui-toolbar>a:last-child{opacity:1;background-color:#1e89f2;color:#ffff}@media screen and (max-width:980px){.mdui-list-item .mdui-text-right{display:none}.mdui-container{width:100% !important;margin:0}.mdui-toolbar>a:last-child,.mdui-toolbar>.mdui-typo-headline,.mdui-toolbar>i:first-child{display:block}}#main-page{cursor:pointer}.nav-a{text-decoration:none;color:#333}.nav-a:hover{text-decoration:underline}.file{width:100%;display:flex;align-items:center}.file a{color:unset;width:100%}#text-input,#close{display:none}#text-input{width:40%}#dplayerContainer{position:relative;background-color:#000;display:none;padding:10px}.close-icon{color:#fff}#viewerContainer{width:100%;height:100%}.viewer-img{width:450px;position:relative;left:30%;z-index:1000}span.overlay{position:fixed;top:0;right:0;bottom:0;left:0;background-color:rgba(0,0,0,.8);z-index:999}#viewerClose{z-index:1000;color:#fff;background:rgba(255,255,255,.12);display:inline-block;position:absolute;bottom:0;cursor:pointer}@media screen and (max-width:768px){.viewer-img{width:60%;position:relative;left:20%;z-index:1000}}
</style>
</head>
<body>
<div class="container mdui-container">
<div class="mdui-container-fluid">
<div class="mdui-toolbar nexmoe-item nav">
<i class="mdui-list-item-icon mdui-icon material-icons mdui-text-color-blue" id="main-page" onclick="goto(rootPath)">home</i>
<span id="path">/</span>
<div class="mdui-toolbar-spacer"></div>
<input type="text" id="text-input" class="mdui-textfield-input" oninput="search(this)" placeholder="请输入关键字">
<button type="button" id="close" class="mdui-textfield-close mdui-btn mdui-btn-icon"><i class="mdui-icon material-icons">close</i></button>
<button type="button" id="btn" class="mdui-textfield-icon mdui-btn mdui-btn-icon"><i class="mdui-icon material-icons">search</i></button>
</div>
</div>
<div class="mdui-container-fluid">
<div class="list-wrapper nexmoe-item">
<div class="list-header">
<div class="file mdui-list-item th">
<span class="name mdui-col-xs-12 mdui-col-sm-7" onclick="view('name')">列表</span>
<span class="time mdui-col-sm-3 mdui-text-right" onclick="view('date')">时间</span>
<span class="size mdui-col-sm-2 mdui-text-right" onclick="view('size')">大小</span>
</div>
</div>
<div id="file-list"></div>
</div>
</div>
</div>
<script src="https://s0.pstatp.com/cdn/expire-1-M/??mdui/0.4.0/js/mdui.min.js,dplayer/1.25.0/DPlayer.min.js"></script>
<script>
let domain = window.location.host,
rootPath = "{{.RootPath}}",
currentPath = "{{.CurrentPath}}",
rawData = "{{.RawData}}",
title = 'GalGame记录页',
$ = mdui.JQ;
if (currentPath === "/") {
currentPath = "";
}
let reverse = false,
pageData = JSON.parse(window.atob(rawData)),
arrayPath = new Array(),
arrayFloder = new Array(),
arryVideo = new Array(),
arrayFile = new Array();
for (let item in pageData) {
if (item.indexOf("@") == 0) {
continue
}
if (getFileType(decodeURIComponent(pageData[item]['name'])) == "video") {
arryVideo.push(decodeURIComponent(pageData[item]['name']))
}
if (pageData[item]['@type'] == 'file') {
arrayFile.push(pageData[item])
} else if (pageData[item]['@type'] == 'folder') {
arrayFloder.push(pageData[item])
}
}
function goto(thePath) {
window.location.href = thePath
}
function sizeNum(Size) {
let dataArray = Size.split(" ", 2),
dataNum = 0;
switch (dataArray[1]) {
case "B":
dataNum = Math.pow(2, 0) * dataArray[0];
break;
case "KB":
dataNum = Math.pow(2, 10) * dataArray[0];
break;
case "MB":
dataNum = Math.pow(2, 20) * dataArray[0];
break;
case "GB":
dataNum = Math.pow(2, 30) * dataArray[0];
break;
case "TB":
dataNum = Math.pow(2, 40) * dataArray[0];
break;
case "PB":
dataNum = Math.pow(2, 50) * dataArray[0];
break;
default:
dataNum = 2 ** 50 * dataArray[0]
}
return dataNum
}
function compare(property) {
if (property === "size") {
return function(a, b) {
let value0 = sizeNum(a[property].toUpperCase());
let value1 = sizeNum(b[property].toUpperCase());
return value0 - value1
}
} else {
return function(a, b) {
let value0 = a[property].toLowerCase();
let value1 = b[property].toLowerCase();
return value0.localeCompare(value1)
}
}
}
function clear() {
let classList = new Array("file-wrapper");
for (let item in classList) {
let obj = document.getElementsByClassName(classList[item]);
for (let i = obj.length - 1; i >= 0; i--) {
obj[i].parentNode.removeChild(obj[i])
}
}
let nav = document.getElementById("path"),
pathSpan = document.createElement("span"),
locPath = currentPath.trim().replace(/^\//, "").replace(/\/$/, "");
locArray = new Array('');
if (nav.innerHTML.length > 0) {
nav.innerHTML = ''
}
if (locPath !== "") {
let locPathArr = locPath.split("/"),
localHref = "";
for (let j = 0; j < locPathArr.length; j++) {
localHref += "/" + locPathArr[j];
if (j === 0 && rootPath !== "/") {
continue
}
navTemp = `<a class="nav-a" href="https://${window.location.host}${localHref}">${locPathArr[j]}</a>`;
locArray.push(navTemp)
}
}
pathSpan.innerHTML = locArray.join("/");
nav.appendChild(pathSpan);
navText = (nav.innerHTML.length === 0) ? '/' : pathSpan.innerHTML.replace(/<[^>]+>/g, "");
document.title += navText.substr(1);
arrayPath = locArray
}
function search(obj) {
let searchVal = obj.value;
if (searchVal === undefined) {
return
} else {
searchVal = searchVal.toString().toLowerCase()
}
let showArray = document.getElementsByClassName("file-wrapper");
for (let i = 0; i < showArray.length; i++) {
let content = showArray[i].children[0].querySelector("span.mdui-text-truncate").innerText;
if (content !== undefined && content.length > 0) {
let newAttr = '';
if (content.toLowerCase().indexOf(searchVal) < 0) {
newAttr = "none"
}
showArray[i].style.display = newAttr
}
}
}
function view(Property) {
arrayFloder.sort(compare(Property));
arrayFile.sort(compare(Property));
if (reverse) {
arrayFloder.reverse();
arrayFile.reverse();
}
reverse = !(reverse);
clear();
let obj = document.getElementById('file-list'),
items = arrayFloder.concat(arrayFile);
for (let item in items) {
let newChild = document.createElement("div"),
icon = (items[item]['@type'] == 'folder') ? 'folder_open' : 'image_aspect_ratio',
itemName = decodeURIComponent(items[item]['name']),
protocol = (document.location.protocol == 'https:') ? 'https:' : 'http:';
let href = protocol + '//' + domain + currentPath + '/' + itemName;
let fileType = getFileType(itemName);
if (icon == 'image_aspect_ratio') {
icon = getFileIcon(fileType)
}
newChild.setAttribute('class', 'row file-wrapper mdui-list-item mdui-ripple');
newChild.innerHTML = `
<div class="file">
<i class="mdui-icon material-icons">${icon}</i>
<a href="javascript:;">
<span class="name mdui-col-xs-12 mdui-col-sm-7 mdui-text-truncate">${itemName}</span>
<span class="time mdui-col-sm-3 mdui-text-right">${items[item]['date']}</span>
<span class="size mdui-col-sm-2 mdui-text-right">${items[item]['size']}</span>
</a>
</div>`;
obj.appendChild(newChild);
newChild.addEventListener('click',function(e){
if (fileType == "video") {
let dplayerContainer = `
<div id="dplayerContainer" class="mdui-container-fluid">
<div align="right">
<button type="button" id="closevideo" onclick="dpClose()" class="mdui-textfield-close mdui-btn mdui-btn-icon"><i class="mdui-icon material-icons close-icon">close</i></button>
</div>
<div id="dplayer"></div>
</div>`;
$('#dplayerContainer').remove();
$(newChild).after(dplayerContainer);
dpOpen(href)
}else if(fileType == "image"){
let imgEle = `
<div id="viewerContainer" onclick="$(this).remove()">
<span class="overlay"></span>
<img class="viewer-img" src="https://www.qfya.com/${href}">
</div>
`;
$('#viewerContainer').remove();
$(newChild).after(imgEle)
}else{
window.location.href = href
}
},false)
newChild.oncontextmenu = function() {
$(newChild).find('a').attr('href', href)
}
}
}
function getFileType(name) {
if (!name) return false;
let imgType = ['gif', 'jpeg', 'jpg', 'bmp', 'png'],
videoType = ['avi', 'wmv', 'mkv', 'mp4', 'mov', '3gp', 'flv', 'mpg', 'rmvb'],
textType = ['txt', 'pdf', 'css', 'js', 'text', 'doc', 'docx', 'ppt', 'xml'],
musicType = ['wav', 'acc', 'flac', 'ape', 'ogg', 'mp3'];
if (RegExp("\.(" + imgType.join("|") + ")$", "i").test(name.toLowerCase())) {
return 'image'
}else if (RegExp("\.(" + videoType.join("|") + ")$", "i").test(name.toLowerCase())) {
return 'video'
}else if (RegExp("\.(" + textType.join("|") + ")$", "i").test(name.toLowerCase())) {
return 'text'
}else if (RegExp("\.(" + musicType.join("|") + ")$", "i").test(name.toLowerCase())) {
return 'music'
} else {
return false
}
}
function getFileIcon(fileType){
switch(fileType) {
case 'image':
icon = 'image';
break;
case 'video':
icon = 'ondemand_video';
break;
case 'music':
icon = 'music_video';
break;
case 'text':
icon = 'text_fields';
break;
default:
icon = 'insert_drive_file';
break;
}
return icon
}
function dpClose() {
$('#dplayerContainer').remove();
if (dp) {dp.destroy()}
}
function dpOpen(link) {
$('#dplayerContainer').show();
dp = new DPlayer({
container: document.getElementById('dplayer'),
autoplay: true,
video: {
url: link,
},
})
}
$('#btn').on('click', function(event) {
event.preventDefault();
$('#text-input').show();
$('#close').show()
$('#btn').hide()
});
$('#close').on('click', function(event) {
event.preventDefault();
$('#text-input').hide();
$('#close').hide();
$('#btn').show()
});
window.onload = view("name")
</script>
</body>
</html>
2. jackjieYYY
最后如果我们只想显示网盘的某些文件夹,那么可以分别挂载该网盘的不同目录即可,具体操作看配置文件说明