35 Commits

Author SHA1 Message Date
鲁树人
c4f07f08dc chore: update references to old urls
Some checks failed
Build / build (push) Has been cancelled
2025-05-09 10:38:10 +09:00
鲁树人
c9d1cbac09 docs: update license year 2025-05-09 10:37:29 +09:00
鲁树人
9f2a74e1d6 docs: update readme re. ci
Some checks failed
Build / build (push) Has been cancelled
2025-05-09 06:07:21 +09:00
鲁树人
abda8cc1bb ci: fix pipeline typo 2025-05-09 06:04:51 +09:00
鲁树人
25aec6b199 ci: simplify build command 2025-05-09 06:03:48 +09:00
鲁树人
1676f9f095 build: copy readme and license to dist 2025-05-09 05:57:53 +09:00
鲁树人
bf96544a51 ci: add build pipeline 2025-05-09 05:56:39 +09:00
鲁树人
80d79d0f65 build: fix extension maker 2025-05-09 05:56:29 +09:00
鲁树人
b0ee928199 build: support node v22 2025-05-09 05:46:18 +09:00
鲁树人
f22b05f40d chore: move extension build script to scripts; update browser db 2025-05-09 05:33:26 +09:00
鲁树人
6addbb5ecd chore: pipeline / issue template cleanup 2025-05-09 05:19:07 +09:00
鲁树人
7f050a682d Merge pull request 'Add support for Firefox addon / 提供对 Firefox 拓展的支持' (#5) from THR-hub/web:main into main
Reviewed-on: https://git.unlock-music.dev/um/web/pulls/5
2025-04-06 00:05:01 +00:00
THR-hub
ce45bdb8dd 更新 extension-manifest.json
Add extension ID which adds support for Firefox addon.
通过添加 extension ID 提供对 Firefox 拓展的支持。

Signed-off-by: THR-hub <thr-hub@noreply.unlock-music.dev>
2025-04-05 23:22:02 +00:00
鲁树人
1846e5c959 1.10.8 2024-10-21 04:35:11 +09:00
鲁树人
c866d4909f chore: update browserlist db 2024-10-14 10:26:59 +09:00
鲁树人
bded04e40f fix #90: update to manifest v3 2024-10-14 02:01:25 +01:00
鲁树人
b7cb485be2 docs: format with prettier 2024-10-13 05:38:27 +09:00
鲁树人
3dcf064b48 docs: add do not fork notice 2024-10-13 05:35:35 +09:00
鲁树人
437765f79f chore: bump version to v1.10.7 2024-09-29 18:22:11 +01:00
鲁树人
584fb24fdd fix: joox skip metadata fetch if api fails 2024-09-29 18:20:44 +01:00
鲁树人
2835ac08ec chore: import joox from local npm repo 2024-09-29 18:20:24 +01:00
鲁树人
201e44fb38 Merge pull request 'remerge https://git.unlock-music.dev/um/web/pulls/30' (#67) from kylebing/web:master into master
Reviewed-on: https://git.unlock-music.dev/um/web/pulls/67
Reviewed-by: jixunmoe <jixunmoe@noreply.unlock-music.dev>
2023-09-25 20:36:30 +00:00
KyleBing
bdb8d6da34 Merge remote-tracking branch 'upstream/master'
# Conflicts:
#	src/App.vue
#	src/view/Home.vue
2023-09-25 12:15:18 +08:00
KyleBing
1429c5786e update to vh 2023-09-25 12:13:36 +08:00
鲁树人
c311fb7f5d 1.10.6 2023-08-28 18:28:19 +01:00
鲁树人
821d936b2c Merge pull request 'bugfix for #36 #50' (#59) from nullptr-0/web:pr into master
Reviewed-on: https://git.unlock-music.dev/um/web/pulls/59
Reviewed-by: jixunmoe <jixunmoe@noreply.unlock-music.dev>
2023-08-28 17:10:02 +00:00
nullptr-0
dfe69c8d1f bugfix for #36 #50 2023-08-29 00:29:05 +08:00
鲁树人
a8acb25cd7 Merge pull request 'docs/issue-template-2' (#55) from docs/issue-template-2 into master
Reviewed-on: https://git.unlock-music.dev/um/web/pulls/55
2023-07-15 20:02:59 +00:00
鲁树人
36774c165e chore: add more template 2023-07-15 20:59:49 +01:00
鲁树人
573435efc9 chore: issue template update 2023-07-15 20:24:18 +01:00
鲁树人
18a22099d0 Merge pull request '将旧的 GitHub 项目链接更换到新的域名' (#54) from fix/docs into master
Reviewed-on: https://git.unlock-music.dev/um/web/pulls/54
2023-07-15 19:12:02 +00:00
鲁树人
a788f48b67 docs: update issue templates 2023-07-15 20:10:59 +01:00
鲁树人
6f405b5e06 chore: replace url to old github project (fix #53) 2023-07-15 20:10:52 +01:00
邴新科
820b98afed 编辑歌曲信息界面优化 2023-02-03 14:28:37 +08:00
邴新科
d567e9f136 优化显示:组件上下间隔,footer 位置置于页面底部 2023-02-03 12:08:59 +08:00
38 changed files with 925 additions and 670 deletions

View File

@@ -1,25 +0,0 @@
---
kind: pipeline
type: docker
name: default
steps:
- name: build
image: node:16.18-bullseye
commands:
- apt-get update
- apt-get install -y jq zip
- npm ci
- npm run test
- ./scripts/build-and-package.sh legacy
- ./scripts/build-and-package.sh extension
- ./scripts/build-and-package.sh modern
- name: upload artifact
image: node:16.18-bullseye
environment:
DRONE_GITEA_SERVER: https://git.unlock-music.dev
GITEA_API_KEY:
from_secret: GITEA_API_KEY
commands:
- ./scripts/upload-packages.sh

View File

@@ -0,0 +1,45 @@
---
name: "错误报告"
about: "报告 Bug 以帮助改进程序,非填表。"
title: "[BUG] "
labels:
- bug
---
* 请按照此模板填写,否则可能立即被关闭。请将符合条件的 `[ ]` 更改为 `[x]`
- [ ] 我确认已经搜索过 issue确认没有已报告的相同 issue
- [ ] 我有证据表明这是程序导致的问题(如不确认,可以通过 Telegram 讨论组 (https://t.me/unlock_music_chat) 进行讨论)
## Bug描述
简要地复述你遇到的 Bug
……
## 复现方法
描述复现方法,必要时请提供样本文件:
……
## 程序截图或浏览器开发者控制台Console的报错信息
……
## 环境信息
- 操作系统和浏览器:
- 程序版本:
- 网页版的地址(如果为非官方部署请注明):
注意:如果需要会员才能获取该资源,你可能也需要作为附件提交。
## 附加信息
如果有其他能够帮助确认问题的信息,请在下方填写:
……

View File

@@ -0,0 +1,28 @@
---
name: "新功能"
about: "对于程序新的想法或建议"
title: "[新功能] "
labels:
- enhancement
---
<!-- ⚠ 请按照此模板填写,否则可能立即被关闭 -->
<!-- 提交前可使用【Preview】预览提交的更改 -->
## 背景和说明
<!-- 简要说明产生此想法的背景和此想法的具体内容 -->
## 附加信息
<!-- 更多你想要表达的内容 -->
## 额外选项
<!-- 请将符合条件的 `[ ]` 更改为 `[x]` -->
- [ ] 我可以自行实现并提交 PR。
- [ ] 我确认已经搜索过 issue确认没有已报告的相同 issue

View File

@@ -0,0 +1,42 @@
name: Build
on:
workflow_dispatch:
push:
paths:
- "src/**/*"
- "package.json"
- "package-lock.json"
pull_request:
branches: [ main ]
types: [ opened, synchronize, reopened ]
paths:
- "src/**/*"
- "package.json"
- "package-lock.json"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout codebase
uses: actions/checkout@v4
- name: Set up node
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
- name: Install dependencies
run: npm ci
- name: Test
run: npm test
- name: Build all variants
run: npm run build:all
- name: Publish artifact
uses: christopherhx/gitea-upload-artifact@v4
with:
name: um-web-all
path: "um-*.zip"

View File

@@ -1,47 +0,0 @@
image: node:16
cache:
paths:
- node_modules/
stages:
- build
build-job:
stage: build
script: |
sed -i 's/deb.debian.org/mirrors.cloud.tencent.com/g' /etc/apt/sources.list
apt-get update
apt-get -y install zip
npm config set registry http://mirrors.cloud.tencent.com/npm/
npm ci
npm run build
tar -czf legacy.tar.gz -C ./dist .
cd dist
zip -rJ9 ../legacy.zip *
cd ..
npm run make-extension
cd dist
zip -rJ9 ../extension.zip *
cd ..
npm run build -- --modern
tar -czf modern.tar.gz -C ./dist .
cd dist
zip -rJ9 ../modern.zip *
cd ..
sha256sum *.tar.gz *.zip > sha256sum.txt
artifacts:
name: "$CI_JOB_NAME"
paths:
- legacy.zip
- legacy.tar.gz
- extension.zip
- modern.zip
- modern.tar.gz
- sha256sum.txt

View File

@@ -1,39 +0,0 @@
---
name: Bug报告
about: 报告Bug以帮助改进程序
title: ''
labels: bug
assignees: ''
---
* 请按照此模板填写,否则可能立即被关闭
- [x] 我确认已经搜索过Issue不存并确认相同的Issue
- [x] 我有证据表明这是程序导致的问题(如不确认,可以在[Discussions](https://github.com/ix64/unlock-music/discussions)内提出)
**Bug描述**
简要地复述你遇到的Bug
**复现方法**
描述复现方法,必要时请提供样本文件
**程序截图或者Console报错信息**
如果可以请提供二者之一
**环境信息:**
- 操作系统和浏览器:
- 程序版本:
- 获取音乐文件所使用的客户端及其版本信息:
**附加信息**
其他能够帮助确认问题的信息

View File

@@ -1,26 +0,0 @@
---
name: 新功能
about: 对于程序新的想法或建议
title: ''
labels: enhancement
assignees: ''
---
- 请按照此模板填写,否则可能立即被关闭
**背景和说明**
简要说明产生此想法的背景和此想法的具体内容
**实现途径**
- 如果没有设计方案,请简要描述实现思路
- 如果你没有任何的实现思路,请通过[Discussions](https://github.com/ix64/unlock-music/discussions)或者Telegram进行讨论
**附加信息**
更多你想要表达的内容

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
@unlock-music:registry=https://git.unlock-music.dev/api/packages/um/npm/

View File

@@ -3,7 +3,7 @@ FROM --platform=$TARGETPLATFORM nginx:stable-alpine
LABEL org.opencontainers.image.title="Unlock Music"
LABEL org.opencontainers.image.description="Unlock encrypted music file in browser"
LABEL org.opencontainers.image.authors="MengYX"
LABEL org.opencontainers.image.source="https://github.com/ix64/unlock-music"
LABEL org.opencontainers.image.source="https://git.unlock-music.dev/um/web"
LABEL org.opencontainers.image.licenses="MIT"
LABEL maintainer="MengYX"

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019-2023 MengYX
Copyright (c) 2019-2025 MengYX
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,17 +1,20 @@
# Unlock Music 音乐解锁
[![Build Status](https://ci.unlock-music.dev/api/badges/um/web/status.svg)](https://ci.unlock-music.dev/um/web)
[![Build Status](https://git.unlock-music.dev/um/web/actions/workflows/build.yml/badge.svg)][ci]
- 在浏览器中解锁加密的音乐文件。 Unlock encrypted music file in the browser.
- Unlock Music 项目是以学习和技术研究的初衷创建的,修改、再分发时请遵循[授权协议]。
- Unlock Music 的 CLI 版本可以在 [unlock-music/cli] 找到,大批量转换建议使用 CLI 版本。
- 我们新建了 Telegram 群组 [`@unlock_music_chat`] ,欢迎加入!
- CI 自动构建已经部署,可以在 [UM-Packages] 下载
- CI 自动构建已经部署,可以在 [Actions][ci] 下载
[授权协议]: https://git.unlock-music.dev/um/web/src/branch/master/LICENSE
> **WARNING**
> 在本站 fork 不会起到备份的作用,只会浪费服务器储存空间。如无必要请勿 fork 该仓库。
[授权协议]: https://git.unlock-music.dev/um/web/src/branch/main/LICENSE
[unlock-music/cli]: https://git.unlock-music.dev/um/cli
[`@unlock_music_chat`]: https://t.me/unlock_music_chat
[UM-Packages]: https://git.unlock-music.dev/um/-/packages/generic/web-build/
[ci]: https://git.unlock-music.dev/um/web/actions?workflow=build.yml
## 特性
@@ -26,7 +29,7 @@
- [x] 虾米音乐格式 (.xm)
- [x] 酷我音乐格式 (.kwm)
- [x] 酷狗音乐格式 (.kgm/.vpr)
- [x] Android版喜马拉雅文件格式 (.x2m/.x3m)
- [x] Android 版喜马拉雅文件格式 (.x2m/.x3m)
- [x] 咪咕音乐格式 (.mg3d)
### 其他特性
@@ -42,7 +45,7 @@
### 使用预构建版本
- 从 [Release] 或 [CI 构建][UM-Packages] 下载预构建的版本
- 从 [Release] 或 [CI 构建][ci] 下载预构建的版本
- :warning: 本地使用请下载`legacy版本``modern版本`只能通过 **http(s)协议** 访问)
- 解压缩后即可部署或本地使用(**请勿直接运行源代码**
@@ -51,7 +54,7 @@
### 自行构建
- 环境要求
- nodejs (v16.x)
- nodejs (v22.x)
- npm
1. 获取项目源代码后安装相关依赖:

View File

@@ -1,16 +1,29 @@
{
"manifest_version": 2,
"manifest_version": 3,
"content_security_policy": {
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';"
},
"name": "音乐解锁",
"short_name": "音乐解锁",
"icons": {
"128": "./img/icons/msapplication-icon-144x144.png"
"16": "img/icons/favicon-16x16.png",
"32": "img/icons/favicon-32x32.png",
"192": "img/icons/android-chrome-192x192.png",
"512": "img/icons/android-chrome-512x512.png"
},
"description": "在任何设备上解锁已购的加密音乐!",
"permissions": ["storage"],
"offline_enabled": true,
"options_page": "./index.html",
"homepage_url": "https://github.com/ix64/unlock-music",
"browser_action": {
"options_page": "index.html",
"homepage_url": "https://git.unlock-music.dev/um/web",
"action": {
"default_icon": "img/icons/favicon-32x32.png",
"default_popup": "./popup.html"
},
"browser_specific_settings": {
"gecko": {
"id": "addon@unlock-music.dev",
"strict_min_version": "128.0"
}
}
}
}

61
package-lock.json generated
View File

@@ -1,17 +1,17 @@
{
"name": "unlock-music",
"version": "1.10.5",
"version": "1.10.8",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "unlock-music",
"version": "1.10.5",
"version": "1.10.8",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@babel/preset-typescript": "^7.16.5",
"@unlock-music/joox-crypto": "^0.0.1-R5",
"@unlock-music/joox-crypto": "^0.0.1",
"@xhacker/kgmwasm": "^1.0.0",
"@xhacker/qmcwasm": "^1.0.0",
"base64-js": "^1.5.1",
@@ -3484,11 +3484,12 @@
"dev": true
},
"node_modules/@unlock-music/joox-crypto": {
"version": "0.0.1-R5",
"resolved": "https://registry.npmjs.org/@unlock-music/joox-crypto/-/joox-crypto-0.0.1-R5.tgz",
"integrity": "sha512-+FhGT4bjzfb1Q7dAwHps/XqbqXrRA6Qg7pkDPzyXfeRmQocAySQ/dekojxkaFBf7ZX5ToIAopwxkKZ5NFt5bFw==",
"version": "0.0.1",
"resolved": "https://git.unlock-music.dev/api/packages/um/npm/%40unlock-music%2Fjoox-crypto/-/0.0.1/joox-crypto-0.0.1.tgz",
"integrity": "sha512-bj7UcA4/KSqK07PPmoRYJ+3s4h3P45RGUVAMspptMYXobhVkDlB1ArTYNlyIlrF/P0EMy7JkfEdOgUz0nD7EAg==",
"license": "MIT",
"dependencies": {
"crypto-js": "^4.1.1"
"crypto-js": "^4.2.0"
},
"bin": {
"joox-decrypt": "joox-decrypt"
@@ -5711,9 +5712,23 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001434",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz",
"integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA=="
"version": "1.0.30001717",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz",
"integrity": "sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
]
},
"node_modules/case-sensitive-paths-webpack-plugin": {
"version": "2.4.0",
@@ -6729,9 +6744,9 @@
}
},
"node_modules/crypto-js": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
"integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
},
"node_modules/css-color-names": {
"version": "0.0.4",
@@ -23653,11 +23668,11 @@
"dev": true
},
"@unlock-music/joox-crypto": {
"version": "0.0.1-R5",
"resolved": "https://registry.npmjs.org/@unlock-music/joox-crypto/-/joox-crypto-0.0.1-R5.tgz",
"integrity": "sha512-+FhGT4bjzfb1Q7dAwHps/XqbqXrRA6Qg7pkDPzyXfeRmQocAySQ/dekojxkaFBf7ZX5ToIAopwxkKZ5NFt5bFw==",
"version": "0.0.1",
"resolved": "https://git.unlock-music.dev/api/packages/um/npm/%40unlock-music%2Fjoox-crypto/-/0.0.1/joox-crypto-0.0.1.tgz",
"integrity": "sha512-bj7UcA4/KSqK07PPmoRYJ+3s4h3P45RGUVAMspptMYXobhVkDlB1ArTYNlyIlrF/P0EMy7JkfEdOgUz0nD7EAg==",
"requires": {
"crypto-js": "^4.1.1"
"crypto-js": "^4.2.0"
}
},
"@vue/babel-helper-vue-jsx-merge-props": {
@@ -25476,9 +25491,9 @@
}
},
"caniuse-lite": {
"version": "1.0.30001434",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz",
"integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA=="
"version": "1.0.30001717",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz",
"integrity": "sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw=="
},
"case-sensitive-paths-webpack-plugin": {
"version": "2.4.0",
@@ -26297,9 +26312,9 @@
}
},
"crypto-js": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
"integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
},
"css-color-names": {
"version": "0.0.4",

View File

@@ -1,27 +1,28 @@
{
"name": "unlock-music",
"version": "1.10.5",
"version": "1.10.8",
"ext_build": 0,
"updateInfo": "完善音乐标签编辑功能,支持编辑更多标签",
"updateInfo": "修正 joox 在远程获取 API 信息出错时不能正确回退到本地元信息获取的错误。",
"license": "MIT",
"description": "Unlock encrypted music file in browser.",
"repository": {
"type": "git",
"url": "https://github.com/ix64/unlock-music"
"url": "https://git.unlock-music.dev/um/web"
},
"private": true,
"scripts": {
"postinstall": "patch-package",
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"build:all": "./scripts/build-and-package.sh --all",
"test": "jest",
"pretty": "prettier --write src/{**/*,*}.{js,ts,jsx,tsx,vue}",
"pretty:check": "prettier --check src/{**/*,*}.{js,ts,jsx,tsx,vue}",
"make-extension": "node ./make-extension.js"
"make-extension": "node ./scripts/make-extension.js"
},
"dependencies": {
"@babel/preset-typescript": "^7.16.5",
"@unlock-music/joox-crypto": "^0.0.1-R5",
"@unlock-music/joox-crypto": "^0.0.1",
"@xhacker/kgmwasm": "^1.0.0",
"@xhacker/qmcwasm": "^1.0.0",
"base64-js": "^1.5.1",
@@ -57,4 +58,4 @@
"vue-cli-plugin-element": "^1.0.1",
"vue-template-compiler": "^2.6.14"
}
}
}

View File

@@ -0,0 +1,21 @@
diff --git a/node_modules/babel-loader/lib/cache.js b/node_modules/babel-loader/lib/cache.js
index fced210..6b0c137 100644
--- a/node_modules/babel-loader/lib/cache.js
+++ b/node_modules/babel-loader/lib/cache.js
@@ -91,15 +91,7 @@ const write = /*#__PURE__*/function () {
const filename = function (source, identifier, options) {
- // md4 hashing is not supported starting with node v17.0.0
- const majorNodeVersion = parseInt(process.versions.node.split(".")[0], 10);
- let hashType = "md4";
-
- if (majorNodeVersion >= 17) {
- hashType = "md5";
- }
-
- const hash = crypto.createHash(hashType);
+ const hash = crypto.createHash("sha256");
const contents = JSON.stringify({
source,
options,

View File

@@ -0,0 +1,13 @@
diff --git a/node_modules/copy-webpack-plugin/dist/postProcessPattern.js b/node_modules/copy-webpack-plugin/dist/postProcessPattern.js
index 8354f00..9bdf6ed 100644
--- a/node_modules/copy-webpack-plugin/dist/postProcessPattern.js
+++ b/node_modules/copy-webpack-plugin/dist/postProcessPattern.js
@@ -69,7 +69,7 @@ function postProcessPattern(globalRef, pattern, file) {
name: _package.name,
version: _package.version,
pattern,
- hash: _crypto.default.createHash('md4').update(content).digest('hex')
+ hash: _crypto.default.createHash('sha256').update(content).digest('hex')
});
return _cacache.default.get(globalRef.cacheDir, cacheKey).then(result => {
logger.debug(`getting cached transformation for '${file.absoluteFrom}'`);

View File

@@ -0,0 +1,13 @@
diff --git a/node_modules/terser-webpack-plugin/dist/index.js b/node_modules/terser-webpack-plugin/dist/index.js
index 6268f6b..1cb8f2c 100644
--- a/node_modules/terser-webpack-plugin/dist/index.js
+++ b/node_modules/terser-webpack-plugin/dist/index.js
@@ -214,7 +214,7 @@ class TerserPlugin {
// eslint-disable-next-line global-require
'terser-webpack-plugin': require('../package.json').version,
'terser-webpack-plugin-options': this.options,
- hash: _crypto.default.createHash('md4').update(input).digest('hex')
+ hash: _crypto.default.createHash('sha256').update(input).digest('hex')
};
task.cacheKeys = this.options.cacheKeys(defaultCacheKeys, file);
}

View File

@@ -0,0 +1,26 @@
diff --git a/node_modules/webpack/lib/optimize/SplitChunksPlugin.js b/node_modules/webpack/lib/optimize/SplitChunksPlugin.js
index e7d560b..94a8401 100644
--- a/node_modules/webpack/lib/optimize/SplitChunksPlugin.js
+++ b/node_modules/webpack/lib/optimize/SplitChunksPlugin.js
@@ -22,7 +22,7 @@ const deterministicGroupingForModules = /** @type {function(DeterministicGroupin
const hashFilename = name => {
return crypto
- .createHash("md4")
+ .createHash("sha256")
.update(name)
.digest("hex")
.slice(0, 8);
diff --git a/node_modules/webpack/lib/util/createHash.js b/node_modules/webpack/lib/util/createHash.js
index 64de510..4cc3fc9 100644
--- a/node_modules/webpack/lib/util/createHash.js
+++ b/node_modules/webpack/lib/util/createHash.js
@@ -131,6 +131,8 @@ module.exports = algorithm => {
// TODO add non-cryptographic algorithm here
case "debug":
return new DebugHash();
+ case 'md4':
+ algorithm = "sha256";
default:
return new BulkUpdateDecorator(require("crypto").createHash(algorithm));
}

View File

@@ -1,39 +1,89 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta content="webkit" name="renderer">
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<meta content="width=device-width,initial-scale=1.0" name="viewport">
<head>
<meta charset="utf-8" />
<meta content="webkit" name="renderer" />
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible" />
<meta content="width=device-width,initial-scale=1.0" name="viewport" />
<title>音乐解锁</title>
<meta content="音乐,解锁,ncm,qmc,mgg,mflac,qq音乐,网易云音乐,加密" name="keywords"/>
<meta content="音乐解锁 - 在任何设备上解锁已购的加密音乐!" name="description"/>
<!--@formatter:off-->
<style>#loader{position:absolute;left:50%;top:50%;z-index:1010;margin:-75px 0 0 -75px;border:16px solid #f3f3f3;border-radius:50%;border-top:16px solid #1db1ff;width:120px;height:120px;animation:spin 2s linear infinite}@keyframes spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}#loader-mask{text-align:center;position:absolute;width:100%;height:100%;bottom:0;left:0;right:0;top:0;z-index:1009;background-color:rgba(242,246,252,.88)}@media (prefers-color-scheme:dark){#loader-mask{color:#fff;background-color:rgba(0,0,0,.85)}#loader-mask a{color:#ddd}#loader-mask a:hover{color:#1db1ff}}#loader-source{font-size:1.5rem}#loader-tips-timeout{font-size:1.2rem}</style>
<!--@formatter:on-->
</head>
<body>
<meta content="音乐,解锁,ncm,qmc,mgg,mflac,qq音乐,网易云音乐,加密" name="keywords" />
<meta content="音乐解锁 - 在任何设备上解锁已购的加密音乐!" name="description" />
<style>
#loader {
position: absolute;
left: 50%;
top: 50%;
z-index: 1010;
margin: -75px 0 0 -75px;
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #1db1ff;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
#loader-mask {
text-align: center;
position: absolute;
width: 100%;
height: 100%;
bottom: 0;
left: 0;
right: 0;
top: 0;
z-index: 1009;
background-color: rgba(242, 246, 252, 0.88);
}
@media (prefers-color-scheme: dark) {
#loader-mask {
color: #fff;
background-color: rgba(0, 0, 0, 0.85);
}
#loader-mask a {
color: #ddd;
}
#loader-mask a:hover {
color: #1db1ff;
}
}
#loader-source {
font-size: 1.5rem;
}
#loader-tips-timeout {
font-size: 1.2rem;
}
</style>
</head>
<div id="loader-mask">
<div id="loader"></div>
<noscript>
<body>
<div id="loader-mask">
<div id="loader"></div>
<noscript>
<h3 id="loader-js">请启用JavaScript</h3>
</noscript>
<h3 id="loader-source"> 请勿直接运行源代码! </h3>
<div id="loader-tips-outdated" hidden>
<h2>您可能在使用不受支持的<span style="color:#f00;">过时</span>浏览器,这可能导致此应用无法正常工作。</h2>
<h3>如果您使用双核浏览器,您可以尝试切换到 <span style="color:#f00;">“极速模式”</span> 解决此问题。</h3>
</noscript>
<h3 id="loader-source">请勿直接运行源代码!</h3>
<div id="loader-tips-outdated" hidden>
<h2>您可能在使用不受支持的<span style="color: #f00">过时</span>浏览器,这可能导致此应用无法正常工作。</h2>
<h3>如果您使用双核浏览器,您可以尝试切换到 <span style="color: #f00">“极速模式”</span> 解决此问题。</h3>
<h3>或者,您可以尝试更换下方的几个浏览器之一。</h3>
</div>
<h3 id="loader-tips-timeout" hidden>
</div>
<h3 id="loader-tips-timeout" hidden>
音乐解锁采用了一些新特性!建议使用
<a href="https://www.microsoft.com/zh-cn/edge" target="_blank">Microsoft Edge Chromium</a>
<a href="https://www.google.cn/chrome/" target="_blank">Google Chrome</a>
<a href="https://www.firefox.com.cn/" target="_blank">Mozilla Firefox</a>
| <a href="https://github.com/ix64/unlock-music/wiki/使用提示" target="_blank">使用提示</a>
</h3>
</div>
<div id="app"></div>
<script src="./loader.js"></script>
</body>
| <a href="https://git.unlock-music.dev/um/web/wiki/使用提示" target="_blank">使用提示</a>
</h3>
</div>
<div id="app"></div>
<script src="./loader.js"></script>
</body>
</html>

View File

@@ -11,6 +11,12 @@ case "$1" in
"modern") npm run build -- --modern ;;
"legacy") npm run build ;;
"extension") npm run make-extension ;;
"--all")
"$0" legacy
"$0" extension
"$0" modern
exit 0
;;
*)
echo "Unknown command: $1"
@@ -18,6 +24,7 @@ case "$1" in
;;
esac
cp README.md LICENSE dist/
mv dist "${DIST_NAME}"
zip -rJ9 "${DIST_NAME}.zip" "${DIST_NAME}"

54
make-extension.js → scripts/make-extension.js Normal file → Executable file
View File

@@ -1,25 +1,29 @@
const fs = require('fs')
const path = require('path')
const src = __dirname + "/src/extension/"
const dst = __dirname + "/dist"
fs.readdirSync(src).forEach(file => {
let srcPath = path.join(src, file)
let dstPath = path.join(dst, file)
fs.copyFileSync(srcPath, dstPath)
console.log(`Copy: ${srcPath} => ${dstPath}`)
})
const manifestRaw = fs.readFileSync(__dirname + "/extension-manifest.json", "utf-8")
const manifest = JSON.parse(manifestRaw)
const pkgRaw = fs.readFileSync(__dirname + "/package.json", "utf-8")
const pkg = JSON.parse(pkgRaw)
verExt = pkg["version"]
if (verExt.startsWith("v")) verExt = verExt.slice(1)
if (verExt.includes("-")) verExt = verExt.split("-")[0]
manifest["version"] = `${verExt}.${pkg["ext_build"]}`
manifest["version_name"] = pkg["version"]
fs.writeFileSync(__dirname + "/dist/manifest.json", JSON.stringify(manifest), "utf-8")
console.log("Write: manifest.json")
#!/usr/bin/env node
const fs = require('fs')
const path = require('path')
const DIR_ROOT = path.resolve(__dirname, "..")
const src = DIR_ROOT + "/src/extension/"
const dst = DIR_ROOT + "/dist"
fs.readdirSync(src).forEach(file => {
let srcPath = path.join(src, file)
let dstPath = path.join(dst, file)
fs.copyFileSync(srcPath, dstPath)
console.log(`Copy: ${srcPath} => ${dstPath}`)
})
const manifestRaw = fs.readFileSync(DIR_ROOT + "/extension-manifest.json", "utf-8")
const manifest = JSON.parse(manifestRaw)
const pkgRaw = fs.readFileSync(DIR_ROOT + "/package.json", "utf-8")
const pkg = JSON.parse(pkgRaw)
verExt = pkg["version"]
if (verExt.startsWith("v")) verExt = verExt.slice(1)
if (verExt.includes("-")) verExt = verExt.split("-")[0]
manifest["version"] = `${verExt}.${pkg["ext_build"]}`
manifest["version_name"] = pkg["version"]
fs.writeFileSync(DIR_ROOT + "/dist/manifest.json", JSON.stringify(manifest), "utf-8")
console.log("Write: manifest.json")

View File

@@ -1,19 +0,0 @@
#!/bin/sh
set -ex
cd "$(git rev-parse --show-toplevel)"
if [ -z "$GITEA_API_KEY" ]; then
echo "GITEA_API_KEY is empty, skip upload."
exit 0
fi
URL_BASE="$DRONE_GITEA_SERVER/api/packages/${DRONE_REPO_NAMESPACE}/generic/${DRONE_REPO_NAME}-build"
for ZIP_NAME in *.zip; do
UPLOAD_URL="${URL_BASE}/${DRONE_BUILD_NUMBER}/${ZIP_NAME}"
sha256sum "${ZIP_NAME}"
curl -sLifu "um-release-bot:$GITEA_API_KEY" -T "${ZIP_NAME}" "${UPLOAD_URL}"
echo "Uploaded to: ${UPLOAD_URL}"
done

View File

@@ -4,22 +4,22 @@
<Home />
</el-main>
<el-footer id="app-footer">
<el-row>
<a href="https://github.com/ix64/unlock-music" target="_blank">音乐解锁</a>({{ version }})
<div>
<a href="https://git.unlock-music.dev/um/web" target="_blank">音乐解锁</a>({{ version }})
移除已购音乐的加密保护
<a href="https://github.com/ix64/unlock-music/wiki/使用提示" target="_blank">使用提示</a>
</el-row>
<el-row>
<a href="https://git.unlock-music.dev/um/web/wiki/使用提示" target="_blank">使用提示</a>
</div>
<div>
目前支持 网易云音乐(ncm), QQ音乐(qmc, mflac, mgg), 酷狗音乐(kgm), 虾米音乐(xm), 酷我音乐(.kwm)
<a href="https://github.com/ix64/unlock-music/blob/master/README.md" target="_blank">更多</a>
</el-row>
<el-row>
<a href="https://git.unlock-music.dev/um/web/src/branch/main/README.md" target="_blank">更多</a>
</div>
<div>
<!--如果进行二次开发此行版权信息不得移除且应明显地标注于页面上-->
<span>Copyright &copy; 2019 - {{ new Date().getFullYear() }} MengYX</span>
音乐解锁使用
<a href="https://github.com/ix64/unlock-music/blob/master/LICENSE" target="_blank">MIT许可协议</a>
<a href="https://git.unlock-music.dev/um/web/src/branch/main/LICENSE" target="_blank">MIT许可协议</a>
开放源代码
</el-row>
</div>
</el-footer>
</el-container>
</template>
@@ -77,7 +77,7 @@ export default {
<div class="update-title">最近更新</div>
<div class="update-content"> ${config.updateInfo} </div>
</div>
<a target="_blank" href="https://github.com/ix64/unlock-music/wiki/使用提示">使用提示</a>
<a target="_blank" href="https://git.unlock-music.dev/um/web/wiki/使用提示">使用提示</a>
</div>`,
dangerouslyUseHTMLString: true,
duration: 10000,

View File

@@ -4,16 +4,6 @@ label {
line-height: 1.2;
display: block;
}
.item-desc {
color: #aaa;
font-size: small;
display: block;
line-height: 1.2;
margin-top: 0.2em;
}
.item-desc a {
color: #aaa;
}
form >>> input {
font-family: 'Courier New', Courier, monospace;
@@ -39,11 +29,13 @@ form >>> input {
</el-form-item>
</label>
<p class="item-desc">
<p class="tip">
下载该加密文件的 JOOX 应用所记录的设备唯一识别码
<br />
参见
<a href="https://github.com/unlock-music/joox-crypto/wiki/%E8%8E%B7%E5%8F%96%E8%AE%BE%E5%A4%87-UUID">
<a
href="https://git.unlock-music.dev/um/joox-crypto/wiki/%E8%8E%B7%E5%8F%96%E8%AE%BE%E5%A4%87-UUID#%E5%89%8D%E8%A8%80"
>
获取设备 UUID · unlock-music/joox-crypto Wiki</a
>
</p>

View File

@@ -1,24 +1,4 @@
<style scoped>
label {
cursor: pointer;
line-height: 1.2;
display: block;
}
.item-desc {
color: #aaa;
font-size: small;
display: block;
line-height: 1.2;
margin-top: 0.2em;
}
.item-desc a {
color: #aaa;
}
form >>> input {
font-family: 'Courier New', Courier, monospace;
}
* >>> .um-edit-dialog {
max-width: 90%;
width: 30em;
@@ -29,57 +9,62 @@ form >>> input {
<el-dialog @close="cancel()" title="音乐标签编辑" :visible="show" custom-class="um-edit-dialog" center>
<el-form ref="form" status-icon :model="form" label-width="0">
<section>
<el-image v-show="!editPicture" :src="imgFile.url || picture" style="width: 100px; height: 100px">
<div slot="error" class="image-slot el-image__error">暂无封面</div>
</el-image>
<el-upload v-show="editPicture" :auto-upload="false" :on-change="addFile" :on-remove="rmvFile" :show-file-list="true" :limit="1" list-type="picture" action="" drag>
<i class="el-icon-upload" />
<div class="el-upload__text">将新图片拖到此处<em>点击选择</em><br />以替换自动匹配的图片</div>
<div slot="tip" class="el-upload__tip">
新拖到此处的图片将覆盖原始图片
</div>
</el-upload>
<i
:class="{'el-icon-edit': !editPicture, 'el-icon-check': editPicture}"
@click="changeCover"
></i><br />
标题:
<span v-show="!editTitle">{{title}}</span>
<el-input v-show="editTitle" v-model="title"></el-input>
<i
:class="{'el-icon-edit': !editTitle, 'el-icon-check': editTitle}"
@click="editTitle = !editTitle"
></i><br />
艺术家:
<span v-show="!editArtist">{{artist}}</span>
<el-input v-show="editArtist" v-model="artist"></el-input>
<i
:class="{'el-icon-edit': !editArtist, 'el-icon-check': editArtist}"
@click="editArtist = !editArtist"
></i><br />
专辑:
<span v-show="!editAlbum">{{album}}</span>
<el-input v-show="editAlbum" v-model="album"></el-input>
<i
:class="{'el-icon-edit': !editAlbum, 'el-icon-check': editAlbum}"
@click="editAlbum = !editAlbum"
></i><br />
专辑艺术家:
<span v-show="!editAlbumartist">{{albumartist}}</span>
<el-input v-show="editAlbumartist" v-model="albumartist"></el-input>
<i
:class="{'el-icon-edit': !editAlbumartist, 'el-icon-check': editAlbumartist}"
@click="editAlbumartist = !editAlbumartist"
></i><br />
风格:
<span v-show="!editGenre">{{genre}}</span>
<el-input v-show="editGenre" v-model="genre"></el-input>
<i
:class="{'el-icon-edit': !editGenre, 'el-icon-check': editGenre}"
@click="editGenre = !editGenre"
></i><br />
<div class="music-cover">
<el-image v-show="!editPicture" :src="imgFile.url || picture">
<div slot="error" class="image-slot el-image__error">暂无封面</div>
</el-image>
<el-upload v-show="editPicture" :auto-upload="false" :on-change="addFile" :on-remove="rmvFile" :show-file-list="true" :limit="1" list-type="picture" action="" drag>
<i class="el-icon-upload" />
<div class="el-upload__text">将新图片拖到此处<em>点击选择</em><br />以替换自动匹配的图片</div>
<div slot="tip" class="el-upload__tip">
新拖到此处的图片将覆盖原始图片
</div>
</el-upload>
<i :class="{'el-icon-edit': !editPicture, 'el-icon-check': editPicture}"
@click="changeCover"></i>
</div>
<p class="item-desc">
<div class="edit-item">
<div class="label">标题</div>
<div class="value" v-show="!editTitle">{{title}}</div>
<el-input class="input" size="small" v-show="editTitle" v-model="title"/>
<i :class="{'el-icon-edit': !editTitle, 'el-icon-check': editTitle}"
@click="editTitle = !editTitle"/>
</div>
<div class="edit-item">
<div class="label">艺术家</div>
<div class="value" v-show="!editArtist">{{artist}}</div>
<el-input class="input" size="small" v-show="editArtist" v-model="artist"/>
<i :class="{'el-icon-edit': !editArtist, 'el-icon-check': editArtist}"
@click="editArtist = !editArtist"
/>
</div>
<div class="edit-item">
<div class="label">专辑</div>
<div class="value" v-show="!editAlbum">{{album}}</div>
<el-input class="input" size="small" v-show="editAlbum" v-model="album"/>
<i :class="{'el-icon-edit': !editAlbum, 'el-icon-check': editAlbum}"
@click="editAlbum = !editAlbum"
/>
</div>
<div class="edit-item">
<div class="label">专辑艺术家</div>
<div class="value" v-show="!editAlbumartist">{{albumartist}}</div>
<el-input class="input" size="small" v-show="editAlbumartist" v-model="albumartist"/>
<i :class="{'el-icon-edit': !editAlbumartist, 'el-icon-check': editAlbumartist}"
@click="editAlbumartist = !editAlbumartist"
/>
</div>
<div class="edit-item">
<div class="label">风格</div>
<div class="value" v-show="!editGenre">{{genre}}</div>
<el-input class="input" size="small" v-show="editGenre" v-model="genre"/>
<i :class="{'el-icon-edit': !editGenre, 'el-icon-check': editGenre}"
@click="editGenre = !editGenre"
/>
</div>
<p class="tip">
为了节省您设备的资源请在确定前充分检查避免反复修改<br />
直接关闭此对话框不会保留所作的更改
</p>

View File

@@ -9,7 +9,7 @@
</el-table-column>
<el-table-column label="歌曲">
<template #default="scope">
<span>{{ scope.row.title }}</span>
<p>{{ scope.row.title }}</p>
</template>
</el-table-column>
<el-table-column label="歌手">

View File

@@ -22,7 +22,7 @@ describe('decrypt/joox', () => {
album: 'unused',
blob: blob,
artist: 'unused',
imgUrl: 'https://github.com/unlock-music',
imgUrl: 'https://example.unlock-music.dev/',
};
});

View File

@@ -2,7 +2,7 @@ import { KgmCrypto } from '@xhacker/kgmwasm/KgmWasmBundle';
import KgmCryptoModule from '@xhacker/kgmwasm/KgmWasmBundle';
import { MergeUint8Array } from '@/utils/MergeUint8Array';
// 每次处理 2M 的数据
// 每次可以处理 2M 的数据
const DECRYPTION_BUF_SIZE = 2 *1024 * 1024;
export interface KGMDecryptionResult {
@@ -36,12 +36,12 @@ export async function DecryptKgmWasm(kgmBlob: ArrayBuffer, ext: string): Promise
// 申请内存块,并文件末端数据到 WASM 的内存堆
let kgmBuf = new Uint8Array(kgmBlob);
const pQmcBuf = KgmCryptoObj._malloc(DECRYPTION_BUF_SIZE);
KgmCryptoObj.writeArrayToMemory(kgmBuf.slice(0, DECRYPTION_BUF_SIZE), pQmcBuf);
const pKgmBuf = KgmCryptoObj._malloc(DECRYPTION_BUF_SIZE);
const preDecDataSize = Math.min(DECRYPTION_BUF_SIZE, kgmBlob.byteLength); // 初始化缓冲区大小
KgmCryptoObj.writeArrayToMemory(kgmBuf.slice(0, preDecDataSize), pKgmBuf);
// 进行解密初始化
const headerSize = KgmCryptoObj.preDec(pQmcBuf, DECRYPTION_BUF_SIZE, ext);
console.log(headerSize);
const headerSize = KgmCryptoObj.preDec(pKgmBuf, preDecDataSize, ext);
kgmBuf = kgmBuf.slice(headerSize);
const decryptedParts = [];
@@ -52,14 +52,14 @@ export async function DecryptKgmWasm(kgmBlob: ArrayBuffer, ext: string): Promise
// 解密一些片段
const blockData = new Uint8Array(kgmBuf.slice(offset, offset + blockSize));
KgmCryptoObj.writeArrayToMemory(blockData, pQmcBuf);
KgmCryptoObj.decBlob(pQmcBuf, blockSize, offset);
decryptedParts.push(KgmCryptoObj.HEAPU8.slice(pQmcBuf, pQmcBuf + blockSize));
KgmCryptoObj.writeArrayToMemory(blockData, pKgmBuf);
KgmCryptoObj.decBlob(pKgmBuf, blockSize, offset);
decryptedParts.push(KgmCryptoObj.HEAPU8.slice(pKgmBuf, pKgmBuf + blockSize));
offset += blockSize;
bytesToDecrypt -= blockSize;
}
KgmCryptoObj._free(pQmcBuf);
KgmCryptoObj._free(pKgmBuf);
result.data = MergeUint8Array(decryptedParts);
result.success = true;

View File

@@ -2,7 +2,7 @@ import { QmcCrypto } from '@xhacker/qmcwasm/QmcWasmBundle';
import QmcCryptoModule from '@xhacker/qmcwasm/QmcWasmBundle';
import { MergeUint8Array } from '@/utils/MergeUint8Array';
// 每次处理 2M 的数据
// 每次可以处理 2M 的数据
const DECRYPTION_BUF_SIZE = 2 *1024 * 1024;
export interface QMCDecryptionResult {
@@ -38,11 +38,12 @@ export async function DecryptQmcWasm(qmcBlob: ArrayBuffer, ext: string): Promise
// 申请内存块,并文件末端数据到 WASM 的内存堆
const qmcBuf = new Uint8Array(qmcBlob);
const pQmcBuf = QmcCryptoObj._malloc(DECRYPTION_BUF_SIZE);
QmcCryptoObj.writeArrayToMemory(qmcBuf.slice(-DECRYPTION_BUF_SIZE), pQmcBuf);
const preDecDataSize = Math.min(DECRYPTION_BUF_SIZE, qmcBlob.byteLength); // 初始化缓冲区大小
QmcCryptoObj.writeArrayToMemory(qmcBuf.slice(-preDecDataSize), pQmcBuf);
// 进行解密初始化
ext = '.' + ext;
const tailSize = QmcCryptoObj.preDec(pQmcBuf, DECRYPTION_BUF_SIZE, ext);
const tailSize = QmcCryptoObj.preDec(pQmcBuf, preDecDataSize, ext);
if (tailSize == -1) {
result.error = QmcCryptoObj.getErr();
return result;

View File

@@ -10,236 +10,46 @@
background-color: $dark-bg;
}
// FORM
.el-radio{
&__label{
color: $dark-text-main;
}
&__input{
color: $dark-text-info;
.el-radio__inner{
border-color: $dark-border;
background-color: $dark-btn-bg;
}
}
&.is-checked{
.el-radio__inner{
background-color: $blue;
}
.el-radio__label{
font-weight: bold;
}
}
}
.el-checkbox.is-bordered{
border-color: $dark-border;
color: $dark-text-main;
background-color: $dark-btn-bg;
.el-checkbox__inner{
background-color: $dark-btn-bg-highlight;
border-color: $dark-border-highlight;
}
&:hover{
border-color: $dark-border-highlight;
.el-checkbox__inner{
background-color: $dark-btn-bg-highlight;
border-color: $dark-border-highlight;
}
.el-checkbox__label{
color: $dark-text-info;
}
}
&.is-checked{
background-color: $blue;
.el-checkbox__inner{
border-color: white;
}
.el-checkbox__label{
color: white;
font-weight: bold;
}
// 编辑歌曲信息
.music-cover{
i{
&:hover{
border-color: $blue;
.el-checkbox__inner{
background-color: white;
}
color: $color-checkbox;
}
}
.el-image{
border: 1px solid $dark-border;
}
}
// BUTTON
.el-button{
background-color: $dark-btn-bg;
border-color: $dark-border;
color: $dark-text-main;
&:active{
transform: translateY(2px);
.edit-item{
.label{
}
&--default{
&.is-plain {
background-color: $dark-btn-bg;
&:hover {
background-color: $blue;
border-color: $blue;
color: white;
}
}
&.is-circle {
background-color: $dark-blue;
border-color: $dark-blue;
&:hover {
background-color: $blue;
border-color: $blue;
color: white;
}
.value{
}
.input{
input{
background-color: transparent !important;
border-bottom: 1px solid $dark-border;
}
}
&--success{
&.is-plain {
background-color: $dark-btn-bg;
&:hover {
background-color: $green;
border-color: $green;
color: white;
}
}
&.is-circle {
background-color: $dark-green;
border-color: $dark-green;
&:hover {
background-color: $green;
border-color: $green;
color: white;
}
}
}
&--danger{
&.is-plain{
border-color: $dark-border;
background-color: $dark-btn-bg;
&:hover{
background-color: $red;
border-color: $red;
}
}
&.is-circle {
background-color: $dark-red;
border-color: $dark-red;
&:hover {
background-color: $red;
border-color: $red;
color: white;
}
}
}
}
// 文件拖放区
.el-upload__tip{
color: $dark-text-info;
}
.el-upload-dragger{
background-color: $dark-uploader-bg;
border-color: $dark-border;
.el-upload__text{
color: $dark-text-info;
}
&:hover{
background: $dark-uploader-bg-highlight;
border-color: $dark-border-highlight;
}
}
// TABLE
.el-table{
background-color: $dark-bg-td;
&:before{ // 去除表格末尾的横线
content: none;
}
&__header{
th{
border-bottom-color: $dark-border !important;
}
}
th.el-table__cell{
background-color: $dark-bg-th;
color: $dark-text-info;
}
td{
border-bottom-color: $dark-border !important;
}
tr{
background-color: $dark-bg-td;
color: $dark-text-main;
i{
&:hover{
td{
background-color: $dark-bg-th !important;
}
color: $color-checkbox;
}
}
}
// LINKS
a{
text-decoration: none;
color: darken($dark-color-link, 15%);
&:hover{
color: $dark-color-link;
}
}
// ALERT
.el-notification{
background-color: $dark-btn-bg-highlight;
border-color: $dark-border;
&__title{
color: white;
}
&__content{
color: $dark-text-info;
}
}
// DIALOG
.el-dialog{
background-color: $dark-dialog-bg;
.el-dialog__header{
.el-dialog__title{
color: $dark-text-main;
// footer
#app-footer {
a {
color: lighten($text-comment, 5%);
&:hover{
color: $color-link;
}
}
.el-dialog__body{
color: $dark-text-main;
.el-input{
.el-input__inner{
color: $dark-text-main;
background-color: $dark-btn-bg;
}
.el-input__suffix{
.el-input__suffix-inner{
}
}
.el-input__count{
.el-input__count-inner{
background-color: transparent;
}
}
}
}
.item-desc{
color: $dark-text-info;
}
}
// 自定义样式
// 首页弹窗提示信息的 更新信息 面板
.update-info{

View File

@@ -1,39 +0,0 @@
$color-checkbox: $blue;
$color-border-el: #DCDFE6;
$btn-radius: 6px;
/* FORM */
// checkbox
.el-checkbox.is-bordered{
@include border-radius($btn-radius) ;
&:hover{
border-color: $color-checkbox;
.el-checkbox__label{
color: $color-checkbox;
}
}
.el-checkbox__input.is-focus{
.el-checkbox__inner{
border-color: $color-border-el;
}
}
&.is-checked{
background-color: $color-checkbox;
.el-checkbox__label{
color: white;
}
.el-checkbox__inner{
border-color: white;
background-color: white;
&:after{
border-color: $color-checkbox;
}
}
}
}
// el-button
.el-button{
@include border-radius($btn-radius) ;
}

View File

@@ -0,0 +1,291 @@
$color-checkbox: $blue;
$color-border-el: #DCDFE6;
$btn-radius: 6px;
/* FORM */
// checkbox
.el-checkbox.is-bordered{
@include border-radius($btn-radius) ;
&:hover{
border-color: $color-checkbox;
.el-checkbox__label{
color: $color-checkbox;
}
}
.el-checkbox__input.is-focus{
.el-checkbox__inner{
border-color: $color-border-el;
}
}
&.is-checked{
background-color: $color-checkbox;
.el-checkbox__label{
color: white;
}
.el-checkbox__inner{
border-color: white;
background-color: white;
&:after{
border-color: $color-checkbox;
}
}
}
}
// el-button
.el-button{
@include border-radius($btn-radius) ;
}
// upload
.el-upload-dragger{
&:hover{
background-color: transparentize($color-checkbox, 0.9);
}
}
.el-upload__tip{
text-align: center;
color: $text-comment;
}
// dialog
.el-dialog{
@include border-radius(5px);
&.el-dialog--center{
.el-dialog__body{
padding: 25px 25px 15px;
}
.el-dialog__footer{
padding: 10px 20px 30px;
}
}
}
@media (prefers-color-scheme: dark) {
// FORM
.el-radio{
&__label{
color: $dark-text-main;
}
&__input{
color: $dark-text-info;
.el-radio__inner{
border-color: $dark-border;
background-color: $dark-btn-bg;
}
}
&.is-checked{
.el-radio__inner{
background-color: $blue;
}
.el-radio__label{
font-weight: bold;
}
}
}
.el-checkbox.is-bordered{
border-color: $dark-border;
color: $dark-text-main;
background-color: $dark-btn-bg;
.el-checkbox__inner{
background-color: $dark-btn-bg-highlight;
border-color: $dark-border-highlight;
}
&:hover{
border-color: $dark-border-highlight;
.el-checkbox__inner{
background-color: $dark-btn-bg-highlight;
border-color: $dark-border-highlight;
}
.el-checkbox__label{
color: $dark-text-info;
}
}
&.is-checked{
background-color: $blue;
.el-checkbox__inner{
border-color: white;
}
.el-checkbox__label{
color: white;
font-weight: bold;
}
&:hover{
border-color: $blue;
.el-checkbox__inner{
background-color: white;
}
}
}
}
// BUTTON
.el-button{
background-color: $dark-btn-bg;
border-color: $dark-border;
color: $dark-text-main;
&:active{
transform: translateY(2px);
}
&--default{
&.is-plain {
background-color: $dark-btn-bg;
&:hover {
background-color: $blue;
border-color: $blue;
color: white;
}
}
&.is-circle {
background-color: $dark-blue;
border-color: $dark-blue;
&:hover {
background-color: $blue;
border-color: $blue;
color: white;
}
}
}
&--success{
&.is-plain {
background-color: $dark-btn-bg;
&:hover {
background-color: $green;
border-color: $green;
color: white;
}
}
&.is-circle {
background-color: $dark-green;
border-color: $dark-green;
&:hover {
background-color: $green;
border-color: $green;
color: white;
}
}
}
&--danger{
&.is-plain{
border-color: $dark-border;
background-color: $dark-btn-bg;
&:hover{
background-color: $red;
border-color: $red;
}
}
&.is-circle {
background-color: $dark-red;
border-color: $dark-red;
&:hover {
background-color: $red;
border-color: $red;
color: white;
}
}
}
}
// 文件拖放区
.el-upload__tip{
color: $dark-text-info;
}
.el-upload-dragger{
background-color: $dark-uploader-bg;
border-color: $dark-border;
.el-upload__text{
color: $dark-text-info;
}
&:hover{
background: $dark-uploader-bg-highlight;
border-color: $dark-border-highlight;
}
}
// TABLE
.el-table{
background-color: $dark-bg-td;
&:before{ // 去除表格末尾的横线
content: none;
}
&__header{
th{
border-bottom-color: $dark-border !important;
}
}
th.el-table__cell{
background-color: $dark-bg-th;
color: $dark-text-info;
}
td{
border-bottom-color: $dark-border !important;
}
tr{
background-color: $dark-bg-td;
color: $dark-text-main;
&:hover{
td{
background-color: $dark-bg-th !important;
}
}
}
}
// ALERT
.el-notification{
background-color: $dark-btn-bg-highlight;
border-color: $dark-border;
&__title{
color: white;
}
&__content{
color: $dark-text-info;
}
}
// DIALOG
.el-dialog{
background-color: $dark-dialog-bg;
.el-dialog__header{
.el-dialog__title{
color: $dark-text-main;
}
}
.el-dialog__body{
color: $dark-text-main;
.el-input{
.el-input__inner{
border-color: $dark-border;
color: $dark-text-main;
background-color: $dark-btn-bg;
}
.el-input__suffix{
.el-input__suffix-inner{
}
}
.el-input__count{
.el-input__count-inner{
background-color: transparent;
}
}
}
}
.item-desc{
color: $dark-text-info;
}
}
}

View File

@@ -1,38 +0,0 @@
body{
font-family: $font-family;
font-size: $fz-main;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
#app {
text-align: center;
color: $text-main;
padding-top: 30px;
}
#app-footer a {
padding-left: 0.2em;
padding-right: 0.2em;
}
#app-footer {
text-align: center;
font-size: small;
}
#app-control {
padding-top: 1em;
padding-bottom: 1em;
}
audio{
margin-bottom: 15px; // 播放控件与表格间隔
}
a{
color: darken($color-link, 15%);
&:hover{
color: $color-link;
}
}

View File

@@ -66,6 +66,7 @@
}
.btn-like{
cursor: pointer;
&:active{
@include transform(translateY(2px))
}

View File

@@ -3,18 +3,17 @@ $blue : #409EFF;
$red : #F56C6C;
$green : #85ce61;
// TEXT
$text-main : #2C3E50;
$color-link: $blue;
// TEXT COLOR
$text-main : #2C3E50;
$text-copyright : #777;
$text-comment : #999;
$color-link : $blue;
// FONT SIZE
$fz-main: 14px;
$fz-mini-title: 13px;
$fz-mini-content: 12px;
$font-family: "Helvetica Neue", Helvetica, "PingFang SC",
"Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
// DARK MODE
$dark-border : lighten(black, 25%);
$dark-border-highlight : lighten(black, 55%);

View File

@@ -1,11 +1,127 @@
@import "variables";
@import "utility";
@import "gaps";
@import "element-ui-overrite";
@import "element-ui-overwrite";
@import "normal";
@import "dark-mode"; // dark-mode 放在 normal 后面,以获得更高优先级
// MAIN CONTENT
body{
margin: 0;
padding: 0;
border: 0;
box-sizing: border-box;
font-family: "PingFang SC", "微软雅黑", "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
font-size: $fz-main;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
#app {
text-align: center;
color: $text-main;
padding: 30px;
}
// 音频文件操作
#app-control {
margin-top: 20px;
}
// 音频播放
audio{
margin-top: 20px;
}
.table-content{
margin-top: 20px;
}
// 编辑歌曲信息
.music-cover{
margin-bottom: 20px;
display: flex;
justify-content: center;
align-items: center;
flex-flow: column nowrap;
i{
margin-top: 10px;
@extend .btn-like;
&:hover{
color: $color-checkbox;
}
}
.el-image{
padding: 5px;
@include border-radius(5px);
border: 1px solid $color-border-el;
width: 150px;
height: 150px;
}
}
.edit-item{
display: flex;
justify-content: flex-start;
align-items: center;
.label{
font-weight: bold;
width: 80px;
text-align: right;
flex-shrink: 0;
}
.value{
padding: 5px 0;
height: 20px;
line-height: 20px;
margin-left: 10px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.input{
margin-left: 10px;
input{
font-family: inherit;
height: 30px;
line-height: 20px;
@include border-radius(0);
border: none;
border-bottom: 1px solid $color-border-el;
padding: 5px 5px;
}
}
i{
margin-left: 10px;
@extend .btn-like;
&:hover{
color: $color-checkbox;
}
}
}
.tip{
margin-top: 20px;
color: $text-comment;
font-size: $fz-mini-content;
a{
color: inherit;
}
}
// footer
#app-footer {
margin-top: 40px;
text-align: center;
color: $text-copyright;
line-height: 1.3;
font-size: $fz-mini-content;
a {
padding-left: 0.2rem;
padding-right: 0.2rem;
color: darken($text-copyright, 10%);
&:hover{
color: $color-link;
}
}
}
// 首页弹窗提示信息的 更新信息 面板
.update-info{
@@ -15,12 +131,14 @@
margin: 10px 0;
.update-title{
font-size: $fz-mini-title;
padding: 5px 10px;
padding: 3px 10px;
background-color: $color-border-el;
}
.update-content{
font-size: $fz-mini-content;
line-height: 1.5;
padding: 10px;
padding: 5px 8px;
}
}
@import "dark-mode"; // dark-mode 放在 normal 后面,以获得更高优先级

View File

@@ -55,7 +55,7 @@ export async function extractQQMusicMeta(
if (id && id !== '0') {
try {
return fetchMetadataFromSongId(id, ext, musicMeta, musicBlob);
return await fetchMetadataFromSongId(id, ext, musicMeta, musicBlob);
} catch (e) {
console.warn('在线获取曲目信息失败,回退到本地 meta 提取', e);
}

View File

@@ -18,7 +18,9 @@
:album="editing_data.album"
:albumartist="editing_data.albumartist"
:genre="editing_data.genre"
@cancel="showEditDialog = false" @ok="handleEdit"></edit-dialog>
@cancel="showEditDialog = false"
@ok="handleEdit"
></edit-dialog>
<config-dialog :show="showConfigDialog" @done="showConfigDialog = false"></config-dialog>
<el-tooltip class="item" effect="dark" placement="top">
<div slot="content">
@@ -37,14 +39,20 @@
开启后解锁结果将不会存留于浏览器中防止内存不足
</span>
</div>
<el-checkbox v-model="instant_save" type="success" border class="ml-2">立即保存</el-checkbox>
<el-checkbox v-model="instant_save" type="success" border class="ml-2">立即保存</el-checkbox>
</el-tooltip>
</el-row>
</div>
<audio :autoplay="playing_auto" :src="playing_url" controls />
<PreviewTable :policy="filename_policy" :table-data="tableData" @download="saveFile" @edit="editFile" @play="changePlaying" />
<PreviewTable
class="table-content"
:policy="filename_policy"
:table-data="tableData"
@download="saveFile"
@edit="editFile"
@play="changePlaying" />
</div>
</template>
@@ -70,7 +78,7 @@ export default {
return {
showConfigDialog: false,
showEditDialog: false,
editing_data: { picture: '', title: '', artist: '', album: '', albumartist: '', genre: '', },
editing_data: { picture: '', title: '', artist: '', album: '', albumartist: '', genre: '' },
tableData: [],
playing_url: '',
playing_auto: false,
@@ -111,7 +119,7 @@ export default {
errInfo +
'' +
filename +
',参考<a target="_blank" href="https://github.com/ix64/unlock-music/wiki/使用提示">使用提示</a>',
',参考<a target="_blank" href="https://git.unlock-music.dev/um/web/wiki/使用提示">使用提示</a>',
dangerouslyUseHTMLString: true,
duration: 6000,
});
@@ -164,12 +172,13 @@ export default {
console.warn('获取图像失败', this.editing_data.picture);
}
}
const newMeta = { picture: imageInfo?.buffer,
const newMeta = {
picture: imageInfo?.buffer,
title: data.title,
artists: data.artist.split(split_regex),
album: data.album,
albumartist: data.albumartist,
genre: data.genre.split(split_regex)
genre: data.genre.split(split_regex),
};
const buffer = Buffer.from(await this.editing_data.blob.arrayBuffer());
const mime = AudioMimeType[this.editing_data.ext] || AudioMimeType.mp3;