检查角色卡是否更新并下载
实现
这要求你有一个永久的下载地址,能以某种方式得知版本需要更新
可选做法是直接用 git 把角色卡存在某网站上(如果也想用请参考此处),因而:
- 每次更新都会有一个独特的 commit 标识
- 最新版本始终是 main
- 网站提供了比较 commit 和 main 之间变动的方法
注意
需要注意的是,出于安全性考虑,浏览器默认情况下不会从某些网址下载文件,并在 f12 的控制台提示:
Access to fetch at 'https://<target website>' from origin 'https://<your website>' in frame has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
示例
点击查看示例
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Version Checker</title>
<style>
body {
background-color: white;
}
.version-container {
text-align: center;
margin-top: 50px;
}
#version-text {
font-size: 18px;
margin-bottom: 20px;
}
#check-button {
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
background-color: #ffffff;
border: 1px solid #cccccc;
border-radius: 4px;
}
#check-button:hover {
background-color: #f5f5f5;
}
.current-version {
color: black;
}
.latest-version {
color: green;
}
.update-available {
color: red;
}
</style>
</head>
<body>
<div class="version-container">
<div id="version-text" class="current-version">
<span id="commit-display">2b74088f</span>
</div>
<button id="check-button">检查更新</button>
</div>
<script>
const config = {
gitlab_domain: 'gitgud.io',
project_path: 'SmilingFace/tavern_resource',
file_path: '角色卡/妹妹请求你保护她/妹妹请求你保护她.png',
current_commit: '2b74088f',
target_commit: 'main'
};
// 初始显示当前 commit 值
document.getElementById('commit-display').textContent = config.current_commit;
let isChecking = false;
document.getElementById('check-button').addEventListener('click', async function () {
if (isChecking) return;
isChecking = true;
const button = this;
const versionText = document.getElementById('commit-display');
try {
const hasUpdate = await gitlab.checkFileChanged(
config.gitlab_domain,
config.project_path,
config.file_path,
config.current_commit,
config.target_commit
);
if (hasUpdate) {
versionText.textContent = '有可用更新';
versionText.parentElement.className = 'update-available';
button.textContent = '点击进行更新';
button.onclick = () => gitlab.confirmAndDownload(
config.gitlab_domain,
config.project_path,
config.file_path,
config.target_commit
);
} else {
versionText.textContent = '已是最新版本';
versionText.parentElement.className = 'latest-version';
setTimeout(() => {
versionText.textContent = config.current_commit;
versionText.parentElement.className = 'current-version';
isChecking = false;
}, 3000);
}
} catch (error) {
alert(`检查更新失败: ${error}`);
isChecking = false;
}
});
</script>
<script>
var download;
(function (download_1) {
function confirmToDownload(download_link, file_name) {
if (!confirm(`你确定要从 ${download_link} 下载 ${file_name} 吗?`)) {
return false;
}
return true;
}
download_1.confirmToDownload = confirmToDownload;
async function download(response, file_name) {
return response
.then(response => response.blob())
.then(blob => {
const object_url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = object_url;
a.download = `${file_name}`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(object_url);
}).catch(reason => alert(`下载失败: ${reason}`));
}
download_1.download = download;
function getBaseName(filepath) {
return filepath.match(/[^\\/]+$/)?.[0] ?? filepath;
}
download_1.getBaseName = getBaseName;
})(download || (download = {}));
var gitlab;
(function (gitlab) {
function toGitlabApi(gitlab_domain, project_path) {
return `https://${gitlab_domain}/api/v4/projects/${encodeURIComponent(project_path)}`;
}
gitlab.toGitlabApi = toGitlabApi;
async function fetchRawFile(gitlab_domain, project_path, file_path, ref = 'main') {
return globalThis.fetch(`${toGitlabApi(gitlab_domain, project_path)}/repository/files/${encodeURIComponent(file_path)}/raw?ref=${ref}`);
}
gitlab.fetchRawFile = fetchRawFile;
async function confirmAndDownload(gitlab_domain, project_path, file_path, ref = 'main') {
const file_name = download.getBaseName(file_path);
if (!download.confirmToDownload(`${gitlab_domain}/${project_path}/${file_path}?ref=${ref}`, download.getBaseName(file_name))) {
return;
}
return download.download(fetchRawFile(gitlab_domain, project_path, file_path, ref), file_name);
}
gitlab.confirmAndDownload = confirmAndDownload;
async function checkFileChanged(gitlab_domain, project_path, file_path, from_commit, to_commit = 'main') {
try {
const response = await globalThis.fetch(`${toGitlabApi(gitlab_domain, project_path)}/repository/compare?from=${from_commit}&to=${to_commit}&path=${encodeURIComponent(file_path)}`);
if (!response.ok) {
throw new Error(`获取更新情况时出错: ${response.status}`);
}
const data = await response.json();
return data.diffs.some((diff) => diff.old_path === file_path || diff.new_path === file_path);
}
catch (error) {
console.error(`${error}`);
throw error;
}
}
gitlab.checkFileChanged = checkFileChanged;
})(gitlab || (gitlab = {}));
</script>
</body>
</html>
typescript
// <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
const config = {
gitlab_domain: "gitgud.io",
project_path: "SmilingFace/tavern_resource",
file_path: "角色卡/妹妹请求你保护她/妹妹请求你保护她.png",
from_commit: "eabaf643",
}
async function check() {
return gitlab.checkFileChanged(config.gitlab_domain, config.project_path, config.file_path, config.from_commit);
}
async function update() {
return gitlab.confirmAndDownload(config.gitlab_domain, config.project_path, config.file_path, config.from_commit);
}
namespace download {
export function confirmToDownload(download_link: string, file_name: string): boolean {
if (!confirm(`你确定要从 ${download_link} 下载 ${file_name} 吗?`)) {
return false;
}
return true;
}
export async function download(response: Promise<Response>, file_name: string): Promise<void> {
return response
.then(response => response.blob())
.then(blob => {
const object_url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = object_url;
a.download = `${file_name}`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(object_url);
}).catch(reason => alert(`下载失败: ${reason}`));
}
export function getBaseName(filepath: string): string {
return filepath.match(/[^\\/]+$/)?.[0] ?? filepath
}
}
namespace gitlab {
export function toGitlabApi(gitlab_domain: string, project_path: string) {
return `https://${gitlab_domain}/api/v4/projects/${encodeURIComponent(project_path)}`
}
export async function fetchRawFile(
gitlab_domain: string,
project_path: string,
file_path: string,
ref: string = 'main'
): Promise<Response> {
return fetch(`${toGitlabApi(gitlab_domain, project_path)}/repository/files/${encodeURIComponent(file_path)}/raw?ref=${ref}`);
}
export async function confirmAndDownload(
gitlab_domain: string,
project_path: string,
file_path: string,
ref: string = 'main'
): Promise<void> {
const file_name = download.getBaseName(file_path);
if (!download.confirmToDownload(`${gitlab_domain}/${project_path}/${file_path}?ref=${ref}`, download.getBaseName(file_name))) {
return;
}
return download.download(fetchRawFile(gitlab_domain, project_path, file_path, ref), file_name);
}
export async function checkFileChanged(
gitlab_domain: string,
project_path: string,
file_path: string,
from_commit: string,
to_commit: string = 'main'): Promise<boolean> {
try {
const response = await fetch(`${toGitlabApi(gitlab_domain, project_path)}/repository/compare?from=${from_commit}&to=${to_commit}&path=${encodeURIComponent(file_path)}`);
if (!response.ok) {
throw new Error(`获取更新情况时出错: ${response.status}`);
}
const data = await response.json();
return data.diffs.some((diff: any) => diff.old_path === file_path || diff.new_path === file_path);
} catch (error) {
console.error(`${error}`);
throw error;
}
}
}