Compare commits
420 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 71817baad0 | |||
| f6735c9c26 | |||
| 708a1a8eb8 | |||
|
|
70ab357799 | ||
|
|
c7e69158d2 | ||
|
|
022009b140 | ||
|
|
6849632abe | ||
|
|
c929beaf6c | ||
|
|
dfb09b3ccd | ||
|
|
9809a758b4 | ||
|
|
c9739b2d0e | ||
|
|
25e274a4f8 | ||
|
|
fc1c8d8512 | ||
|
|
e27879aa94 | ||
|
|
d219e48541 | ||
|
|
107f5765b0 | ||
|
|
adafffd86b | ||
|
|
9d807d1d63 | ||
|
|
481ba6bce3 | ||
|
|
df82c7cd22 | ||
|
|
bd5af9c721 | ||
|
|
7cb063d511 | ||
|
|
904e61bee6 | ||
|
|
24477694f8 | ||
|
|
87ef48b826 | ||
|
|
da8afc12cf | ||
|
|
84613dcf8a | ||
|
|
dd8aa175d1 | ||
|
|
e3caa24ca4 | ||
|
|
ae352f27e1 | ||
|
|
552a1d4b44 | ||
|
|
b0ed85689b | ||
|
|
a18e093d4a | ||
|
|
79a7c6d991 | ||
|
|
1a2c3e2843 | ||
|
|
741fdc973c | ||
|
|
1400636201 | ||
|
|
6e737b50ee | ||
|
|
c409e3b6ed | ||
|
|
e738d1e46d | ||
|
|
380c55a653 | ||
|
|
9241b3a26a | ||
|
|
3093b6f386 | ||
|
|
42366f4a32 | ||
|
|
6d6fd9a88c | ||
|
|
dc1e0aaf90 | ||
|
|
a5cb1f729d | ||
|
|
e997cd9907 | ||
|
|
c5c7ccc89e | ||
|
|
61d0b5953f | ||
|
|
a5bf5c7dfd | ||
|
|
fd40a29180 | ||
|
|
486b04b70b | ||
|
|
6ad756b215 | ||
|
|
ed1daab1f6 | ||
|
|
f2f4e2ce58 | ||
|
|
845bc8a921 | ||
|
|
f2efc4e682 | ||
|
|
f4d3d67132 | ||
|
|
e14e6d73c6 | ||
|
|
4ec550dc46 | ||
|
|
dd6d4bf1c6 | ||
|
|
a6e433bdc5 | ||
|
|
59898c7883 | ||
|
|
1b7e33c222 | ||
|
|
221ca63d3d | ||
|
|
b7f7ac8d31 | ||
|
|
65f5df8a60 | ||
|
|
8a50337854 | ||
|
|
7b97ac0139 | ||
|
|
1cb3e4b29f | ||
|
|
c89ebbdd22 | ||
|
|
ce738f6b40 | ||
|
|
2a0af8f975 | ||
|
|
2f452dbe74 | ||
|
|
622f95439d | ||
|
|
210e65dd9a | ||
|
|
241de709da | ||
|
|
c6804decfc | ||
|
|
75d3e28ef8 | ||
|
|
99371def54 | ||
|
|
70d2713643 | ||
|
|
41b72563ff | ||
|
|
345f3588bd | ||
|
|
022f740c3f | ||
|
|
ce778afff6 | ||
|
|
9fcb6da960 | ||
|
|
b589f82b6c | ||
|
|
f9e6164245 | ||
|
|
43c5bda806 | ||
|
|
6d3508c62a | ||
|
|
9e64222bdf | ||
|
|
7b911c1658 | ||
|
|
a31d552788 | ||
|
|
000cfda922 | ||
|
|
0abd616ca1 | ||
|
|
2a2ac5a37d | ||
|
|
1496a8a0d0 | ||
|
|
6b690baef6 | ||
|
|
b9cdade832 | ||
|
|
fbc1e9903e | ||
|
|
439f368fd6 | ||
|
|
bbbd729fdf | ||
|
|
c1efcb895c | ||
|
|
cb59eb94a1 | ||
|
|
f355d5da50 | ||
|
|
6e1d58964e | ||
|
|
c3aea5ee8d | ||
|
|
f064859a27 | ||
|
|
9e787bab03 | ||
|
|
97ac4117db | ||
|
|
8cd8ae4255 | ||
|
|
35edd84c22 | ||
|
|
4613feff18 | ||
|
|
fab099c6fb | ||
|
|
107bf53a39 | ||
|
|
e0f2d3fd57 | ||
|
|
93c7ba2fd8 | ||
|
|
5dd00bec87 | ||
|
|
21c7b5ae44 | ||
|
|
a9b05d66a6 | ||
|
|
c85af59b21 | ||
|
|
93ae57adbe | ||
|
|
e1f7618cbd | ||
|
|
3c798a5606 | ||
|
|
d87c4bad21 | ||
|
|
177a8c8eff | ||
|
|
aeda63faf7 | ||
|
|
0a1c847b4b | ||
|
|
ab85c51831 | ||
|
|
f88addc95d | ||
|
|
ea4b20755d | ||
|
|
16b525915e | ||
|
|
af0a997609 | ||
|
|
3fca7d16bb | ||
|
|
7d64dea29b | ||
|
|
999bf6fdb4 | ||
|
|
871217713a | ||
|
|
e5d1af49bf | ||
|
|
fe660cf1a9 | ||
|
|
5ff8868d3e | ||
|
|
1c25f11821 | ||
|
|
626786a008 | ||
|
|
b1c5873bd6 | ||
|
|
748db54f52 | ||
|
|
fbf695eb16 | ||
|
|
98068f55cb | ||
|
|
ef7f51ecf0 | ||
|
|
be35a8ded4 | ||
|
|
7dad7d810a | ||
|
|
596204dc58 | ||
|
|
6ed5f274e6 | ||
|
|
b65a8b9da0 | ||
|
|
8e68d7e796 | ||
|
|
3d5d40c476 | ||
|
|
3e1dc62fa0 | ||
|
|
76b358445e | ||
|
|
91b4e08449 | ||
|
|
80b19192c3 | ||
|
|
d15b58d805 | ||
|
|
5a5fb1f191 | ||
|
|
c8b9c0dae8 | ||
|
|
769ba47a1d | ||
|
|
8a6c13e62f | ||
|
|
7b7b8745b6 | ||
|
|
171f4708d8 | ||
|
|
9ae5e4f059 | ||
|
|
3ddfe9c3bb | ||
|
|
1c7344beaf | ||
|
|
240c1560db | ||
|
|
6af6b86e87 | ||
|
|
50171911f9 | ||
|
|
e6abbe244b | ||
|
|
ebd863f5b6 | ||
|
|
d5ffacae3a | ||
|
|
a870dc3185 | ||
|
|
4b22651a07 | ||
|
|
75fed70c38 | ||
|
|
d716bb8cde | ||
|
|
3cbb8d9b25 | ||
|
|
585691aa0f | ||
|
|
42f3da9b37 | ||
|
|
3ea5446fcc | ||
|
|
c7c6583523 | ||
|
|
d4ca9d6dcf | ||
|
|
76a8742d61 | ||
|
|
73df7f28f4 | ||
|
|
a87686098c | ||
|
|
b501214c02 | ||
|
|
66f9b4f526 | ||
|
|
14566b20b2 | ||
|
|
07b5d4de3d | ||
|
|
3d71e9fc00 | ||
|
|
5071e82e1c | ||
|
|
98ac9fd1ac | ||
|
|
f9ad6aef05 | ||
|
|
92d0b2ea8b | ||
|
|
cbeb64a65c | ||
|
|
d153810205 | ||
|
|
f5cdbea379 | ||
|
|
e838f1d6d4 | ||
|
|
83b78bab34 | ||
|
|
c73da5c5ad | ||
|
|
836b7d0ce9 | ||
|
|
d049eb4903 | ||
|
|
4a99f31aed | ||
|
|
47862d6710 | ||
|
|
e9d9c3aee8 | ||
|
|
d322a29b72 | ||
|
|
5c6eaa8fda | ||
|
|
16c3613267 | ||
|
|
3bc9d24677 | ||
|
|
0d3df4a1e4 | ||
|
|
8341727882 | ||
|
|
e4298fdad6 | ||
|
|
d580e63358 | ||
|
|
ee59479ff8 | ||
|
|
9b565c41c2 | ||
|
|
d0ae7fee72 | ||
|
|
75bb6b9e2d | ||
|
|
c25a3065e1 | ||
|
|
9d18ad51f6 | ||
|
|
704732a046 | ||
|
|
b6cc6f8284 | ||
|
|
d424f2cad5 | ||
|
|
de818282c8 | ||
|
|
8b59a72506 | ||
|
|
aa418cd0d7 | ||
|
|
dd65c67568 | ||
|
|
14f47f8cfc | ||
|
|
bb87b7f20d | ||
|
|
17ef0e927c | ||
|
|
8b089e6cf4 | ||
|
|
dfcae06496 | ||
|
|
eb94aae801 | ||
|
|
cc914d4799 | ||
|
|
b052b462e3 | ||
|
|
060569ee92 | ||
|
|
e96e93f965 | ||
|
|
89d346b2ff | ||
|
|
daadad7bf3 | ||
|
|
610f663c24 | ||
|
|
c091e4cb9f | ||
|
|
9dcf9f5db1 | ||
|
|
12cae1b921 | ||
|
|
dd315e573d | ||
|
|
caaf62ea20 | ||
|
|
c7681df0e8 | ||
|
|
ed0bb1c031 | ||
|
|
f3076f21b2 | ||
|
|
e54c606c6d | ||
|
|
78d90f15f5 | ||
|
|
f89fc84f95 | ||
|
|
723cf1b3b3 | ||
|
|
c506dea02b | ||
|
|
22c9691a73 | ||
|
|
d70fd44d8e | ||
|
|
04bc4770b4 | ||
|
|
7781ac8ec2 | ||
|
|
3d29bb5ca1 | ||
|
|
571d0d71f8 | ||
|
|
996904f056 | ||
|
|
7c79afd0d1 | ||
|
|
226a2145c4 | ||
|
|
41ff8058b2 | ||
|
|
61d1d0ab4d | ||
|
|
efd67346fd | ||
|
|
0d610c45a7 | ||
|
|
a00d27d0c9 | ||
|
|
347bc1665d | ||
|
|
5869f889f9 | ||
|
|
e233aa9f1d | ||
|
|
b9da9a41fd | ||
|
|
e226afbaff | ||
|
|
6910d5ba87 | ||
|
|
427806b0d7 | ||
|
|
2d712eefe1 | ||
|
|
85592142af | ||
|
|
d464e30d83 | ||
|
|
fe949ccdc0 | ||
|
|
e57847c6e1 | ||
|
|
41a30a25c0 | ||
|
|
ad0c585371 | ||
|
|
3678839522 | ||
|
|
c627f4684f | ||
|
|
8d68ee0ee3 | ||
|
|
5d80ca89ea | ||
|
|
920b216466 | ||
|
|
b05a686180 | ||
|
|
ba4d211ee7 | ||
|
|
6ce9055484 | ||
|
|
5b6619d1de | ||
|
|
cc6b364b87 | ||
|
|
c8adba7f13 | ||
|
|
b98d69af18 | ||
|
|
79de3009ad | ||
|
|
95f22f47fe | ||
|
|
fa9f2d9785 | ||
|
|
07455c8ecd | ||
|
|
c564d03c17 | ||
|
|
f8e194a691 | ||
|
|
b059de17fb | ||
|
|
1f02f6c71f | ||
|
|
fa9acc9c89 | ||
|
|
f1e3d8ebf0 | ||
|
|
9ccce5b468 | ||
|
|
2b22c7e050 | ||
|
|
458f6dd447 | ||
|
|
aa269cf2ca | ||
|
|
fa98085dcf | ||
|
|
9351f6bc89 | ||
|
|
6922c716e2 | ||
|
|
2c8ba10e20 | ||
|
|
603e39f362 | ||
|
|
b537081f2a | ||
|
|
fab0227ed3 | ||
|
|
5355caa4e4 | ||
|
|
7d580e7113 | ||
|
|
b47ea5aa1f | ||
|
|
cf7dd55bf4 | ||
|
|
64c56e33f0 | ||
|
|
021941d212 | ||
|
|
e3c198b32b | ||
|
|
c2793cf9ff | ||
|
|
e0c9fa38d3 | ||
|
|
f16d08ab6d | ||
|
|
2f0e8e1e24 | ||
|
|
fc08992244 | ||
|
|
dceb5cdb2c | ||
|
|
4e023502ca | ||
|
|
9708064d26 | ||
|
|
9ae15c198d | ||
|
|
a47697fbfa | ||
|
|
f702c33834 | ||
|
|
2ecba8e161 | ||
|
|
4f4f2b09e3 | ||
|
|
872fd73b05 | ||
|
|
5cf0092b4f | ||
|
|
f50c210725 | ||
|
|
43a293da6a | ||
|
|
1e7274e97f | ||
|
|
025e28399e | ||
|
|
d9c8489c92 | ||
|
|
36447ae5d8 | ||
|
|
b98bf909fb | ||
|
|
91ae6bb107 | ||
|
|
fee97f7f3c | ||
|
|
bf61fe6e28 | ||
|
|
750e1997e6 | ||
|
|
fd16d470bd | ||
|
|
044720c531 | ||
|
|
55585a921f | ||
|
|
cc50faeb09 | ||
|
|
2098170867 | ||
|
|
752d7cb482 | ||
|
|
7efe109c83 | ||
|
|
2a9a53a940 | ||
|
|
849f67d335 | ||
|
|
3bbab6ba27 | ||
|
|
59397ed535 | ||
|
|
387917e3ee | ||
|
|
4cbe9f98ce | ||
|
|
d69080b695 | ||
|
|
f656b314f7 | ||
|
|
267a678f2a | ||
|
|
0e6c40f32f | ||
|
|
915c828fac | ||
|
|
ffb782fe0c | ||
|
|
42ad13dc3c | ||
|
|
6b68287e62 | ||
|
|
52d7a2af26 | ||
|
|
a25c874b7d | ||
|
|
82c179c9ae | ||
|
|
4b2d5a2b3c | ||
|
|
0adae696cf | ||
|
|
2cf99fc1e6 | ||
|
|
89216f2387 | ||
|
|
ee248be2c5 | ||
|
|
0b701d23da | ||
|
|
fc0367c9b4 | ||
|
|
ee77b34ee9 | ||
|
|
11eb29b3b8 | ||
|
|
e169ee19e2 | ||
|
|
94ef0934a5 | ||
|
|
6e57766433 | ||
|
|
c96df58dea | ||
|
|
bcd05803e2 | ||
|
|
aaa68c5808 | ||
|
|
498fcf7402 | ||
|
|
c74c47564e | ||
|
|
7e33afa94d | ||
|
|
bccf52d34a | ||
|
|
5223089dc7 | ||
|
|
970124f70d | ||
|
|
8a30558ac1 | ||
|
|
da586ed219 | ||
|
|
4831095925 | ||
|
|
76c0120e5b | ||
|
|
4de69bd647 | ||
|
|
b489c847fd | ||
|
|
39c56af8c1 | ||
|
|
25c35c4e79 | ||
|
|
bcc1b1e72e | ||
|
|
733005489f | ||
|
|
c6ddf9ac3f | ||
|
|
a75c039a40 | ||
|
|
cf093064e9 | ||
|
|
3f114dda91 | ||
|
|
8db5af5b7a | ||
|
|
d425edf7ea | ||
|
|
b9adf9ef7e | ||
|
|
0c2098d01f | ||
|
|
51fc57efe8 | ||
|
|
c0c4597cd8 | ||
|
|
1d34aa794f | ||
|
|
a473df2afc | ||
|
|
ea52c32400 | ||
|
|
97f2ce043b | ||
|
|
96a90eea25 | ||
|
|
236075a1f8 |
16
.dockerignore
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
node_modules
|
||||
npm-debug.log
|
||||
Dockerfile*
|
||||
docker-compose*
|
||||
.dockerignore
|
||||
.git
|
||||
.github
|
||||
.gitignore
|
||||
README.md
|
||||
LICENSE
|
||||
.vscode
|
||||
dist
|
||||
dist_electron
|
||||
build
|
||||
images
|
||||
script
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
VUE_APP_NETEASE_API_URL=http://127.0.0.1:3000
|
||||
VUE_APP_NETEASE_API_URL=/api
|
||||
VUE_APP_ELECTRON_API_URL=/api
|
||||
VUE_APP_ELECTRON_API_URL_DEV=http://127.0.0.1:3000
|
||||
VUE_APP_ENABLE_SENTRY=false
|
||||
VUE_APP_ELECTRON_API_URL_DEV=http://127.0.0.1:10754
|
||||
VUE_APP_LASTFM_API_KEY=09c55292403d961aa517ff7f5e8a3d9c
|
||||
VUE_APP_LASTFM_API_SHARED_SECRET=307c9fda32b3904e53654baff215cb67
|
||||
DEV_SERVER_PORT=20201
|
||||
|
||||
|
|
|
|||
4
.envrc
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
source_url "https://raw.githubusercontent.com/cachix/devenv/82c0147677e510b247d8b9165c54f73d32dfd899/direnvrc" "sha256-7u4iDd1nZpxL4tCzmPG0dQgC5V+/44Ba+tHkPob1v2k="
|
||||
|
||||
export NIXPKGS_ALLOW_INSECURE=1
|
||||
use devenv
|
||||
7
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
* text eol=lf
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.mp3 binary
|
||||
*.icns binary
|
||||
*.gif binary
|
||||
93
.github/workflows/build.yaml
vendored
|
|
@ -1,6 +1,15 @@
|
|||
name: Build/release
|
||||
name: Release
|
||||
|
||||
on: [push, pull_request]
|
||||
env:
|
||||
YARN_INSTALL_NOPT: yarn add --ignore-platform --ignore-optional
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- v*
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
|
|
@ -8,19 +17,75 @@ jobs:
|
|||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest]
|
||||
os: [macos-latest, windows-latest, ubuntu-22.04]
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: "recursive"
|
||||
|
||||
- name: Install Node.js, NPM and Yarn
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 12.16.3
|
||||
node-version: 16
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install RPM & Pacman (on Ubuntu)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get update &&
|
||||
sudo apt-get install --no-install-recommends -y rpm &&
|
||||
sudo apt-get install --no-install-recommends -y libarchive-tools &&
|
||||
sudo apt-get install --no-install-recommends -y libopenjp2-tools
|
||||
|
||||
- name: Install Snapcraft (on Ubuntu)
|
||||
uses: samuelmeuli/action-snapcraft@v1
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
with:
|
||||
snapcraft_token: ${{ secrets.snapcraft_token }}
|
||||
|
||||
- id: get_unm_version
|
||||
name: Get the installed UNM version
|
||||
run: |
|
||||
yarn --ignore-optional
|
||||
unm_version=$(node -e "console.log(require('./node_modules/@unblockneteasemusic/rust-napi/package.json').version)")
|
||||
echo "::set-output name=unmver::${unm_version}"
|
||||
shell: bash
|
||||
|
||||
- name: Install UNM dependencies for Windows
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
${{ env.YARN_INSTALL_NOPT }} \
|
||||
@unblockneteasemusic/rust-napi-win32-x64-msvc@${{steps.get_unm_version.outputs.unmver}}
|
||||
shell: bash
|
||||
|
||||
- name: Install UNM dependencies for macOS
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
${{ env.YARN_INSTALL_NOPT }} \
|
||||
@unblockneteasemusic/rust-napi-darwin-x64@${{steps.get_unm_version.outputs.unmver}} \
|
||||
@unblockneteasemusic/rust-napi-darwin-arm64@${{steps.get_unm_version.outputs.unmver}} \
|
||||
dmg-license
|
||||
shell: bash
|
||||
|
||||
- name: Install UNM dependencies for Linux
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
${{ env.YARN_INSTALL_NOPT }} \
|
||||
@unblockneteasemusic/rust-napi-linux-x64-gnu@${{steps.get_unm_version.outputs.unmver}} \
|
||||
@unblockneteasemusic/rust-napi-linux-arm64-gnu@${{steps.get_unm_version.outputs.unmver}} \
|
||||
@unblockneteasemusic/rust-napi-linux-arm-gnueabihf@${{steps.get_unm_version.outputs.unmver}}
|
||||
shell: bash
|
||||
|
||||
- name: Build/release Electron app
|
||||
uses: samuelmeuli/action-electron-builder@v1
|
||||
uses: samuelmeuli/action-electron-builder@v1.6.0
|
||||
env:
|
||||
VUE_APP_NETEASE_API_URL: /api
|
||||
VUE_APP_ELECTRON_API_URL: /api
|
||||
VUE_APP_ELECTRON_API_URL_DEV: http://127.0.0.1:10754
|
||||
VUE_APP_LASTFM_API_KEY: 09c55292403d961aa517ff7f5e8a3d9c
|
||||
VUE_APP_LASTFM_API_SHARED_SECRET: 307c9fda32b3904e53654baff215cb67
|
||||
with:
|
||||
# GitHub token, automatically provided to the action
|
||||
# (No need to define this secret in the repo settings)
|
||||
|
|
@ -32,14 +97,20 @@ jobs:
|
|||
|
||||
use_vue_cli: true
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: YesPlayMusic-mac
|
||||
path: dist_electron/*.dmg
|
||||
path: dist_electron/*-universal.dmg
|
||||
if-no-files-found: ignore
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: YesPlayMusic-win
|
||||
path: dist_electron/*.exe
|
||||
path: dist_electron/*Setup*.exe
|
||||
if-no-files-found: ignore
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: YesPlayMusic-linux
|
||||
path: dist_electron/*.AppImage
|
||||
if-no-files-found: ignore
|
||||
|
|
|
|||
48
.github/workflows/sync.yml
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
name: Upstream Sync
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
actions: write
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 * * * *' # every hour
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
sync_latest_from_upstream:
|
||||
name: Sync latest commits from upstream repo
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.repository.fork }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Clean issue notice
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'close-issues'
|
||||
labels: '🚨 Sync Fail'
|
||||
|
||||
- name: Sync upstream changes
|
||||
id: sync
|
||||
uses: aormsby/Fork-Sync-With-Upstream-action@v3.4
|
||||
with:
|
||||
upstream_sync_repo: qier222/YesPlayMusic
|
||||
upstream_sync_branch: master
|
||||
target_sync_branch: master
|
||||
target_repo_token: ${{ secrets.GITHUB_TOKEN }} # automatically generated, no need to set
|
||||
test_mode: false
|
||||
|
||||
- name: Sync check
|
||||
if: failure()
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'create-issue'
|
||||
title: '🚨 同步失败 | Sync Fail'
|
||||
labels: '🚨 Sync Fail'
|
||||
body: |
|
||||
Due to a change in the workflow file of the upstream repository, GitHub has automatically suspended the scheduled automatic update. You need to manually sync your fork.
|
||||
|
||||
由于上游仓库的 workflow 文件变更,导致 GitHub 自动暂停了本次自动更新,你需要手动 Sync Fork 一次。
|
||||
16
.gitignore
vendored
|
|
@ -1,8 +1,6 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
dist_electron
|
||||
Icon?
|
||||
|
||||
# local env files
|
||||
.env
|
||||
|
|
@ -25,9 +23,21 @@ pnpm-debug.log*
|
|||
*.sw?
|
||||
|
||||
.vercel
|
||||
/netease_api
|
||||
|
||||
#Electron-builder output
|
||||
/dist_electron
|
||||
NeteaseCloudMusicApi-master
|
||||
NeteaseCloudMusicApi-master.zip
|
||||
|
||||
# Local Netlify folder
|
||||
.netlify
|
||||
vercel.json
|
||||
# Devenv
|
||||
.devenv*
|
||||
devenv.local.nix
|
||||
|
||||
# direnv
|
||||
.direnv
|
||||
|
||||
# pre-commit
|
||||
.pre-commit-config.yaml
|
||||
|
|
|
|||
4
.npmrc
|
|
@ -1,4 +0,0 @@
|
|||
# 如果发现 npm / yarn 安装太慢,可以解除注释
|
||||
# registry=https://registry.npm.taobao.org/
|
||||
# ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron
|
||||
# phantomjs_cdnurl=https://npm.taobao.org/dist/phantomjs
|
||||
1
.nvmrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
14
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
build
|
||||
coverage
|
||||
dist
|
||||
netease_api
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@
|
|||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"singleQuote": true,
|
||||
"jsxSingleQuote": true,
|
||||
"jsxBracketSameLine": false,
|
||||
"arrowParens": "always",
|
||||
"arrowParens": "avoid",
|
||||
"endOfLine": "lf",
|
||||
"bracketSpacing": true,
|
||||
"htmlWhitespaceSensitivity": "strict"
|
||||
|
|
|
|||
25
Dockerfile
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
FROM node:16.13.1-alpine as build
|
||||
ENV VUE_APP_NETEASE_API_URL=/api
|
||||
WORKDIR /app
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories &&\
|
||||
apk add --no-cache python3 make g++ git
|
||||
COPY package.json yarn.lock ./
|
||||
RUN yarn install
|
||||
COPY . .
|
||||
RUN yarn config set electron_mirror https://npmmirror.com/mirrors/electron/ && \
|
||||
yarn build
|
||||
|
||||
FROM nginx:1.20.2-alpine as app
|
||||
|
||||
COPY --from=build /app/package.json /usr/local/lib/
|
||||
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories &&\
|
||||
apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/v3.14/main libuv \
|
||||
&& apk add --no-cache --update-cache --repository http://dl-cdn.alpinelinux.org/alpine/v3.14/main nodejs npm \
|
||||
&& npm i -g $(awk -F \" '{if($2=="NeteaseCloudMusicApi") print $2"@"$4}' /usr/local/lib/package.json) \
|
||||
&& rm -f /usr/local/lib/package.json
|
||||
|
||||
COPY --from=build /app/docker/nginx.conf.example /etc/nginx/conf.d/default.conf
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
|
||||
CMD nginx ; exec npx NeteaseCloudMusicApi
|
||||
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 qier222
|
||||
Copyright (c) 2020-2023 qier222
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
|||
204
README.md
|
|
@ -8,69 +8,221 @@
|
|||
<p align="center">
|
||||
高颜值的第三方网易云播放器
|
||||
<br />
|
||||
<a href="https://music.qier222.com" target="blank"><strong>🌎 访问DEMO</strong></a> |
|
||||
<a href="#%EF%B8%8F-安装" target="blank"><strong>📦️ 下载安装包</strong></a>
|
||||
<a href="https://music.ineko.cc" target="blank"><strong>🌎 访问DEMO</strong></a> |
|
||||
<a href="#%EF%B8%8F-安装" target="blank"><strong>📦️ 下载安装包</strong></a> |
|
||||
<a href="https://t.me/yesplaymusic" target="blank"><strong>💬 加入交流群</strong></a>
|
||||
<br />
|
||||
<br />
|
||||
</p>
|
||||
</p>
|
||||
|
||||
[![Library][library-screenshot]](https://music.qier222.com)
|
||||
[![Library][library-screenshot]](https://music.ineko.cc)
|
||||
|
||||
|
||||
## 全新版本
|
||||
全新2.0 Alpha测试版已发布,欢迎前往 [Releases](https://github.com/qier222/YesPlayMusic/releases) 页面下载。
|
||||
当前版本将会进入维护模式,除重大bug修复外,不会再更新新功能。
|
||||
|
||||
## ✨ 特性
|
||||
|
||||
- ✅ 使用 Vue.js 全家桶开发
|
||||
- 🔴 网易云账号登录
|
||||
- 📺 MV 播放
|
||||
- 🔴 网易云账号登录(扫码/手机/邮箱登录)
|
||||
- 📺 支持 MV 播放
|
||||
- 📃 支持歌词显示
|
||||
- 📻 支持私人 FM / 每日推荐歌曲
|
||||
- 🚫🤝 无任何社交功能
|
||||
- 🌎️ 海外用户可直接播放(需要登录网易云账号)
|
||||
- 🔐 支持 [UnblockNeteaseMusic](https://github.com/nondanee/UnblockNeteaseMusic),自动使用 QQ/酷狗/酷我音源替换变灰歌曲链接 (网页版不支持)
|
||||
- ⏭️ 支持 MediaSession API,可以使用系统快捷键操作上一首下一首
|
||||
- 🔐 支持 [UnblockNeteaseMusic](https://github.com/UnblockNeteaseMusic/server#音源清单),自动使用[各类音源](https://github.com/UnblockNeteaseMusic/server#音源清单)替换变灰歌曲链接 (网页版不支持)
|
||||
- 「各类音源」指默认启用的音源。
|
||||
- YouTube 音源需自行安装 `yt-dlp`。
|
||||
- ✔️ 每日自动签到(手机端和电脑端同时签到)
|
||||
- 🌚 Light/Dark Mode 自动切换
|
||||
- 👆 支持 Touch Bar
|
||||
- 🖥️ 支持 PWA,可在 Chrome/Edge 里点击地址栏右边的 ➕ 安装到电脑
|
||||
- 🙉 支持显示歌曲和专辑的 Explicit 标志
|
||||
- 🟥 支持 Last.fm Scrobble
|
||||
- ☁️ 支持音乐云盘
|
||||
- ⌨️ 自定义快捷键和全局快捷键
|
||||
- 🎧 支持 Mpris
|
||||
- 🛠 更多特性开发中
|
||||
|
||||
## 📦️ 安装
|
||||
|
||||
Electron 版本由 [@hawtim](https://github.com/hawtim) 和 [@qier222](https://github.com/qier222) 适配并维护,支持 macOS、Windows、Linux。
|
||||
|
||||
访问本项目的 [Releases](https://github.com/qier222/YesPlayMusic/releases) 页面下载安装包,或者访问 [镜像下载站 (大陆访问更快)](https://dl.qier222.com/YesPlayMusic/) 下载。
|
||||
访问本项目的 [Releases](https://github.com/qier222/YesPlayMusic/releases)
|
||||
页面下载安装包。
|
||||
|
||||
## ⚙️ 部署至服务器
|
||||
- macOS 用户可以通过 Homebrew 来安装:`brew install --cask yesplaymusic`
|
||||
|
||||
除了下载安装包使用,你还可以将本项目部署到你的服务器上。
|
||||
- Windows 用户可以通过 Scoop 来安装:`scoop install extras/yesplaymusic`
|
||||
|
||||
## ⚙️ 部署至 Vercel
|
||||
|
||||
除了下载安装包使用,你还可以将本项目部署到 Vercel 或你的服务器上。下面是部署到 Vercel 的方法。
|
||||
|
||||
本项目的 Demo (https://music.qier222.com) 就是部署在 Vercel 上的网站。
|
||||
|
||||
[](https://vercel.com/?utm_source=ohmusic&utm_campaign=oss)
|
||||
|
||||
1. 部署网易云 API,详情参见 [Binaryify/NeteaseCloudMusicApi](https://neteasecloudmusicapi.vercel.app/#/?id=%e5%ae%89%e8%a3%85)
|
||||
。你也可以将 API 部署到 Vercel。
|
||||
|
||||
2. 点击本仓库右上角的 Fork,复制本仓库到你的 GitHub 账号。
|
||||
|
||||
3. 点击仓库的 Add File,选择 Create new file,输入 `vercel.json`,将下面的内容复制粘贴到文件中,并将 `https://your-netease-api.example.com` 替换为你刚刚部署的网易云 API 地址:
|
||||
|
||||
```json
|
||||
{
|
||||
"rewrites": [
|
||||
{
|
||||
"source": "/api/:match*",
|
||||
"destination": "https://your-netease-api.example.com/:match*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
4. 打开 [Vercel.com](https://vercel.com),使用 GitHub 登录。
|
||||
|
||||
5. 点击 Import Git Repository 并选择你刚刚复制的仓库并点击 Import。
|
||||
|
||||
6. 点击 PERSONAL ACCOUNT 旁边的 Select。
|
||||
|
||||
7. 点击 Environment Variables,填写 Name 为 `VUE_APP_NETEASE_API_URL`,Value 为 `/api`,点击 Add。最后点击底部的 Deploy 就可以部署到
|
||||
Vercel 了。
|
||||
|
||||
## ⚙️ 部署到自己的服务器
|
||||
|
||||
除了部署到 Vercel,你还可以部署到自己的服务器上
|
||||
|
||||
1. 部署网易云 API,详情参见 [Binaryify/NeteaseCloudMusicApi](https://github.com/Binaryify/NeteaseCloudMusicApi)
|
||||
|
||||
2. 克隆本仓库
|
||||
|
||||
```sh
|
||||
git clone https://github.com/qier222/YesPlayMusic.git
|
||||
git clone --recursive https://github.com/qier222/YesPlayMusic.git
|
||||
```
|
||||
|
||||
3. 安装依赖
|
||||
|
||||
```sh
|
||||
yarn install
|
||||
|
||||
```
|
||||
|
||||
4. 复制 `/.env.example` 文件为 `/.env`,修改里面 `VUE_APP_NETEASE_API_URL` 的值为网易云 API 地址。本地开发的话可以填写 API 地址为 `http://localhost:3000`,YesPlayMusic 地址为 `http://localhost:8080`
|
||||
4. (可选)使用 Nginx 反向代理 API,将 API 路径映射为 `/api`,如果 API 和网页不在同一个域名下的话(跨域),会有一些 bug。
|
||||
|
||||
5. 复制 `/.env.example` 文件为 `/.env`,修改里面 `VUE_APP_NETEASE_API_URL` 的值为网易云 API 地址。本地开发的话可以填写 API 地址为 `http://localhost:3000`,YesPlayMusic 地址为 `http://localhost:8080`。如果你使用了反向代理 API,可以填写 API 地址为 `/api`。
|
||||
|
||||
```
|
||||
VUE_APP_NETEASE_API_URL=http://localhost:3000
|
||||
```
|
||||
|
||||
5. 编译打包
|
||||
6. 编译打包
|
||||
|
||||
```sh
|
||||
yarn run build
|
||||
```
|
||||
|
||||
6. 将 `/dist` 目录下的文件上传到你的 Web 服务器
|
||||
7. 将 `/dist` 目录下的文件上传到你的 Web 服务器
|
||||
|
||||
## ⚙️ 宝塔面板 docker应用商店 部署
|
||||
|
||||
1. 安装宝塔面板,前往[宝塔面板官网](https://www.bt.cn/new/download.html) ,选择正式版的脚本下载安装。
|
||||
|
||||
2. 安装后登录宝塔面板,在左侧导航栏中点击 Docker,首次进入会提示安装Docker服务,点击立即安装,按提示完成安装
|
||||
|
||||
3. 安装完成后在应用商店中找到YesPlayMusic,点击安装,配置域名、端口等基本信息即可完成安装。
|
||||
|
||||
4. 安装后在浏览器输入上一步骤设置的域名即可访问。
|
||||
|
||||
## ⚙️ Docker 部署
|
||||
|
||||
1. 构建 Docker Image
|
||||
|
||||
```sh
|
||||
docker build -t yesplaymusic .
|
||||
```
|
||||
|
||||
2. 启动 Docker Container
|
||||
|
||||
```sh
|
||||
docker run -d --name YesPlayMusic -p 80:80 yesplaymusic
|
||||
```
|
||||
|
||||
3. Docker Compose 启动
|
||||
|
||||
```sh
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
YesPlayMusic 地址为 `http://localhost`
|
||||
|
||||
## ⚙️ 部署至 Replit
|
||||
|
||||
1. 新建 Repl,选择 Bash 模板
|
||||
|
||||
2. 在 Replit shell 中运行以下命令
|
||||
|
||||
```sh
|
||||
bash <(curl -s -L https://raw.githubusercontent.com/qier222/YesPlayMusic/main/install-replit.sh)
|
||||
```
|
||||
|
||||
3. 首次运行成功后,只需点击绿色按钮 `Run` 即可再次运行
|
||||
|
||||
4. 由于 replit 个人版限制内存为 1G(教育版为 3G),构建过程中可能会失败,请再次运行上述命令或运行以下命令:
|
||||
|
||||
```sh
|
||||
cd /home/runner/${REPL_SLUG}/music && yarn install && yarn run build
|
||||
```
|
||||
|
||||
## 👷♂️ 打包客户端
|
||||
|
||||
如果在 Release 页面没有找到适合你的设备的安装包的话,你可以根据下面的步骤来打包自己的客户端。
|
||||
|
||||
1. 打包 Electron 需要用到 Node.js 和 Yarn。可前往 [Node.js 官网](https://nodejs.org/zh-cn/) 下载安装包。安装 Node.js
|
||||
后可在终端里执行 `npm install -g yarn` 来安装 Yarn。
|
||||
|
||||
2. 使用 `git clone --recursive https://github.com/qier222/YesPlayMusic.git` 克隆本仓库到本地。
|
||||
|
||||
3. 使用 `yarn install` 安装项目依赖。
|
||||
|
||||
4. 复制 `/.env.example` 文件为 `/.env` 。
|
||||
|
||||
5. 选择下列表格的命令来打包适合的你的安装包,打包出来的文件在 `/dist_electron` 目录下。了解更多信息可访问 [electron-builder 文档](https://www.electron.build/cli)
|
||||
|
||||
| 命令 | 说明 |
|
||||
| ------------------------------------------ | ------------------------- |
|
||||
| `yarn electron:build --windows nsis:ia32` | Windows 32 位 |
|
||||
| `yarn electron:build --windows nsis:arm64` | Windows ARM |
|
||||
| `yarn electron:build --linux deb:armv7l` | Debian armv7l(树莓派等) |
|
||||
| `yarn electron:build --macos dir:arm64` | macOS ARM |
|
||||
|
||||
## :computer: 配置开发环境
|
||||
|
||||
本项目由 [NeteaseCloudMusicApi](https://github.com/Binaryify/NeteaseCloudMusicApi) 提供 API。
|
||||
|
||||
运行本项目
|
||||
|
||||
```shell
|
||||
# 安装依赖
|
||||
yarn install
|
||||
|
||||
# 创建本地环境变量
|
||||
cp .env.example .env
|
||||
|
||||
# 运行(网页端)
|
||||
yarn serve
|
||||
|
||||
# 运行(electron)
|
||||
yarn electron:serve
|
||||
```
|
||||
|
||||
本地运行 NeteaseCloudMusicApi,或者将 API [部署至 Vercel](#%EF%B8%8F-部署至-vercel)
|
||||
|
||||
```shell
|
||||
# 运行 API (默认 3000 端口)
|
||||
yarn netease_api:run
|
||||
```
|
||||
|
||||
## ☑️ Todo
|
||||
|
||||
|
|
@ -86,6 +238,8 @@ yarn run build
|
|||
|
||||
## 灵感来源
|
||||
|
||||
API 源代码来自 [Binaryify/NeteaseCloudMusicApi](https://github.com/Binaryify/NeteaseCloudMusicApi)
|
||||
|
||||
- [Apple Music](https://music.apple.com)
|
||||
- [YouTube Music](https://music.youtube.com)
|
||||
- [Spotify](https://www.spotify.com)
|
||||
|
|
@ -93,12 +247,14 @@ yarn run build
|
|||
|
||||
## 🖼️ 截图
|
||||
|
||||
[![artist][artist-screenshot]](https://music.qier222.com)
|
||||
[![album][album-screenshot]](https://music.qier222.com)
|
||||
[![playlist][playlist-screenshot]](https://music.qier222.com)
|
||||
[![explore][explore-screenshot]](https://music.qier222.com)
|
||||
[![search][search-screenshot]](https://music.qier222.com)
|
||||
[![home][home-screenshot]](https://music.qier222.com)
|
||||
![lyrics][lyrics-screenshot]
|
||||
![library-dark][library-dark-screenshot]
|
||||
![album][album-screenshot]
|
||||
![home-2][home-2-screenshot]
|
||||
![artist][artist-screenshot]
|
||||
![search][search-screenshot]
|
||||
![home][home-screenshot]
|
||||
![explore][explore-screenshot]
|
||||
|
||||
<!-- MARKDOWN LINKS & IMAGES -->
|
||||
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
|
||||
|
|
@ -107,6 +263,8 @@ yarn run build
|
|||
[artist-screenshot]: images/artist.png
|
||||
[explore-screenshot]: images/explore.png
|
||||
[home-screenshot]: images/home.png
|
||||
[home-2-screenshot]: images/home-2.png
|
||||
[lyrics-screenshot]: images/lyrics.png
|
||||
[library-screenshot]: images/library.png
|
||||
[playlist-screenshot]: images/playlist.png
|
||||
[library-dark-screenshot]: images/library-dark.png
|
||||
[search-screenshot]: images/search.png
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
module.exports = {
|
||||
presets: ["@vue/cli-plugin-babel/preset"],
|
||||
plugins: [
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
presets: [
|
||||
[
|
||||
'@vue/cli-plugin-babel/preset',
|
||||
{
|
||||
useBuiltIns: 'usage',
|
||||
shippedProposals: true,
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
|
|
|
|||
132
devenv.lock
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
{
|
||||
"nodes": {
|
||||
"devenv": {
|
||||
"locked": {
|
||||
"dir": "src/modules",
|
||||
"lastModified": 1730412360,
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "45847cb1f14a6d8cfa86ea943703c54a8798ae7e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"dir": "src/modules",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"pre-commit-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709087332,
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1730272153,
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2d2a9ddbe3f2c00747398f3dc9b05f7f2ebb0f53",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1730327045,
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "080166c15633801df010977d9d7474b4a6c549d7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-24.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nodejs16": {
|
||||
"locked": {
|
||||
"lastModified": 1700230496,
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a71323f68d4377d12c04a5410e214495ec598d4c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a71323f68d4377d12c04a5410e214495ec598d4c",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1730302582,
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "af8a16fe5c264f5e9e18bcee2859b40a656876cf",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"devenv": "devenv",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nodejs16": "nodejs16",
|
||||
"pre-commit-hooks": "pre-commit-hooks"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
53
devenv.nix
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{ pkgs, lib, config, inputs, ... }:
|
||||
|
||||
let
|
||||
nodejs16 = import inputs.nodejs16 { system = pkgs.stdenv.system; };
|
||||
in
|
||||
{
|
||||
# https://devenv.sh/basics/
|
||||
env.GREET = "devenv";
|
||||
|
||||
# https://devenv.sh/packages/
|
||||
packages = [ pkgs.git ] ++ lib.optionals pkgs.stdenv.isDarwin (with pkgs.darwin.apple_sdk; [
|
||||
frameworks.AppKit
|
||||
]);
|
||||
|
||||
# https://devenv.sh/languages/
|
||||
languages.javascript.enable = true;
|
||||
languages.javascript.package = nodejs16.pkgs.nodejs_16;
|
||||
languages.javascript.corepack.enable = true;
|
||||
# languages.rust.enable = true;
|
||||
|
||||
# https://devenv.sh/processes/
|
||||
# processes.cargo-watch.exec = "cargo-watch";
|
||||
|
||||
# https://devenv.sh/services/
|
||||
# services.postgres.enable = true;
|
||||
|
||||
# https://devenv.sh/scripts/
|
||||
scripts.hello.exec = ''
|
||||
echo hello from $GREET
|
||||
'';
|
||||
|
||||
enterShell = ''
|
||||
hello
|
||||
git --version
|
||||
'';
|
||||
|
||||
# https://devenv.sh/tasks/
|
||||
# tasks = {
|
||||
# "myproj:setup".exec = "mytool build";
|
||||
# "devenv:enterShell".after = [ "myproj:setup" ];
|
||||
# };
|
||||
|
||||
# https://devenv.sh/tests/
|
||||
enterTest = ''
|
||||
echo "Running tests"
|
||||
git --version | grep --color=auto "${pkgs.git.version}"
|
||||
'';
|
||||
|
||||
# https://devenv.sh/pre-commit-hooks/
|
||||
# pre-commit.hooks.shellcheck.enable = true;
|
||||
|
||||
# See full reference at https://devenv.sh/reference/options/
|
||||
}
|
||||
19
devenv.yaml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json
|
||||
inputs:
|
||||
nixpkgs:
|
||||
url: github:nixos/nixpkgs/nixpkgs-unstable
|
||||
nodejs16:
|
||||
url: github:nixos/nixpkgs/a71323f68d4377d12c04a5410e214495ec598d4c
|
||||
|
||||
# https://github.com/cachix/devenv/issues/792#issuecomment-2043166453
|
||||
impure: true
|
||||
# If you're using non-OSS software, you can set allowUnfree to true.
|
||||
# allowUnfree: true
|
||||
|
||||
# If you're willing to use a package that's vulnerable
|
||||
# permittedInsecurePackages:
|
||||
# - "openssl-1.1.1w"
|
||||
|
||||
# If you have more than one devenv you can merge them
|
||||
#imports:
|
||||
# - ./backend
|
||||
39
docker-compose.yml
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
services:
|
||||
YesPlayMusic:
|
||||
build:
|
||||
context: .
|
||||
image: yesplaymusic
|
||||
container_name: YesPlayMusic
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- ./docker/nginx.conf.example:/etc/nginx/conf.d/default.conf:ro
|
||||
ports:
|
||||
- 80:80
|
||||
restart: always
|
||||
depends_on:
|
||||
- UnblockNeteaseMusic
|
||||
environment:
|
||||
- NODE_TLS_REJECT_UNAUTHORIZED=0
|
||||
networks:
|
||||
my_network:
|
||||
|
||||
UnblockNeteaseMusic:
|
||||
image: pan93412/unblock-netease-music-enhanced
|
||||
command: -o kugou kuwo migu bilibili pyncmd -p 80:443 -f 45.127.129.53 -e -
|
||||
# environment:
|
||||
# JSON_LOG: true
|
||||
# LOG_LEVEL: debug
|
||||
networks:
|
||||
my_network:
|
||||
aliases:
|
||||
- music.163.com
|
||||
- interface.music.163.com
|
||||
- interface3.music.163.com
|
||||
- interface.music.163.com.163jiasu.com
|
||||
- interface3.music.163.com.163jiasu.com
|
||||
restart: always
|
||||
|
||||
networks:
|
||||
my_network:
|
||||
driver: bridge
|
||||
28
docker/nginx.conf.example
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
server {
|
||||
gzip on;
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location @rewrites {
|
||||
rewrite ^(.*)$ /index.html last;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_buffers 16 32k;
|
||||
proxy_buffer_size 128k;
|
||||
proxy_busy_buffers_size 128k;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Host $remote_addr;
|
||||
proxy_set_header X-NginX-Proxy true;
|
||||
proxy_pass http://localhost:3000/;
|
||||
}
|
||||
}
|
||||
BIN
images/album.png
|
Before Width: | Height: | Size: 804 KiB After Width: | Height: | Size: 253 KiB |
|
Before Width: | Height: | Size: 730 KiB After Width: | Height: | Size: 228 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 354 KiB |
BIN
images/home-2.png
Normal file
|
After Width: | Height: | Size: 312 KiB |
BIN
images/home.png
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 389 KiB |
BIN
images/library-dark.png
Normal file
|
After Width: | Height: | Size: 335 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 324 KiB |
BIN
images/lyrics.png
Normal file
|
After Width: | Height: | Size: 339 KiB |
|
Before Width: | Height: | Size: 1 MiB |
|
Before Width: | Height: | Size: 1 MiB After Width: | Height: | Size: 276 KiB |
28
install-replit.sh
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/bash
|
||||
|
||||
# 初始化 .replit 和 replit.nix
|
||||
if [[ $1 == i ]];then
|
||||
echo -e 'run = ["bash", "main.sh"]\n\nentrypoint = "main.sh"' >.replit
|
||||
echo -e "{ pkgs }: {\n\t\tdeps = [\n\t\t\tpkgs.nodejs-16_x\n\t\t\tpkgs.yarn\n\t\t\tpkgs.bashInteractive\n\t\t];\n}" > replit.nix
|
||||
exit
|
||||
fi
|
||||
|
||||
# 安装
|
||||
if [[ ! -d api ]];then
|
||||
mkdir api
|
||||
git clone https://github.com/Binaryify/NeteaseCloudMusicApi ./api && \
|
||||
cd api && npm install && cd ..
|
||||
fi
|
||||
|
||||
if [[ ! -d music ]];then
|
||||
mkdir music
|
||||
git clone https://github.com/qier222/YesPlayMusic ./music && \
|
||||
cd music && cp .env.example .env && npm install --force && npm run build && cd ..
|
||||
fi
|
||||
|
||||
# 启动
|
||||
PID=`ps -ef | grep npm | awk '{print $2}' | sed '$d'`
|
||||
|
||||
if [[ ! -z ${PID} ]];then echo $PID | xargs kill;fi
|
||||
nohup bash -c 'cd api && PORT=35216 node app.js' > api.log 2>&1
|
||||
nohup bash -c 'npx serve music/dist/' > music.log 2>&1
|
||||
|
|
@ -7,7 +7,8 @@
|
|||
},
|
||||
"target": "ES6",
|
||||
"module": "commonjs",
|
||||
"allowSyntheticDefaultImports": true
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
|
|
|
|||
83
package.json
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"name": "YesPlayMusic",
|
||||
"version": "0.3.3",
|
||||
"name": "yesplaymusic",
|
||||
"version": "0.4.9",
|
||||
"private": true,
|
||||
"description": "A third party music application for Netease Music",
|
||||
"author": "hawtim<hawtimzhang@gmail.com>",
|
||||
"description": "A third party music player for Netease Music",
|
||||
"author": "qier222<qier222@outlook.com>",
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
|
|
@ -18,57 +18,68 @@
|
|||
"electron:publish": "vue-cli-service electron:build -mwl -p always",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"postuninstall": "electron-builder install-app-deps",
|
||||
"prettier": "npx prettier --write ./src ./script",
|
||||
"netease_api:run": "cd ./netease_api && npm run start",
|
||||
"netease_api:pull": "node script/pull.js",
|
||||
"netease_api:install": "cd ./netease_api && npm install",
|
||||
"netease_api:setup": "npm run netease_api:pull && npm run netease_api:install"
|
||||
"prettier": "npx prettier --write ./src",
|
||||
"netease_api:run": "npx NeteaseCloudMusicApi"
|
||||
},
|
||||
"main": "background.js",
|
||||
"engines": {
|
||||
"node": "14 || 16"
|
||||
},
|
||||
"dependencies": {
|
||||
"@njzy/unblockneteasemusic": "^0.25.3",
|
||||
"axios": "^0.21.0",
|
||||
"big-integer": "^1.6.48",
|
||||
"@unblockneteasemusic/rust-napi": "^0.4.0",
|
||||
"NeteaseCloudMusicApi": "^4.23.3",
|
||||
"axios": "^0.26.1",
|
||||
"change-case": "^4.1.2",
|
||||
"cli-color": "^2.0.0",
|
||||
"color": "^4.2.3",
|
||||
"core-js": "^3.6.5",
|
||||
"crypto-js": "^4.0.0",
|
||||
"dayjs": "^1.8.36",
|
||||
"electron": "11.0.2",
|
||||
"electron-context-menu": "^2.3.0",
|
||||
"dexie": "^3.0.3",
|
||||
"discord-rich-presence": "^0.0.8",
|
||||
"electron": "^13.6.7",
|
||||
"electron-builder": "^23.0.0",
|
||||
"electron-context-menu": "^3.1.2",
|
||||
"electron-debug": "^3.1.0",
|
||||
"electron-devtools-installer": "^3.1.1",
|
||||
"electron-icon-builder": "^1.0.2",
|
||||
"electron-is-dev": "^1.2.0",
|
||||
"electron-devtools-installer": "^3.2",
|
||||
"electron-icon-builder": "^2.0.1",
|
||||
"electron-is-dev": "^2.0.0",
|
||||
"electron-log": "^4.3.0",
|
||||
"electron-store": "^6.0.1",
|
||||
"electron-updater": "^4.3.5",
|
||||
"electron-store": "^8.0.1",
|
||||
"electron-updater": "^5.0.1",
|
||||
"esbuild": "^0.20.1",
|
||||
"esbuild-loader": "^4.0.3",
|
||||
"express": "^4.17.1",
|
||||
"express-fileupload": "^1.2.0",
|
||||
"express-http-proxy": "^1.6.2",
|
||||
"extract-zip": "^2.0.1",
|
||||
"howler": "^2.2.0",
|
||||
"howler": "^2.2.3",
|
||||
"js-cookie": "^2.2.1",
|
||||
"localforage": "^1.9.0",
|
||||
"jsbi": "^4.1.0",
|
||||
"lodash": "^4.17.20",
|
||||
"md5": "^2.3.0",
|
||||
"mpris-service": "^2.1.2",
|
||||
"music-metadata": "^7.5.3",
|
||||
"node-vibrant": "^3.2.1-alpha.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"pac-proxy-agent": "^4.1.0",
|
||||
"plyr": "^3.6.2",
|
||||
"prettier": "2.1.2",
|
||||
"qrcode": "^1.4.4",
|
||||
"register-service-worker": "^1.7.1",
|
||||
"svg-sprite-loader": "^5.0.0",
|
||||
"svg-sprite-loader": "^6.0.11",
|
||||
"tunnel": "^0.0.6",
|
||||
"vscode-codicons": "^0.0.17",
|
||||
"vue": "^2.6.11",
|
||||
"vue-analytics": "^5.22.1",
|
||||
"vue-electron": "^1.0.6",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
"vue-gtag": "1",
|
||||
"vue-i18n": "^8.22.0",
|
||||
"vue-router": "^3.4.3",
|
||||
"vue-slider-component": "^3.2.5",
|
||||
"vuex": "^3.4.0"
|
||||
"vuex": "^3.4.0",
|
||||
"x11": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sentry/browser": "^5.27.0",
|
||||
"@sentry/integrations": "^5.27.0",
|
||||
"@sentry/tracing": "^5.27.0",
|
||||
"@types/node": "^17.0.0",
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-plugin-pwa": "~4.5.0",
|
||||
|
|
@ -76,13 +87,21 @@
|
|||
"@vue/cli-service": "~4.5.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"eslint-config-prettier": "^8.1.0",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
"eslint-plugin-vue": "^7.9.0",
|
||||
"husky": "^4.3.0",
|
||||
"prettier": "2.5.1",
|
||||
"sass": "^1.26.11",
|
||||
"sass-loader": "^10.0.2",
|
||||
"vue-cli-plugin-electron-builder": "~2.0.0-rc.4",
|
||||
"vue-cli-plugin-electron-builder": "~2.1.1",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
},
|
||||
"resolutions": {
|
||||
"icon-gen": "3.0.0",
|
||||
"degenerator": "2.2.0",
|
||||
"electron-builder": "^23.0.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
|
|
@ -91,6 +110,8 @@
|
|||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"plugin:vue/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
|
|
|
|||
BIN
public/img/icons/exit.png
Normal file
|
After Width: | Height: | Size: 223 B |
BIN
public/img/icons/left.png
Normal file
|
After Width: | Height: | Size: 191 B |
BIN
public/img/icons/like.png
Normal file
|
After Width: | Height: | Size: 308 B |
BIN
public/img/icons/menu-dark@88.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
BIN
public/img/icons/pause.png
Normal file
|
After Width: | Height: | Size: 953 B |
BIN
public/img/icons/play.png
Normal file
|
After Width: | Height: | Size: 396 B |
BIN
public/img/icons/repeat.png
Normal file
|
After Width: | Height: | Size: 344 B |
BIN
public/img/icons/right.png
Normal file
|
After Width: | Height: | Size: 218 B |
BIN
public/img/icons/unlike.png
Normal file
|
After Width: | Height: | Size: 932 B |
BIN
public/img/logos/lastfm.png
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
public/img/logos/nyancat-stop.png
Normal file
|
After Width: | Height: | Size: 816 B |
BIN
public/img/logos/yesplaymusic-white24x24.png
Normal file
|
After Width: | Height: | Size: 750 B |
BIN
public/img/touchbar/backward.png
Normal file
|
After Width: | Height: | Size: 801 B |
BIN
public/img/touchbar/forward.png
Normal file
|
After Width: | Height: | Size: 779 B |
BIN
public/img/touchbar/like.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/img/touchbar/like_fill.png
Normal file
|
After Width: | Height: | Size: 871 B |
BIN
public/img/touchbar/next_up.png
Normal file
|
After Width: | Height: | Size: 855 B |
BIN
public/img/touchbar/page_next.png
Normal file
|
After Width: | Height: | Size: 656 B |
BIN
public/img/touchbar/page_prev.png
Normal file
|
After Width: | Height: | Size: 610 B |
BIN
public/img/touchbar/pause.png
Normal file
|
After Width: | Height: | Size: 499 B |
BIN
public/img/touchbar/play.png
Normal file
|
After Width: | Height: | Size: 746 B |
BIN
public/img/touchbar/search.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -1,23 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="referrer" content="no-referrer" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>
|
||||
<strong
|
||||
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="referrer" content="no-referrer" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
|
||||
<title>
|
||||
<%= htmlWebpackPlugin.options.title %>
|
||||
</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
|
||||
properly without JavaScript enabled. Please enable it to
|
||||
continue.</strong
|
||||
>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
User-agent: *
|
||||
Disallow:
|
||||
Disallow: /
|
||||
|
|
|
|||
7
restyled.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
commit_template: 'style: with ${restyler.name}'
|
||||
restylers:
|
||||
- prettier
|
||||
- prettier-json
|
||||
- prettier-markdown
|
||||
- prettier-yaml
|
||||
- whitespace
|
||||
103
script/pull.js
|
|
@ -1,103 +0,0 @@
|
|||
// node module
|
||||
const fs = require("fs");
|
||||
const https = require("https");
|
||||
const resolve = require("path").resolve;
|
||||
const join = require("path").resolve;
|
||||
const extract = require("extract-zip");
|
||||
|
||||
// 函数参数
|
||||
const dest = resolve(__dirname, "../");
|
||||
const fileName = "NeteaseCloudMusicApi-master.zip";
|
||||
const options = {
|
||||
hostname: "github.91chifun.workers.dev",
|
||||
path: `//https://github.com/Binaryify/NeteaseCloudMusicApi/archive/master.zip`,
|
||||
};
|
||||
|
||||
// 完整的流程控制
|
||||
/**
|
||||
* 1. 检查本地文件是否已有
|
||||
* 2. 下载默认/指定版本的 zip 压缩包,等待下载
|
||||
* 3. 解压缩
|
||||
* 4. 进入目录安装依赖 npm install
|
||||
*/
|
||||
|
||||
function fix2(number) {
|
||||
return number.toFixed(2);
|
||||
}
|
||||
|
||||
async function download(options, fileName, callback) {
|
||||
return await new Promise((resolve, reject) => {
|
||||
const destPath = join(__dirname, "../" + fileName);
|
||||
// Check if exist
|
||||
if (fs.existsSync(destPath)) return resolve(destPath);
|
||||
|
||||
const file = fs.createWriteStream(destPath);
|
||||
const request = https.get(options, (res) => {
|
||||
let len = res.headers && parseInt(res.headers["content-length"], 10);
|
||||
let cur = 0;
|
||||
// 1048576 - bytes in 1Megabyte
|
||||
const MEGA = 1048576;
|
||||
let total = 0;
|
||||
if (len) {
|
||||
total = len / MEGA;
|
||||
}
|
||||
if (!len) {
|
||||
console.log(
|
||||
"Downloading, but can not get content-length, please be patient."
|
||||
);
|
||||
}
|
||||
res.on("data", (chunk) => {
|
||||
if (len) {
|
||||
cur += chunk.length;
|
||||
console.log(
|
||||
`Downloading ${fix2((100.0 * cur) / len)}% ${fix2(
|
||||
cur / MEGA
|
||||
)}/${fix2(total)}mb`
|
||||
);
|
||||
}
|
||||
});
|
||||
res.on("end", () => {
|
||||
callback("Downloading complete!");
|
||||
});
|
||||
res.pipe(file);
|
||||
file.on("finish", () => {
|
||||
file.close(() => {
|
||||
callback("File wrote complete!");
|
||||
resolve(destPath);
|
||||
});
|
||||
});
|
||||
file.on("error", (err) => {
|
||||
fs.unlink(destPath);
|
||||
reject(err);
|
||||
});
|
||||
request.on("error", (err) => {
|
||||
console.log("Error: " + err.message);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function unzip(source, target) {
|
||||
try {
|
||||
await extract(source, {
|
||||
dir: target,
|
||||
});
|
||||
console.log("Extraction complete");
|
||||
return true;
|
||||
} catch (err) {
|
||||
// handle any errors
|
||||
if (err.message === "end of central directory record signature not found") {
|
||||
console.log("Not a full_downloaded zip file, removed!");
|
||||
fs.unlinkSync(source);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Download process
|
||||
download(options, fileName, (text) => {
|
||||
console.log(text);
|
||||
}).then((path) => {
|
||||
console.log(path);
|
||||
// Unzip process
|
||||
return unzip(path, dest);
|
||||
});
|
||||
207
src/App.vue
|
|
@ -1,39 +1,43 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<Navbar ref="navbar" />
|
||||
<main v-show="!this.$store.state.showLyrics">
|
||||
<div id="app" :class="{ 'user-select-none': userSelectNone }">
|
||||
<Scrollbar v-show="!showLyrics" ref="scrollbar" />
|
||||
<Navbar v-show="showNavbar" ref="navbar" />
|
||||
<main
|
||||
ref="main"
|
||||
:style="{ overflow: enableScrolling ? 'auto' : 'hidden' }"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<keep-alive>
|
||||
<router-view v-if="$route.meta.keepAlive"></router-view>
|
||||
</keep-alive>
|
||||
<router-view v-if="!$route.meta.keepAlive"></router-view>
|
||||
</main>
|
||||
<transition name="slide-up">
|
||||
<Player
|
||||
v-if="this.$store.state.player.enabled"
|
||||
ref="player"
|
||||
v-show="showPlayer"
|
||||
/></transition>
|
||||
<Player v-if="enablePlayer" v-show="showPlayer" ref="player" />
|
||||
</transition>
|
||||
<Toast />
|
||||
<ModalAddTrackToPlaylist v-if="isAccountLoggedIn" />
|
||||
<ModalNewPlaylist v-if="isAccountLoggedIn" />
|
||||
<transition name="slide-up" v-if="this.$store.state.player.enabled">
|
||||
<Lyrics v-show="this.$store.state.showLyrics" />
|
||||
<transition v-if="enablePlayer" name="slide-up">
|
||||
<Lyrics v-show="showLyrics" />
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ModalAddTrackToPlaylist from "./components/ModalAddTrackToPlaylist.vue";
|
||||
import ModalNewPlaylist from "./components/ModalNewPlaylist.vue";
|
||||
import Navbar from "./components/Navbar.vue";
|
||||
import Player from "./components/Player.vue";
|
||||
import Toast from "./components/Toast.vue";
|
||||
import { ipcRenderer } from "./electron/ipcRenderer";
|
||||
import { isAccountLoggedIn } from "@/utils/auth";
|
||||
import Lyrics from "./views/lyrics.vue";
|
||||
import ModalAddTrackToPlaylist from './components/ModalAddTrackToPlaylist.vue';
|
||||
import ModalNewPlaylist from './components/ModalNewPlaylist.vue';
|
||||
import Scrollbar from './components/Scrollbar.vue';
|
||||
import Navbar from './components/Navbar.vue';
|
||||
import Player from './components/Player.vue';
|
||||
import Toast from './components/Toast.vue';
|
||||
import { ipcRenderer } from './electron/ipcRenderer';
|
||||
import { isAccountLoggedIn, isLooseLoggedIn } from '@/utils/auth';
|
||||
import Lyrics from './views/lyrics.vue';
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
name: 'App',
|
||||
components: {
|
||||
Navbar,
|
||||
Player,
|
||||
|
|
@ -41,163 +45,104 @@ export default {
|
|||
ModalAddTrackToPlaylist,
|
||||
ModalNewPlaylist,
|
||||
Lyrics,
|
||||
Scrollbar,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isElectron: process.env.IS_ELECTRON, // true || undefined
|
||||
userSelectNone: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(['showLyrics', 'settings', 'player', 'enableScrolling']),
|
||||
isAccountLoggedIn() {
|
||||
return isAccountLoggedIn();
|
||||
},
|
||||
showPlayer() {
|
||||
return (
|
||||
["mv", "loginUsername", "login", "loginAccount"].includes(
|
||||
this.$route.name
|
||||
) === false
|
||||
[
|
||||
'mv',
|
||||
'loginUsername',
|
||||
'login',
|
||||
'loginAccount',
|
||||
'lastfmCallback',
|
||||
].includes(this.$route.name) === false
|
||||
);
|
||||
},
|
||||
enablePlayer() {
|
||||
return this.player.enabled && this.$route.name !== 'lastfmCallback';
|
||||
},
|
||||
showNavbar() {
|
||||
return this.$route.name !== 'lastfmCallback';
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (this.isElectron) {
|
||||
ipcRenderer(this);
|
||||
}
|
||||
window.addEventListener("keydown", this.handleKeydown);
|
||||
if (this.isElectron) ipcRenderer(this);
|
||||
window.addEventListener('keydown', this.handleKeydown);
|
||||
this.fetchData();
|
||||
},
|
||||
methods: {
|
||||
handleKeydown(e) {
|
||||
if (e.code === "Space") {
|
||||
if (e.target.tagName === "INPUT") return false;
|
||||
if (this.$route.name === "mv") return false;
|
||||
if (e.code === 'Space') {
|
||||
if (e.target.tagName === 'INPUT') return false;
|
||||
if (this.$route.name === 'mv') return false;
|
||||
e.preventDefault();
|
||||
this.$refs.player.play();
|
||||
this.player.playOrPause();
|
||||
}
|
||||
},
|
||||
fetchData() {
|
||||
if (!isLooseLoggedIn()) return;
|
||||
this.$store.dispatch('fetchLikedSongs');
|
||||
this.$store.dispatch('fetchLikedSongsWithDetails');
|
||||
this.$store.dispatch('fetchLikedPlaylist');
|
||||
if (isAccountLoggedIn()) {
|
||||
this.$store.dispatch('fetchLikedAlbums');
|
||||
this.$store.dispatch('fetchLikedArtists');
|
||||
this.$store.dispatch('fetchLikedMVs');
|
||||
this.$store.dispatch('fetchCloudDisk');
|
||||
}
|
||||
},
|
||||
handleScroll() {
|
||||
this.$refs.scrollbar.handleScroll();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import url("https://fonts.googleapis.com/css2?family=Barlow:ital,wght@0,500;0,600;0,700;0,800;0,900;1,500;1,600;1,700;1,800;1,900&display=swap");
|
||||
|
||||
:root {
|
||||
--color-body-bg: #ffffff;
|
||||
--color-text: #000;
|
||||
--color-primary: #335eea;
|
||||
--color-primary-bg: #eaeffd;
|
||||
--color-secondary: #7a7a7b;
|
||||
--color-secondary-bg: #f5f5f7;
|
||||
--color-navbar-bg: rgba(255, 255, 255, 0.86);
|
||||
--color-primary-bg-for-transparent: rgba(189, 207, 255, 0.28);
|
||||
--color-secondary-bg-for-transparent: rgba(209, 209, 214, 0.28);
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
--color-body-bg: #222222;
|
||||
--color-text: #ffffff;
|
||||
--color-primary: #335eea;
|
||||
--color-primary-bg: #bbcdff;
|
||||
--color-secondary: #7a7a7b;
|
||||
--color-secondary-bg: #323232;
|
||||
--color-navbar-bg: rgba(34, 34, 34, 0.86);
|
||||
--color-primary-bg-for-transparent: rgba(255, 255, 255, 0.12);
|
||||
--color-secondary-bg-for-transparent: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
#app {
|
||||
width: 100%;
|
||||
transition: all 0.4s;
|
||||
}
|
||||
#app,
|
||||
input {
|
||||
font-family: "Barlow", -apple-system, BlinkMacSystemFont, Helvetica Neue,
|
||||
PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC,
|
||||
WenQuanYi Micro Hei, sans-serif;
|
||||
}
|
||||
body {
|
||||
background-color: var(--color-body-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: overlay;
|
||||
min-width: 768px;
|
||||
}
|
||||
|
||||
main {
|
||||
margin-top: 84px;
|
||||
margin-bottom: 96px;
|
||||
padding: {
|
||||
right: 10vw;
|
||||
left: 10vw;
|
||||
}
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
overflow: auto;
|
||||
padding: 64px 10vw 96px 10vw;
|
||||
box-sizing: border-box;
|
||||
scrollbar-width: none; // firefox
|
||||
}
|
||||
|
||||
@media (max-width: 1336px) {
|
||||
main {
|
||||
padding: 0 5vw;
|
||||
padding: 64px 5vw 96px 5vw;
|
||||
}
|
||||
}
|
||||
|
||||
select,
|
||||
button {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
input,
|
||||
button {
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
/* Let's get this party started */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
border-left: 1px solid rgba(128, 128, 128, 0.18);
|
||||
background: var(--color-body-bg);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
-webkit-border-radius: 10px;
|
||||
border-radius: 10px;
|
||||
background: rgba(128, 128, 128, 0.38);
|
||||
}
|
||||
|
||||
[data-theme="dark"] ::-webkit-scrollbar-thumb {
|
||||
background: var(--color-secondary-bg);
|
||||
main::-webkit-scrollbar {
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
.slide-up-enter-active,
|
||||
.slide-up-leave-active {
|
||||
transition: all 0.4s;
|
||||
transition: transform 0.4s;
|
||||
}
|
||||
.slide-up-enter, .slide-up-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
||||
.slide-up-enter,
|
||||
.slide-up-leave-to {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
[data-electron="yes"] {
|
||||
button,
|
||||
.navigation-links a,
|
||||
.playlist-info .description {
|
||||
cursor: default !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import request from "@/utils/request";
|
||||
import { mapTrackPlayableStatus } from "@/utils/common";
|
||||
import request from '@/utils/request';
|
||||
import { mapTrackPlayableStatus } from '@/utils/common';
|
||||
import { cacheAlbum, getAlbumFromCache } from '@/utils/db';
|
||||
|
||||
/**
|
||||
* 获取专辑内容
|
||||
|
|
@ -7,15 +8,23 @@ import { mapTrackPlayableStatus } from "@/utils/common";
|
|||
* @param {number} id
|
||||
*/
|
||||
export function getAlbum(id) {
|
||||
return request({
|
||||
url: "/album",
|
||||
method: "get",
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
}).then((data) => {
|
||||
data.songs = mapTrackPlayableStatus(data.songs);
|
||||
return data;
|
||||
const fetchLatest = () => {
|
||||
return request({
|
||||
url: '/album',
|
||||
method: 'get',
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
}).then(data => {
|
||||
cacheAlbum(id, data);
|
||||
data.songs = mapTrackPlayableStatus(data.songs);
|
||||
return data;
|
||||
});
|
||||
};
|
||||
fetchLatest();
|
||||
|
||||
return getAlbumFromCache(id).then(result => {
|
||||
return result ?? fetchLatest();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -32,8 +41,8 @@ export function getAlbum(id) {
|
|||
*/
|
||||
export function newAlbums(params) {
|
||||
return request({
|
||||
url: "/album/new",
|
||||
method: "get",
|
||||
url: '/album/new',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -46,8 +55,8 @@ export function newAlbums(params) {
|
|||
*/
|
||||
export function albumDynamicDetail(id) {
|
||||
return request({
|
||||
url: "/album/detail/dynamic",
|
||||
method: "get",
|
||||
url: '/album/detail/dynamic',
|
||||
method: 'get',
|
||||
params: { id, timestamp: new Date().getTime() },
|
||||
});
|
||||
}
|
||||
|
|
@ -63,8 +72,8 @@ export function albumDynamicDetail(id) {
|
|||
*/
|
||||
export function likeAAlbum(params) {
|
||||
return request({
|
||||
url: "/album/sub",
|
||||
method: "post",
|
||||
url: '/album/sub',
|
||||
method: 'post',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import request from "@/utils/request";
|
||||
import { mapTrackPlayableStatus } from "@/utils/common";
|
||||
import request from '@/utils/request';
|
||||
import { mapTrackPlayableStatus } from '@/utils/common';
|
||||
import { isAccountLoggedIn } from '@/utils/auth';
|
||||
import { getTrackDetail } from '@/api/track';
|
||||
|
||||
/**
|
||||
* 获取歌手单曲
|
||||
|
|
@ -8,13 +10,19 @@ import { mapTrackPlayableStatus } from "@/utils/common";
|
|||
*/
|
||||
export function getArtist(id) {
|
||||
return request({
|
||||
url: "/artists",
|
||||
method: "get",
|
||||
url: '/artists',
|
||||
method: 'get',
|
||||
params: {
|
||||
id,
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
}).then((data) => {
|
||||
}).then(async data => {
|
||||
if (!isAccountLoggedIn()) {
|
||||
const trackIDs = data.hotSongs.map(t => t.id);
|
||||
const tracks = await getTrackDetail(trackIDs.join(','));
|
||||
data.hotSongs = tracks.songs;
|
||||
return data;
|
||||
}
|
||||
data.hotSongs = mapTrackPlayableStatus(data.hotSongs);
|
||||
return data;
|
||||
});
|
||||
|
|
@ -33,8 +41,8 @@ export function getArtist(id) {
|
|||
*/
|
||||
export function getArtistAlbum(params) {
|
||||
return request({
|
||||
url: "/artist/album",
|
||||
method: "get",
|
||||
url: '/artist/album',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -50,12 +58,14 @@ export function getArtistAlbum(params) {
|
|||
* @param {number=} type
|
||||
*/
|
||||
export function toplistOfArtists(type = null) {
|
||||
let params = {};
|
||||
if (type) {
|
||||
params.type = type;
|
||||
}
|
||||
return request({
|
||||
url: "/toplist/artist",
|
||||
method: "get",
|
||||
params: {
|
||||
type,
|
||||
},
|
||||
url: '/toplist/artist',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
/**
|
||||
|
|
@ -67,8 +77,8 @@ export function toplistOfArtists(type = null) {
|
|||
*/
|
||||
export function artistMv(params) {
|
||||
return request({
|
||||
url: "/artist/mv",
|
||||
method: "get",
|
||||
url: '/artist/mv',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -84,8 +94,8 @@ export function artistMv(params) {
|
|||
*/
|
||||
export function followAArtist(params) {
|
||||
return request({
|
||||
url: "/artist/sub",
|
||||
method: "post",
|
||||
url: '/artist/sub',
|
||||
method: 'post',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -98,8 +108,8 @@ export function followAArtist(params) {
|
|||
*/
|
||||
export function similarArtists(id) {
|
||||
return request({
|
||||
url: "/simi/artist",
|
||||
method: "post",
|
||||
url: '/simi/artist',
|
||||
method: 'post',
|
||||
params: { id },
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import request from "@/utils/request";
|
||||
import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 手机登录
|
||||
|
|
@ -14,11 +14,12 @@ import request from "@/utils/request";
|
|||
*/
|
||||
export function loginWithPhone(params) {
|
||||
return request({
|
||||
url: "/login/cellphone",
|
||||
method: "post",
|
||||
url: '/login/cellphone',
|
||||
method: 'post',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 邮箱登录
|
||||
* - email: 163 网易邮箱
|
||||
|
|
@ -31,12 +32,60 @@ export function loginWithPhone(params) {
|
|||
*/
|
||||
export function loginWithEmail(params) {
|
||||
return request({
|
||||
url: "/login",
|
||||
method: "post",
|
||||
url: '/login',
|
||||
method: 'post',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 二维码key生成接口
|
||||
*/
|
||||
export function loginQrCodeKey() {
|
||||
return request({
|
||||
url: '/login/qr/key',
|
||||
method: 'get',
|
||||
params: {
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 二维码生成接口
|
||||
* 说明: 调用此接口传入上一个接口生成的key可生成二维码图片的base64和二维码信息,
|
||||
* 可使用base64展示图片,或者使用二维码信息内容自行使用第三方二维码生产库渲染二维码
|
||||
* @param {Object} params
|
||||
* @param {string} params.key
|
||||
* @param {string=} params.qrimg 传入后会额外返回二维码图片base64编码
|
||||
*/
|
||||
export function loginQrCodeCreate(params) {
|
||||
return request({
|
||||
url: '/login/qr/create',
|
||||
method: 'get',
|
||||
params: {
|
||||
...params,
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 二维码检测扫码状态接口
|
||||
* 说明: 轮询此接口可获取二维码扫码状态,800为二维码过期,801为等待扫码,802为待确认,803为授权登录成功(803状态码下会返回cookies)
|
||||
* @param {string} key
|
||||
*/
|
||||
export function loginQrCodeCheck(key) {
|
||||
return request({
|
||||
url: '/login/qr/check',
|
||||
method: 'get',
|
||||
params: {
|
||||
key,
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新登录
|
||||
* 说明 : 调用此接口 , 可刷新登录状态
|
||||
|
|
@ -44,8 +93,8 @@ export function loginWithEmail(params) {
|
|||
*/
|
||||
export function refreshCookie() {
|
||||
return request({
|
||||
url: "/login/refresh",
|
||||
method: "post",
|
||||
url: '/login/refresh',
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +104,7 @@ export function refreshCookie() {
|
|||
*/
|
||||
export function logout() {
|
||||
return request({
|
||||
url: "/logout",
|
||||
method: "post",
|
||||
url: '/logout',
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
|
|
|
|||
80
src/api/lastfm.js
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
// Last.fm API documents 👉 https://www.last.fm/api
|
||||
|
||||
import axios from 'axios';
|
||||
import md5 from 'crypto-js/md5';
|
||||
|
||||
const apiKey = process.env.VUE_APP_LASTFM_API_KEY;
|
||||
const apiSharedSecret = process.env.VUE_APP_LASTFM_API_SHARED_SECRET;
|
||||
const baseUrl = window.location.origin;
|
||||
const url = 'https://ws.audioscrobbler.com/2.0/';
|
||||
|
||||
const sign = params => {
|
||||
const sortParamsKeys = Object.keys(params).sort();
|
||||
const sortedParams = sortParamsKeys.reduce((acc, key) => {
|
||||
acc[key] = params[key];
|
||||
return acc;
|
||||
}, {});
|
||||
let signature = '';
|
||||
for (const [key, value] of Object.entries(sortedParams)) {
|
||||
signature += `${key}${value}`;
|
||||
}
|
||||
return md5(signature + apiSharedSecret).toString();
|
||||
};
|
||||
|
||||
export function auth() {
|
||||
const url = process.env.IS_ELECTRON
|
||||
? `https://www.last.fm/api/auth/?api_key=${apiKey}&cb=${baseUrl}/#/lastfm/callback`
|
||||
: `https://www.last.fm/api/auth/?api_key=${apiKey}&cb=${baseUrl}/lastfm/callback`;
|
||||
window.open(url);
|
||||
}
|
||||
|
||||
export function authGetSession(token) {
|
||||
const signature = md5(
|
||||
`api_key${apiKey}methodauth.getSessiontoken${token}${apiSharedSecret}`
|
||||
).toString();
|
||||
return axios({
|
||||
url,
|
||||
method: 'GET',
|
||||
params: {
|
||||
method: 'auth.getSession',
|
||||
format: 'json',
|
||||
api_key: apiKey,
|
||||
api_sig: signature,
|
||||
token,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function trackUpdateNowPlaying(params) {
|
||||
params.api_key = apiKey;
|
||||
params.method = 'track.updateNowPlaying';
|
||||
params.sk = JSON.parse(localStorage.getItem('lastfm'))['key'];
|
||||
const signature = sign(params);
|
||||
|
||||
return axios({
|
||||
url,
|
||||
method: 'POST',
|
||||
params: {
|
||||
...params,
|
||||
api_sig: signature,
|
||||
format: 'json',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function trackScrobble(params) {
|
||||
params.api_key = apiKey;
|
||||
params.method = 'track.scrobble';
|
||||
params.sk = JSON.parse(localStorage.getItem('lastfm'))['key'];
|
||||
const signature = sign(params);
|
||||
|
||||
return axios({
|
||||
url,
|
||||
method: 'POST',
|
||||
params: {
|
||||
...params,
|
||||
api_sig: signature,
|
||||
format: 'json',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import request from "@/utils/request";
|
||||
import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 获取 mv 数据
|
||||
|
|
@ -9,8 +9,8 @@ import request from "@/utils/request";
|
|||
*/
|
||||
export function mvDetail(mvid) {
|
||||
return request({
|
||||
url: "/mv/detail",
|
||||
method: "get",
|
||||
url: '/mv/detail',
|
||||
method: 'get',
|
||||
params: {
|
||||
mvid,
|
||||
timestamp: new Date().getTime(),
|
||||
|
|
@ -30,8 +30,8 @@ export function mvDetail(mvid) {
|
|||
*/
|
||||
export function mvUrl(params) {
|
||||
return request({
|
||||
url: "/mv/url",
|
||||
method: "get",
|
||||
url: '/mv/url',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -43,8 +43,8 @@ export function mvUrl(params) {
|
|||
*/
|
||||
export function simiMv(mvid) {
|
||||
return request({
|
||||
url: "/simi/mv",
|
||||
method: "get",
|
||||
url: '/simi/mv',
|
||||
method: 'get',
|
||||
params: { mvid },
|
||||
});
|
||||
}
|
||||
|
|
@ -62,8 +62,8 @@ export function simiMv(mvid) {
|
|||
export function likeAMV(params) {
|
||||
params.timestamp = new Date().getTime();
|
||||
return request({
|
||||
url: "/mv/sub",
|
||||
method: "post",
|
||||
url: '/mv/sub',
|
||||
method: 'post',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import request from "@/utils/request";
|
||||
import { mapTrackPlayableStatus } from "@/utils/common";
|
||||
import request from '@/utils/request';
|
||||
import { mapTrackPlayableStatus } from '@/utils/common';
|
||||
|
||||
/**
|
||||
* 搜索
|
||||
|
|
@ -18,12 +18,33 @@ import { mapTrackPlayableStatus } from "@/utils/common";
|
|||
*/
|
||||
export function search(params) {
|
||||
return request({
|
||||
url: "/search",
|
||||
method: "get",
|
||||
url: '/search',
|
||||
method: 'get',
|
||||
params,
|
||||
}).then((data) => {
|
||||
}).then(data => {
|
||||
if (data.result?.song !== undefined)
|
||||
data.result.song.songs = mapTrackPlayableStatus(data.result.song.songs);
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
export function personalFM() {
|
||||
return request({
|
||||
url: '/personal_fm',
|
||||
method: 'get',
|
||||
params: {
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function fmTrash(id) {
|
||||
return request({
|
||||
url: '/fm_trash',
|
||||
method: 'post',
|
||||
params: {
|
||||
timestamp: new Date().getTime(),
|
||||
id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import request from "@/utils/request";
|
||||
import { mapTrackPlayableStatus } from "@/utils/common";
|
||||
import request from '@/utils/request';
|
||||
import { mapTrackPlayableStatus } from '@/utils/common';
|
||||
|
||||
/**
|
||||
* 推荐歌单
|
||||
|
|
@ -11,8 +11,8 @@ import { mapTrackPlayableStatus } from "@/utils/common";
|
|||
*/
|
||||
export function recommendPlaylist(params) {
|
||||
return request({
|
||||
url: "/personalized",
|
||||
method: "get",
|
||||
url: '/personalized',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -24,9 +24,12 @@ export function recommendPlaylist(params) {
|
|||
*/
|
||||
export function dailyRecommendPlaylist(params) {
|
||||
return request({
|
||||
url: "/recommend/resource",
|
||||
method: "get",
|
||||
params,
|
||||
url: '/recommend/resource',
|
||||
method: 'get',
|
||||
params: {
|
||||
params,
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
|
|
@ -43,14 +46,16 @@ export function getPlaylistDetail(id, noCache = false) {
|
|||
let params = { id };
|
||||
if (noCache) params.timestamp = new Date().getTime();
|
||||
return request({
|
||||
url: "/playlist/detail",
|
||||
method: "get",
|
||||
url: '/playlist/detail',
|
||||
method: 'get',
|
||||
params,
|
||||
}).then((data) => {
|
||||
data.playlist.tracks = mapTrackPlayableStatus(
|
||||
data.playlist.tracks,
|
||||
data.privileges || []
|
||||
);
|
||||
}).then(data => {
|
||||
if (data.playlist) {
|
||||
data.playlist.tracks = mapTrackPlayableStatus(
|
||||
data.playlist.tracks,
|
||||
data.privileges || []
|
||||
);
|
||||
}
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
|
@ -67,8 +72,8 @@ export function getPlaylistDetail(id, noCache = false) {
|
|||
*/
|
||||
export function highQualityPlaylist(params) {
|
||||
return request({
|
||||
url: "/top/playlist/highquality",
|
||||
method: "get",
|
||||
url: '/top/playlist/highquality',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -86,8 +91,8 @@ export function highQualityPlaylist(params) {
|
|||
*/
|
||||
export function topPlaylist(params) {
|
||||
return request({
|
||||
url: "/top/playlist",
|
||||
method: "get",
|
||||
url: '/top/playlist',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -98,8 +103,8 @@ export function topPlaylist(params) {
|
|||
*/
|
||||
export function playlistCatlist() {
|
||||
return request({
|
||||
url: "/playlist/catlist",
|
||||
method: "get",
|
||||
url: '/playlist/catlist',
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -109,8 +114,8 @@ export function playlistCatlist() {
|
|||
*/
|
||||
export function toplists() {
|
||||
return request({
|
||||
url: "/toplist",
|
||||
method: "get",
|
||||
url: '/toplist',
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -126,8 +131,8 @@ export function toplists() {
|
|||
export function subscribePlaylist(params) {
|
||||
params.timestamp = new Date().getTime();
|
||||
return request({
|
||||
url: "/playlist/subscribe",
|
||||
method: "post",
|
||||
url: '/playlist/subscribe',
|
||||
method: 'post',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -140,8 +145,8 @@ export function subscribePlaylist(params) {
|
|||
*/
|
||||
export function deletePlaylist(id) {
|
||||
return request({
|
||||
url: "/playlist/delete",
|
||||
method: "post",
|
||||
url: '/playlist/delete',
|
||||
method: 'post',
|
||||
params: { id },
|
||||
});
|
||||
}
|
||||
|
|
@ -160,8 +165,8 @@ export function deletePlaylist(id) {
|
|||
export function createPlaylist(params) {
|
||||
params.timestamp = new Date().getTime();
|
||||
return request({
|
||||
url: "/playlist/create",
|
||||
method: "post",
|
||||
url: '/playlist/create',
|
||||
method: 'post',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -178,8 +183,47 @@ export function createPlaylist(params) {
|
|||
export function addOrRemoveTrackFromPlaylist(params) {
|
||||
params.timestamp = new Date().getTime();
|
||||
return request({
|
||||
url: "/playlist/tracks",
|
||||
method: "post",
|
||||
url: '/playlist/tracks',
|
||||
method: 'post',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 每日推荐歌曲
|
||||
* 说明 : 调用此接口 , 可获得每日推荐歌曲 ( 需要登录 )
|
||||
* @param {Object} params
|
||||
* @param {string} params.op
|
||||
* @param {string} params.pid
|
||||
*/
|
||||
export function dailyRecommendTracks() {
|
||||
return request({
|
||||
url: '/recommend/songs',
|
||||
method: 'get',
|
||||
params: { timestamp: new Date().getTime() },
|
||||
}).then(result => {
|
||||
result.data.dailySongs = mapTrackPlayableStatus(
|
||||
result.data.dailySongs,
|
||||
result.data.privileges
|
||||
);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 心动模式/智能播放
|
||||
* 说明 : 登录后调用此接口 , 可获取心动模式/智能播放列表 必选参数 : id : 歌曲 id
|
||||
* - id : 歌曲 id
|
||||
* - pid : 歌单 id
|
||||
* - sid : 要开始播放的歌曲的 id (可选参数)
|
||||
* @param {Object} params
|
||||
* @param {number=} params.id
|
||||
* @param {number=} params.pid
|
||||
*/
|
||||
export function intelligencePlaylist(params) {
|
||||
return request({
|
||||
url: '/playmode/intelligence/list',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
106
src/api/track.js
|
|
@ -1,6 +1,13 @@
|
|||
import store from "@/store";
|
||||
import request from "@/utils/request";
|
||||
import { mapTrackPlayableStatus } from "@/utils/common";
|
||||
import store from '@/store';
|
||||
import request from '@/utils/request';
|
||||
import { mapTrackPlayableStatus } from '@/utils/common';
|
||||
import {
|
||||
cacheTrackDetail,
|
||||
getTrackDetailFromCache,
|
||||
cacheLyric,
|
||||
getLyricFromCache,
|
||||
} from '@/utils/db';
|
||||
|
||||
/**
|
||||
* 获取音乐 url
|
||||
* 说明 : 使用歌单详情接口后 , 能得到的音乐的 id, 但不能得到的音乐 url, 调用此接口, 传入的音乐 id( 可多个 , 用逗号隔开 ), 可以获取对应的音乐的 url,
|
||||
|
|
@ -8,51 +15,85 @@ import { mapTrackPlayableStatus } from "@/utils/common";
|
|||
* @param {string} id - 音乐的 id,例如 id=405998841,33894312
|
||||
*/
|
||||
export function getMP3(id) {
|
||||
let br =
|
||||
store.state.settings?.musicQuality !== undefined
|
||||
? store.state.settings.musicQuality
|
||||
: 320000;
|
||||
const getBr = () => {
|
||||
// 当返回的 quality >= 400000时,就会优先返回 hi-res
|
||||
const quality = store.state.settings?.musicQuality ?? '320000';
|
||||
return quality === 'flac' ? '350000' : quality;
|
||||
};
|
||||
|
||||
return request({
|
||||
url: "/song/url",
|
||||
method: "get",
|
||||
url: '/song/url',
|
||||
method: 'get',
|
||||
params: {
|
||||
id,
|
||||
br,
|
||||
br: getBr(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取歌曲详情
|
||||
* 说明 : 调用此接口 , 传入音乐 id(支持多个 id, 用 , 隔开), 可获得歌曲详情(注意:歌曲封面现在需要通过专辑内容接口获取)
|
||||
* @param {string} ids - 音乐 id, 例如 ids=405998841,33894312
|
||||
*/
|
||||
export function getTrackDetail(ids) {
|
||||
return request({
|
||||
url: "/song/detail",
|
||||
method: "get",
|
||||
params: {
|
||||
ids,
|
||||
},
|
||||
}).then((data) => {
|
||||
data.songs = mapTrackPlayableStatus(data.songs, data.privileges);
|
||||
return data;
|
||||
const fetchLatest = () => {
|
||||
return request({
|
||||
url: '/song/detail',
|
||||
method: 'get',
|
||||
params: {
|
||||
ids,
|
||||
},
|
||||
}).then(data => {
|
||||
data.songs.map(song => {
|
||||
const privileges = data.privileges.find(t => t.id === song.id);
|
||||
cacheTrackDetail(song, privileges);
|
||||
});
|
||||
data.songs = mapTrackPlayableStatus(data.songs, data.privileges);
|
||||
return data;
|
||||
});
|
||||
};
|
||||
fetchLatest();
|
||||
|
||||
let idsInArray = [String(ids)];
|
||||
if (typeof ids === 'string') {
|
||||
idsInArray = ids.split(',');
|
||||
}
|
||||
|
||||
return getTrackDetailFromCache(idsInArray).then(result => {
|
||||
if (result) {
|
||||
result.songs = mapTrackPlayableStatus(result.songs, result.privileges);
|
||||
}
|
||||
return result ?? fetchLatest();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取歌词
|
||||
* 说明 : 调用此接口 , 传入音乐 id 可获得对应音乐的歌词 ( 不需要登录 )
|
||||
* @param {number} id - 音乐 id
|
||||
*/
|
||||
|
||||
export function getLyric(id) {
|
||||
return request({
|
||||
url: "/lyric",
|
||||
method: "get",
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
const fetchLatest = () => {
|
||||
return request({
|
||||
url: '/lyric',
|
||||
method: 'get',
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
}).then(result => {
|
||||
cacheLyric(id, result);
|
||||
return result;
|
||||
});
|
||||
};
|
||||
|
||||
fetchLatest();
|
||||
|
||||
return getLyricFromCache(id).then(result => {
|
||||
return result ?? fetchLatest();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 新歌速递
|
||||
* 说明 : 调用此接口 , 可获取新歌速递
|
||||
|
|
@ -60,13 +101,14 @@ export function getLyric(id) {
|
|||
*/
|
||||
export function topSong(type) {
|
||||
return request({
|
||||
url: "/top/song",
|
||||
method: "get",
|
||||
url: '/top/song',
|
||||
method: 'get',
|
||||
params: {
|
||||
type,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 喜欢音乐
|
||||
* 说明 : 调用此接口 , 传入音乐 id, 可喜欢该音乐
|
||||
|
|
@ -79,8 +121,8 @@ export function topSong(type) {
|
|||
export function likeATrack(params) {
|
||||
params.timestamp = new Date().getTime();
|
||||
return request({
|
||||
url: "/like",
|
||||
method: "get",
|
||||
url: '/like',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -99,8 +141,8 @@ export function likeATrack(params) {
|
|||
export function scrobble(params) {
|
||||
params.timestamp = new Date().getTime();
|
||||
return request({
|
||||
url: "/scrobble",
|
||||
method: "get",
|
||||
url: '/scrobble',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
144
src/api/user.js
|
|
@ -1,4 +1,4 @@
|
|||
import request from "@/utils/request";
|
||||
import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 获取用户详情
|
||||
|
|
@ -8,10 +8,25 @@ import request from "@/utils/request";
|
|||
*/
|
||||
export function userDetail(uid) {
|
||||
return request({
|
||||
url: "/user/detail",
|
||||
method: "get",
|
||||
url: '/user/detail',
|
||||
method: 'get',
|
||||
params: {
|
||||
uid,
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取账号详情
|
||||
* 说明 : 登录后调用此接口 ,可获取用户账号信息
|
||||
*/
|
||||
export function userAccount() {
|
||||
return request({
|
||||
url: '/user/account',
|
||||
method: 'get',
|
||||
params: {
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -29,8 +44,25 @@ export function userDetail(uid) {
|
|||
*/
|
||||
export function userPlaylist(params) {
|
||||
return request({
|
||||
url: "/user/playlist",
|
||||
method: "get",
|
||||
url: '/user/playlist',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户播放记录
|
||||
* 说明 : 登录后调用此接口 , 传入用户 id, 可获取用户播放记录
|
||||
* - uid : 用户 id
|
||||
* - type : type=1 时只返回 weekData, type=0 时返回 allData
|
||||
* @param {Object} params
|
||||
* @param {number} params.uid
|
||||
* @param {number} params.type
|
||||
*/
|
||||
export function userPlayHistory(params) {
|
||||
return request({
|
||||
url: '/user/record',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -43,8 +75,8 @@ export function userPlaylist(params) {
|
|||
*/
|
||||
export function userLikedSongsIDs(uid) {
|
||||
return request({
|
||||
url: "/likelist",
|
||||
method: "get",
|
||||
url: '/likelist',
|
||||
method: 'get',
|
||||
params: {
|
||||
uid,
|
||||
timestamp: new Date().getTime(),
|
||||
|
|
@ -60,8 +92,8 @@ export function userLikedSongsIDs(uid) {
|
|||
*/
|
||||
export function dailySignin(type = 0) {
|
||||
return request({
|
||||
url: "/daily_signin",
|
||||
method: "post",
|
||||
url: '/daily_signin',
|
||||
method: 'post',
|
||||
params: {
|
||||
type,
|
||||
timestamp: new Date().getTime(),
|
||||
|
|
@ -72,17 +104,18 @@ export function dailySignin(type = 0) {
|
|||
/**
|
||||
* 获取收藏的专辑(需要登录)
|
||||
* 说明 : 调用此接口可获取到用户收藏的专辑
|
||||
* - limit : 返回数量 , 默认为 30
|
||||
* - offset : 偏移数量,用于分页 , 如 :( 页数 -1)*30, 其中 30 为 limit 的值 , 默认为 0
|
||||
* - limit : 返回数量 , 默认为 25
|
||||
* - offset : 偏移数量,用于分页 , 如 :( 页数 -1)*25, 其中 25 为 limit 的值 , 默认为 0
|
||||
* @param {Object} params
|
||||
* @param {number} params.limit
|
||||
* @param {number=} params.offset
|
||||
*/
|
||||
export function likedAlbums() {
|
||||
export function likedAlbums(params) {
|
||||
return request({
|
||||
url: "/album/sublist",
|
||||
method: "get",
|
||||
url: '/album/sublist',
|
||||
method: 'get',
|
||||
params: {
|
||||
limit: params.limit,
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
});
|
||||
|
|
@ -92,11 +125,12 @@ export function likedAlbums() {
|
|||
* 获取收藏的歌手(需要登录)
|
||||
* 说明 : 调用此接口可获取到用户收藏的歌手
|
||||
*/
|
||||
export function likedArtists() {
|
||||
export function likedArtists(params) {
|
||||
return request({
|
||||
url: "/artist/sublist",
|
||||
method: "get",
|
||||
url: '/artist/sublist',
|
||||
method: 'get',
|
||||
params: {
|
||||
limit: params.limit,
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
});
|
||||
|
|
@ -106,12 +140,82 @@ export function likedArtists() {
|
|||
* 获取收藏的MV(需要登录)
|
||||
* 说明 : 调用此接口可获取到用户收藏的MV
|
||||
*/
|
||||
export function likedMVs() {
|
||||
export function likedMVs(params) {
|
||||
return request({
|
||||
url: "/mv/sublist",
|
||||
method: "get",
|
||||
url: '/mv/sublist',
|
||||
method: 'get',
|
||||
params: {
|
||||
limit: params.limit,
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传歌曲到云盘(需要登录)
|
||||
*/
|
||||
export function uploadSong(file) {
|
||||
let formData = new FormData();
|
||||
formData.append('songFile', file);
|
||||
return request({
|
||||
url: '/cloud',
|
||||
method: 'post',
|
||||
params: {
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
timeout: 200000,
|
||||
}).catch(error => {
|
||||
alert(`上传失败,Error: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取云盘歌曲(需要登录)
|
||||
* 说明 : 登录后调用此接口 , 可获取云盘数据 , 获取的数据没有对应 url, 需要再调用一 次 /song/url 获取 url
|
||||
* - limit : 返回数量 , 默认为 200
|
||||
* - offset : 偏移数量,用于分页 , 如 :( 页数 -1)*200, 其中 200 为 limit 的值 , 默认为 0
|
||||
* @param {Object} params
|
||||
* @param {number} params.limit
|
||||
* @param {number=} params.offset
|
||||
*/
|
||||
export function cloudDisk(params = {}) {
|
||||
params.timestamp = new Date().getTime();
|
||||
return request({
|
||||
url: '/user/cloud',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取云盘歌曲详情(需要登录)
|
||||
*/
|
||||
export function cloudDiskTrackDetail(id) {
|
||||
return request({
|
||||
url: '/user/cloud/detail',
|
||||
method: 'get',
|
||||
params: {
|
||||
timestamp: new Date().getTime(),
|
||||
id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除云盘歌曲(需要登录)
|
||||
* @param {Array} id
|
||||
*/
|
||||
export function cloudDiskTrackDelete(id) {
|
||||
return request({
|
||||
url: '/user/cloud/del',
|
||||
method: 'get',
|
||||
params: {
|
||||
timestamp: new Date().getTime(),
|
||||
id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
135
src/assets/css/global.scss
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
@font-face {
|
||||
font-family: 'Barlow';
|
||||
font-weight: normal;
|
||||
src: url('~@/assets/fonts/Barlow-Regular.woff2') format('woff2'),
|
||||
url('~@/assets/fonts/Barlow-Regular.ttf') format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Barlow';
|
||||
font-weight: medium;
|
||||
src: url('~@/assets/fonts/Barlow-Medium.woff2') format('woff2'),
|
||||
url('~@/assets/fonts/Barlow-Medium.ttf') format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Barlow';
|
||||
font-weight: 600;
|
||||
src: url('~@/assets/fonts/Barlow-SemiBold.woff2') format('woff2'),
|
||||
url('~@/assets/fonts/Barlow-SemiBold.ttf') format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Barlow';
|
||||
font-weight: bold;
|
||||
src: url('~@/assets/fonts/Barlow-Bold.woff2') format('woff2'),
|
||||
url('~@/assets/fonts/Barlow-Bold.ttf') format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Barlow';
|
||||
font-weight: 800;
|
||||
src: url('~@/assets/fonts/Barlow-ExtraBold.woff2') format('woff2'),
|
||||
url('~@/assets/fonts/Barlow-ExtraBold.ttf') format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Barlow';
|
||||
font-weight: 900;
|
||||
src: url('~@/assets/fonts/Barlow-Black.woff2') format('woff2'),
|
||||
url('~@/assets/fonts/Barlow-Black.ttf') format('truetype');
|
||||
}
|
||||
|
||||
:root {
|
||||
--color-body-bg: #ffffff;
|
||||
--color-text: #000;
|
||||
--color-primary: #335eea;
|
||||
--color-primary-bg: #eaeffd;
|
||||
--color-secondary: #7a7a7b;
|
||||
--color-secondary-bg: #f5f5f7;
|
||||
--color-navbar-bg: rgba(255, 255, 255, 0.86);
|
||||
--color-primary-bg-for-transparent: rgba(189, 207, 255, 0.28);
|
||||
--color-secondary-bg-for-transparent: rgba(209, 209, 214, 0.28);
|
||||
--html-overflow-y: overlay;
|
||||
}
|
||||
|
||||
[data-theme='dark'] {
|
||||
--color-body-bg: #222222;
|
||||
--color-text: #ffffff;
|
||||
--color-primary: #335eea;
|
||||
--color-primary-bg: #bbcdff;
|
||||
--color-secondary: #7a7a7b;
|
||||
--color-secondary-bg: #323232;
|
||||
--color-navbar-bg: rgba(34, 34, 34, 0.86);
|
||||
--color-primary-bg-for-transparent: rgba(255, 255, 255, 0.12);
|
||||
--color-secondary-bg-for-transparent: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
#app,
|
||||
input {
|
||||
font-family: 'Barlow', ui-sans-serif, system-ui, -apple-system,
|
||||
BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei,
|
||||
Source Han Sans SC, Noto Sans CJK SC, WenQuanYi Micro Hei, sans-serif,
|
||||
microsoft uighur;
|
||||
}
|
||||
body {
|
||||
background-color: var(--color-body-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: var(--html-overflow-y);
|
||||
min-width: 768px;
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
|
||||
select,
|
||||
button {
|
||||
font-family: inherit;
|
||||
}
|
||||
button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
input,
|
||||
button {
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
[data-electron='yes'] {
|
||||
button,
|
||||
.navigation-links a,
|
||||
.playlist-info .description {
|
||||
cursor: default !important;
|
||||
}
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
border-left: 1px solid rgba(128, 128, 128, 0.18);
|
||||
background: var(--color-body-bg);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
-webkit-border-radius: 10px;
|
||||
border-radius: 10px;
|
||||
background: rgba(128, 128, 128, 0.38);
|
||||
}
|
||||
|
||||
[data-theme='dark'] ::-webkit-scrollbar-thumb {
|
||||
background: var(--color-secondary-bg);
|
||||
}
|
||||
|
||||
.user-select-none {
|
||||
user-select: none;
|
||||
}
|
||||
|
|
@ -265,17 +265,17 @@ a.plyr__control::before {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.plyr [data-plyr="airplay"],
|
||||
.plyr [data-plyr="captions"],
|
||||
.plyr [data-plyr="fullscreen"],
|
||||
.plyr [data-plyr="pip"] {
|
||||
.plyr [data-plyr='airplay'],
|
||||
.plyr [data-plyr='captions'],
|
||||
.plyr [data-plyr='fullscreen'],
|
||||
.plyr [data-plyr='pip'] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.plyr--airplay-supported [data-plyr="airplay"],
|
||||
.plyr--captions-enabled [data-plyr="captions"],
|
||||
.plyr--fullscreen-enabled [data-plyr="fullscreen"],
|
||||
.plyr--pip-supported [data-plyr="pip"] {
|
||||
.plyr--airplay-supported [data-plyr='airplay'],
|
||||
.plyr--captions-enabled [data-plyr='captions'],
|
||||
.plyr--fullscreen-enabled [data-plyr='fullscreen'],
|
||||
.plyr--pip-supported [data-plyr='pip'] {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
|
|
@ -288,11 +288,11 @@ a.plyr__control::before {
|
|||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.plyr__menu .plyr__control[aria-expanded="true"] svg {
|
||||
.plyr__menu .plyr__control[aria-expanded='true'] svg {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.plyr__menu .plyr__control[aria-expanded="true"] .plyr__tooltip {
|
||||
.plyr__menu .plyr__control[aria-expanded='true'] .plyr__tooltip {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
|
@ -327,7 +327,7 @@ a.plyr__control::before {
|
|||
border: var(--plyr-menu-arrow-size, 4px) solid transparent;
|
||||
border-top-color: rgba(255, 255, 255, 0.9);
|
||||
border-top-color: var(--plyr-menu-background, rgba(255, 255, 255, 0.9));
|
||||
content: "";
|
||||
content: '';
|
||||
height: 0;
|
||||
position: absolute;
|
||||
right: calc(((18px / 2) + calc(10px * 0.7)) - (4px / 2));
|
||||
|
|
@ -341,18 +341,18 @@ a.plyr__control::before {
|
|||
width: 0;
|
||||
}
|
||||
|
||||
.plyr__menu__container [role="menu"] {
|
||||
.plyr__menu__container [role='menu'] {
|
||||
padding: calc(10px * 0.7);
|
||||
padding: calc(var(--plyr-control-spacing, 10px) * 0.7);
|
||||
}
|
||||
|
||||
.plyr__menu__container [role="menuitem"],
|
||||
.plyr__menu__container [role="menuitemradio"] {
|
||||
.plyr__menu__container [role='menuitem'],
|
||||
.plyr__menu__container [role='menuitemradio'] {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.plyr__menu__container [role="menuitem"]:first-child,
|
||||
.plyr__menu__container [role="menuitemradio"]:first-child {
|
||||
.plyr__menu__container [role='menuitem']:first-child,
|
||||
.plyr__menu__container [role='menuitemradio']:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
|
@ -386,7 +386,7 @@ a.plyr__control::before {
|
|||
.plyr__menu__container .plyr__control::after {
|
||||
border: 4px solid transparent;
|
||||
border: var(--plyr-menu-item-arrow-size, 4px) solid transparent;
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
|
|
@ -441,7 +441,7 @@ a.plyr__control::before {
|
|||
background: var(--plyr-menu-back-border-color, #dcdfe5);
|
||||
box-shadow: 0 1px 0 #fff;
|
||||
box-shadow: 0 1px 0 var(--plyr-menu-back-border-shadow-color, #fff);
|
||||
content: "";
|
||||
content: '';
|
||||
height: 1px;
|
||||
left: 0;
|
||||
margin-top: calc(calc(10px * 0.7) / 2);
|
||||
|
|
@ -457,19 +457,19 @@ a.plyr__control::before {
|
|||
border-right-color: currentColor;
|
||||
}
|
||||
|
||||
.plyr__menu__container .plyr__control[role="menuitemradio"] {
|
||||
.plyr__menu__container .plyr__control[role='menuitemradio'] {
|
||||
padding-left: calc(10px * 0.7);
|
||||
padding-left: calc(var(--plyr-control-spacing, 10px) * 0.7);
|
||||
}
|
||||
|
||||
.plyr__menu__container .plyr__control[role="menuitemradio"]::after,
|
||||
.plyr__menu__container .plyr__control[role="menuitemradio"]::before {
|
||||
.plyr__menu__container .plyr__control[role='menuitemradio']::after,
|
||||
.plyr__menu__container .plyr__control[role='menuitemradio']::before {
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
.plyr__menu__container .plyr__control[role="menuitemradio"]::before {
|
||||
.plyr__menu__container .plyr__control[role='menuitemradio']::before {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
content: "";
|
||||
content: '';
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
height: 16px;
|
||||
|
|
@ -479,7 +479,7 @@ a.plyr__control::before {
|
|||
width: 16px;
|
||||
}
|
||||
|
||||
.plyr__menu__container .plyr__control[role="menuitemradio"]::after {
|
||||
.plyr__menu__container .plyr__control[role='menuitemradio']::after {
|
||||
background: #fff;
|
||||
border: 0;
|
||||
height: 6px;
|
||||
|
|
@ -492,7 +492,7 @@ a.plyr__control::before {
|
|||
}
|
||||
|
||||
.plyr__menu__container
|
||||
.plyr__control[role="menuitemradio"][aria-checked="true"]::before {
|
||||
.plyr__control[role='menuitemradio'][aria-checked='true']::before {
|
||||
background: #00b3ff;
|
||||
background: var(
|
||||
--plyr-control-toggle-checked-background,
|
||||
|
|
@ -501,14 +501,14 @@ a.plyr__control::before {
|
|||
}
|
||||
|
||||
.plyr__menu__container
|
||||
.plyr__control[role="menuitemradio"][aria-checked="true"]::after {
|
||||
.plyr__control[role='menuitemradio'][aria-checked='true']::after {
|
||||
opacity: 1;
|
||||
transform: translateY(-50%) scale(1);
|
||||
}
|
||||
|
||||
.plyr__menu__container
|
||||
.plyr__control[role="menuitemradio"].plyr__tab-focus::before,
|
||||
.plyr__menu__container .plyr__control[role="menuitemradio"]:hover::before {
|
||||
.plyr__control[role='menuitemradio'].plyr__tab-focus::before,
|
||||
.plyr__menu__container .plyr__control[role='menuitemradio']:hover::before {
|
||||
background: rgba(35, 40, 47, 0.1);
|
||||
}
|
||||
|
||||
|
|
@ -524,7 +524,7 @@ a.plyr__control::before {
|
|||
pointer-events: none;
|
||||
}
|
||||
|
||||
.plyr--full-ui input[type="range"] {
|
||||
.plyr--full-ui input[type='range'] {
|
||||
-webkit-appearance: none;
|
||||
background: 0 0;
|
||||
border: 0;
|
||||
|
|
@ -547,7 +547,7 @@ a.plyr__control::before {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.plyr--full-ui input[type="range"]::-webkit-slider-runnable-track {
|
||||
.plyr--full-ui input[type='range']::-webkit-slider-runnable-track {
|
||||
background: 0 0;
|
||||
border: 0;
|
||||
border-radius: calc(5px / 2);
|
||||
|
|
@ -566,7 +566,7 @@ a.plyr__control::before {
|
|||
);
|
||||
}
|
||||
|
||||
.plyr--full-ui input[type="range"]::-webkit-slider-thumb {
|
||||
.plyr--full-ui input[type='range']::-webkit-slider-thumb {
|
||||
background: #fff;
|
||||
background: var(--plyr-range-thumb-background, #fff);
|
||||
border: 0;
|
||||
|
|
@ -596,7 +596,7 @@ a.plyr__control::before {
|
|||
);
|
||||
}
|
||||
|
||||
.plyr--full-ui input[type="range"]::-moz-range-track {
|
||||
.plyr--full-ui input[type='range']::-moz-range-track {
|
||||
background: 0 0;
|
||||
border: 0;
|
||||
border-radius: calc(5px / 2);
|
||||
|
|
@ -608,7 +608,7 @@ a.plyr__control::before {
|
|||
user-select: none;
|
||||
}
|
||||
|
||||
.plyr--full-ui input[type="range"]::-moz-range-thumb {
|
||||
.plyr--full-ui input[type='range']::-moz-range-thumb {
|
||||
background: #fff;
|
||||
background: var(--plyr-range-thumb-background, #fff);
|
||||
border: 0;
|
||||
|
|
@ -628,7 +628,7 @@ a.plyr__control::before {
|
|||
width: var(--plyr-range-thumb-height, 13px);
|
||||
}
|
||||
|
||||
.plyr--full-ui input[type="range"]::-moz-range-progress {
|
||||
.plyr--full-ui input[type='range']::-moz-range-progress {
|
||||
background: currentColor;
|
||||
border-radius: calc(5px / 2);
|
||||
border-radius: calc(var(--plyr-range-track-height, 5px) / 2);
|
||||
|
|
@ -636,7 +636,7 @@ a.plyr__control::before {
|
|||
height: var(--plyr-range-track-height, 5px);
|
||||
}
|
||||
|
||||
.plyr--full-ui input[type="range"]::-ms-track {
|
||||
.plyr--full-ui input[type='range']::-ms-track {
|
||||
background: 0 0;
|
||||
border: 0;
|
||||
border-radius: calc(5px / 2);
|
||||
|
|
@ -650,7 +650,7 @@ a.plyr__control::before {
|
|||
color: transparent;
|
||||
}
|
||||
|
||||
.plyr--full-ui input[type="range"]::-ms-fill-upper {
|
||||
.plyr--full-ui input[type='range']::-ms-fill-upper {
|
||||
background: 0 0;
|
||||
border: 0;
|
||||
border-radius: calc(5px / 2);
|
||||
|
|
@ -663,7 +663,7 @@ a.plyr__control::before {
|
|||
user-select: none;
|
||||
}
|
||||
|
||||
.plyr--full-ui input[type="range"]::-ms-fill-lower {
|
||||
.plyr--full-ui input[type='range']::-ms-fill-lower {
|
||||
background: 0 0;
|
||||
border: 0;
|
||||
border-radius: calc(5px / 2);
|
||||
|
|
@ -677,7 +677,7 @@ a.plyr__control::before {
|
|||
background: currentColor;
|
||||
}
|
||||
|
||||
.plyr--full-ui input[type="range"]::-ms-thumb {
|
||||
.plyr--full-ui input[type='range']::-ms-thumb {
|
||||
background: #fff;
|
||||
background: var(--plyr-range-thumb-background, #fff);
|
||||
border: 0;
|
||||
|
|
@ -698,20 +698,20 @@ a.plyr__control::before {
|
|||
margin-top: 0;
|
||||
}
|
||||
|
||||
.plyr--full-ui input[type="range"]::-ms-tooltip {
|
||||
.plyr--full-ui input[type='range']::-ms-tooltip {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.plyr--full-ui input[type="range"]:focus {
|
||||
.plyr--full-ui input[type='range']:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.plyr--full-ui input[type="range"]::-moz-focus-outer {
|
||||
.plyr--full-ui input[type='range']::-moz-focus-outer {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.plyr--full-ui
|
||||
input[type="range"].plyr__tab-focus::-webkit-slider-runnable-track {
|
||||
input[type='range'].plyr__tab-focus::-webkit-slider-runnable-track {
|
||||
outline-color: #00b3ff;
|
||||
outline-color: var(
|
||||
--plyr-tab-focus-color,
|
||||
|
|
@ -722,7 +722,7 @@ a.plyr__control::before {
|
|||
outline-width: 3px;
|
||||
}
|
||||
|
||||
.plyr--full-ui input[type="range"].plyr__tab-focus::-moz-range-track {
|
||||
.plyr--full-ui input[type='range'].plyr__tab-focus::-moz-range-track {
|
||||
outline-color: #00b3ff;
|
||||
outline-color: var(
|
||||
--plyr-tab-focus-color,
|
||||
|
|
@ -733,7 +733,7 @@ a.plyr__control::before {
|
|||
outline-width: 3px;
|
||||
}
|
||||
|
||||
.plyr--full-ui input[type="range"].plyr__tab-focus::-ms-track {
|
||||
.plyr--full-ui input[type='range'].plyr__tab-focus::-ms-track {
|
||||
outline-color: #00b3ff;
|
||||
outline-color: var(
|
||||
--plyr-tab-focus-color,
|
||||
|
|
@ -769,7 +769,7 @@ a.plyr__control::before {
|
|||
}
|
||||
|
||||
.plyr__time + .plyr__time::before {
|
||||
content: "\2044";
|
||||
content: '\2044';
|
||||
margin-right: 10px;
|
||||
margin-right: var(--plyr-control-spacing, 10px);
|
||||
}
|
||||
|
|
@ -821,7 +821,7 @@ a.plyr__control::before {
|
|||
var(--plyr-tooltip-background, rgba(255, 255, 255, 0.9));
|
||||
bottom: calc(4px * -1);
|
||||
bottom: calc(var(--plyr-tooltip-arrow-size, 4px) * -1);
|
||||
content: "";
|
||||
content: '';
|
||||
height: 0;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
|
|
@ -906,7 +906,7 @@ a.plyr__control::before {
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.plyr__progress input[type="range"],
|
||||
.plyr__progress input[type='range'],
|
||||
.plyr__progress__buffer {
|
||||
margin-left: calc(13px * -0.5);
|
||||
margin-left: calc(var(--plyr-range-thumb-height, 13px) * -0.5);
|
||||
|
|
@ -916,7 +916,7 @@ a.plyr__control::before {
|
|||
width: calc(100% + var(--plyr-range-thumb-height, 13px));
|
||||
}
|
||||
|
||||
.plyr__progress input[type="range"] {
|
||||
.plyr__progress input[type='range'] {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
|
@ -1024,7 +1024,7 @@ a.plyr__control::before {
|
|||
width: 20%;
|
||||
}
|
||||
|
||||
.plyr__volume input[type="range"] {
|
||||
.plyr__volume input[type='range'] {
|
||||
margin-left: calc(10px / 2);
|
||||
margin-left: calc(var(--plyr-control-spacing, 10px) / 2);
|
||||
margin-right: calc(10px / 2);
|
||||
|
|
@ -1054,7 +1054,7 @@ a.plyr__control::before {
|
|||
|
||||
.plyr--audio .plyr__control.plyr__tab-focus,
|
||||
.plyr--audio .plyr__control:hover,
|
||||
.plyr--audio .plyr__control[aria-expanded="true"] {
|
||||
.plyr--audio .plyr__control[aria-expanded='true'] {
|
||||
background: #00b3ff;
|
||||
background: var(
|
||||
--plyr-audio-control-background-hover,
|
||||
|
|
@ -1064,7 +1064,7 @@ a.plyr__control::before {
|
|||
color: var(--plyr-audio-control-color-hover, #fff);
|
||||
}
|
||||
|
||||
.plyr--full-ui.plyr--audio input[type="range"]::-webkit-slider-runnable-track {
|
||||
.plyr--full-ui.plyr--audio input[type='range']::-webkit-slider-runnable-track {
|
||||
background-color: rgba(193, 200, 209, 0.6);
|
||||
background-color: var(
|
||||
--plyr-audio-range-track-background,
|
||||
|
|
@ -1072,7 +1072,7 @@ a.plyr__control::before {
|
|||
);
|
||||
}
|
||||
|
||||
.plyr--full-ui.plyr--audio input[type="range"]::-moz-range-track {
|
||||
.plyr--full-ui.plyr--audio input[type='range']::-moz-range-track {
|
||||
background-color: rgba(193, 200, 209, 0.6);
|
||||
background-color: var(
|
||||
--plyr-audio-range-track-background,
|
||||
|
|
@ -1080,7 +1080,7 @@ a.plyr__control::before {
|
|||
);
|
||||
}
|
||||
|
||||
.plyr--full-ui.plyr--audio input[type="range"]::-ms-track {
|
||||
.plyr--full-ui.plyr--audio input[type='range']::-ms-track {
|
||||
background-color: rgba(193, 200, 209, 0.6);
|
||||
background-color: var(
|
||||
--plyr-audio-range-track-background,
|
||||
|
|
@ -1088,7 +1088,7 @@ a.plyr__control::before {
|
|||
);
|
||||
}
|
||||
|
||||
.plyr--full-ui.plyr--audio input[type="range"]:active::-webkit-slider-thumb {
|
||||
.plyr--full-ui.plyr--audio input[type='range']:active::-webkit-slider-thumb {
|
||||
box-shadow: 0 1px 1px rgba(35, 40, 47, 0.15), 0 0 0 1px rgba(35, 40, 47, 0.2),
|
||||
0 0 0 3px rgba(35, 40, 47, 0.1);
|
||||
box-shadow: var(
|
||||
|
|
@ -1100,7 +1100,7 @@ a.plyr__control::before {
|
|||
var(--plyr-audio-range-thumb-active-shadow-color, rgba(35, 40, 47, 0.1));
|
||||
}
|
||||
|
||||
.plyr--full-ui.plyr--audio input[type="range"]:active::-moz-range-thumb {
|
||||
.plyr--full-ui.plyr--audio input[type='range']:active::-moz-range-thumb {
|
||||
box-shadow: 0 1px 1px rgba(35, 40, 47, 0.15), 0 0 0 1px rgba(35, 40, 47, 0.2),
|
||||
0 0 0 3px rgba(35, 40, 47, 0.1);
|
||||
box-shadow: var(
|
||||
|
|
@ -1112,7 +1112,7 @@ a.plyr__control::before {
|
|||
var(--plyr-audio-range-thumb-active-shadow-color, rgba(35, 40, 47, 0.1));
|
||||
}
|
||||
|
||||
.plyr--full-ui.plyr--audio input[type="range"]:active::-ms-thumb {
|
||||
.plyr--full-ui.plyr--audio input[type='range']:active::-ms-thumb {
|
||||
box-shadow: 0 1px 1px rgba(35, 40, 47, 0.15), 0 0 0 1px rgba(35, 40, 47, 0.2),
|
||||
0 0 0 3px rgba(35, 40, 47, 0.1);
|
||||
box-shadow: var(
|
||||
|
|
@ -1207,7 +1207,7 @@ a.plyr__control::before {
|
|||
|
||||
.plyr--video .plyr__control.plyr__tab-focus,
|
||||
.plyr--video .plyr__control:hover,
|
||||
.plyr--video .plyr__control[aria-expanded="true"] {
|
||||
.plyr--video .plyr__control[aria-expanded='true'] {
|
||||
background: #00b3ff;
|
||||
background: var(
|
||||
--plyr-video-control-background-hover,
|
||||
|
|
@ -1258,7 +1258,7 @@ a.plyr__control::before {
|
|||
display: block;
|
||||
}
|
||||
|
||||
.plyr--full-ui.plyr--video input[type="range"]::-webkit-slider-runnable-track {
|
||||
.plyr--full-ui.plyr--video input[type='range']::-webkit-slider-runnable-track {
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
background-color: var(
|
||||
--plyr-video-range-track-background,
|
||||
|
|
@ -1266,7 +1266,7 @@ a.plyr__control::before {
|
|||
);
|
||||
}
|
||||
|
||||
.plyr--full-ui.plyr--video input[type="range"]::-moz-range-track {
|
||||
.plyr--full-ui.plyr--video input[type='range']::-moz-range-track {
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
background-color: var(
|
||||
--plyr-video-range-track-background,
|
||||
|
|
@ -1274,7 +1274,7 @@ a.plyr__control::before {
|
|||
);
|
||||
}
|
||||
|
||||
.plyr--full-ui.plyr--video input[type="range"]::-ms-track {
|
||||
.plyr--full-ui.plyr--video input[type='range']::-ms-track {
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
background-color: var(
|
||||
--plyr-video-range-track-background,
|
||||
|
|
@ -1282,7 +1282,7 @@ a.plyr__control::before {
|
|||
);
|
||||
}
|
||||
|
||||
.plyr--full-ui.plyr--video input[type="range"]:active::-webkit-slider-thumb {
|
||||
.plyr--full-ui.plyr--video input[type='range']:active::-webkit-slider-thumb {
|
||||
box-shadow: 0 1px 1px rgba(35, 40, 47, 0.15), 0 0 0 1px rgba(35, 40, 47, 0.2),
|
||||
0 0 0 3px rgba(255, 255, 255, 0.5);
|
||||
box-shadow: var(
|
||||
|
|
@ -1297,7 +1297,7 @@ a.plyr__control::before {
|
|||
);
|
||||
}
|
||||
|
||||
.plyr--full-ui.plyr--video input[type="range"]:active::-moz-range-thumb {
|
||||
.plyr--full-ui.plyr--video input[type='range']:active::-moz-range-thumb {
|
||||
box-shadow: 0 1px 1px rgba(35, 40, 47, 0.15), 0 0 0 1px rgba(35, 40, 47, 0.2),
|
||||
0 0 0 3px rgba(255, 255, 255, 0.5);
|
||||
box-shadow: var(
|
||||
|
|
@ -1312,7 +1312,7 @@ a.plyr__control::before {
|
|||
);
|
||||
}
|
||||
|
||||
.plyr--full-ui.plyr--video input[type="range"]:active::-ms-thumb {
|
||||
.plyr--full-ui.plyr--video input[type='range']:active::-ms-thumb {
|
||||
box-shadow: 0 1px 1px rgba(35, 40, 47, 0.15), 0 0 0 1px rgba(35, 40, 47, 0.2),
|
||||
0 0 0 3px rgba(255, 255, 255, 0.5);
|
||||
box-shadow: var(
|
||||
|
|
@ -1713,7 +1713,7 @@ a.plyr__control::before {
|
|||
var(--plyr-tooltip-background, rgba(255, 255, 255, 0.9));
|
||||
bottom: calc(4px * -1);
|
||||
bottom: calc(var(--plyr-tooltip-arrow-size, 4px) * -1);
|
||||
content: "";
|
||||
content: '';
|
||||
height: 0;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@
|
|||
}
|
||||
|
||||
.nyancat .vue-slider-dot-handle {
|
||||
background: url("/img/logos/nyancat.gif");
|
||||
background: url('/img/logos/nyancat.gif');
|
||||
background-size: 36px;
|
||||
width: 36px;
|
||||
height: 24px;
|
||||
|
|
@ -100,6 +100,11 @@
|
|||
visibility: visible;
|
||||
}
|
||||
|
||||
.nyancat-stop .vue-slider-dot-handle {
|
||||
background-image: url('/img/logos/nyancat-stop.png');
|
||||
transition: 300ms;
|
||||
}
|
||||
|
||||
/* lyrics */
|
||||
.lyrics-page .vue-slider-rail {
|
||||
background-color: rgba(128, 128, 128, 0.18);
|
||||
|
|
@ -121,10 +126,19 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
body[data-theme="dark"] .lyrics-page .vue-slider-process {
|
||||
body[data-theme='dark'] .lyrics-page .vue-slider-process {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
body[data-theme="dark"] .lyrics-page .vue-slider-dot-handle {
|
||||
body[data-theme='dark'] .lyrics-page .vue-slider-dot-handle {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.lyrics-page[data-theme='dark'] .vue-slider-rail {
|
||||
background-color: rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
.lyrics-page[data-theme='dark'] .vue-slider-process,
|
||||
.lyrics-page[data-theme='dark'] .vue-slider-dot-handle {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
|
|
|||
BIN
src/assets/fonts/Barlow-Black.ttf
Executable file
BIN
src/assets/fonts/Barlow-Black.woff2
Normal file
BIN
src/assets/fonts/Barlow-Bold.ttf
Executable file
BIN
src/assets/fonts/Barlow-Bold.woff2
Normal file
BIN
src/assets/fonts/Barlow-ExtraBold.ttf
Executable file
BIN
src/assets/fonts/Barlow-ExtraBold.woff2
Normal file
BIN
src/assets/fonts/Barlow-Medium.ttf
Executable file
BIN
src/assets/fonts/Barlow-Medium.woff2
Normal file
BIN
src/assets/fonts/Barlow-Regular.ttf
Executable file
BIN
src/assets/fonts/Barlow-Regular.woff2
Normal file
BIN
src/assets/fonts/Barlow-SemiBold.ttf
Executable file
BIN
src/assets/fonts/Barlow-SemiBold.woff2
Normal file
1
src/assets/icons/arrow-down.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 32 32" height="32px" id="Layer_1" version="1.1" viewBox="0 0 32 32" width="32px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M14.77,23.795L5.185,14.21c-0.879-0.879-0.879-2.317,0-3.195l0.8-0.801c0.877-0.878,2.316-0.878,3.194,0 l7.315,7.315l7.316-7.315c0.878-0.878,2.317-0.878,3.194,0l0.8,0.801c0.879,0.878,0.879,2.316,0,3.195l-9.587,9.585 c-0.471,0.472-1.104,0.682-1.723,0.647C15.875,24.477,15.243,24.267,14.77,23.795z" fill="currentColor"/></svg>
|
||||
|
After Width: | Height: | Size: 666 B |
1
src/assets/icons/arrow-up-alt.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="arrow-up" class="svg-inline--fa fa-arrow-up fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M34.9 289.5l-22.2-22.2c-9.4-9.4-9.4-24.6 0-33.9L207 39c9.4-9.4 24.6-9.4 33.9 0l194.3 194.3c9.4 9.4 9.4 24.6 0 33.9L413 289.4c-9.5 9.5-25 9.3-34.3-.4L264 168.6V456c0 13.3-10.7 24-24 24h-32c-13.3 0-24-10.7-24-24V168.6L69.2 289.1c-9.3 9.8-24.8 10-34.3.4z"></path></svg>
|
||||
|
After Width: | Height: | Size: 487 B |
1
src/assets/icons/arrow-up.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 32 32" height="32px" id="Layer_1" version="1.1" viewBox="0 0 32 32" width="32px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M18.221,7.206l9.585,9.585c0.879,0.879,0.879,2.317,0,3.195l-0.8,0.801c-0.877,0.878-2.316,0.878-3.194,0 l-7.315-7.315l-7.315,7.315c-0.878,0.878-2.317,0.878-3.194,0l-0.8-0.801c-0.879-0.878-0.879-2.316,0-3.195l9.587-9.585 c0.471-0.472,1.103-0.682,1.723-0.647C17.115,6.524,17.748,6.734,18.221,7.206z" fill="currentColor"/></svg>
|
||||
|
After Width: | Height: | Size: 668 B |
|
|
@ -1 +0,0 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="circle" class="svg-inline--fa fa-circle fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 301 B |
1
src/assets/icons/dropdown.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="caret-down" class="svg-inline--fa fa-caret-down fa-w-10" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z"></path></svg>
|
||||
|
After Width: | Height: | Size: 359 B |
|
|
@ -1 +0,0 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="expand-alt" class="svg-inline--fa fa-expand-alt fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M212.686 315.314L120 408l32.922 31.029c15.12 15.12 4.412 40.971-16.97 40.971h-112C10.697 480 0 469.255 0 456V344c0-21.382 25.803-32.09 40.922-16.971L72 360l92.686-92.686c6.248-6.248 16.379-6.248 22.627 0l25.373 25.373c6.249 6.248 6.249 16.378 0 22.627zm22.628-118.628L328 104l-32.922-31.029C279.958 57.851 290.666 32 312.048 32h112C437.303 32 448 42.745 448 56v112c0 21.382-25.803 32.09-40.922 16.971L376 152l-92.686 92.686c-6.248 6.248-16.379 6.248-22.627 0l-25.373-25.373c-6.249-6.248-6.249-16.378 0-22.627z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 749 B |
1
src/assets/icons/fm.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="radio-alt" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-radio-alt fa-w-16 fa-7x"><path fill="currentColor" d="M209 368h-64a16 16 0 0 0-16 16v16a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16v-16a16 16 0 0 0-16-16zm144 56a72 72 0 1 0-72-72 72.09 72.09 0 0 0 72 72zm96-296H212.5l288.83-81.21a16 16 0 0 0 11.07-19.74l-4.33-15.38A16 16 0 0 0 488.33.6L47.68 124.5A64 64 0 0 0 1 186.11V448a64 64 0 0 0 64 64h384a64 64 0 0 0 64-64V192a64 64 0 0 0-64-64zm16 320a16 16 0 0 1-16 16H65a16 16 0 0 1-16-16V256h416zM113 336h128a16 16 0 0 0 16-16v-16a16 16 0 0 0-16-16H113a16 16 0 0 0-16 16v16a16 16 0 0 0 16 16z" class=""></path></svg>
|
||||
|
After Width: | Height: | Size: 733 B |
3
src/assets/icons/fullscreen-exit.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="14" height="14" viewBox="0 0 14 14" data-darkreader-inline-fill="" xmlns="http://www.w3.org/2000/svg">
|
||||
<path id="path" d="M1 11L3 11L3 13C3 13.55 3.45 14 4 14C4.55 14 5 13.55 5 13L5 10C5 9.45 4.55 9 4 9L1 9C0.45 9 0 9.45 0 10C0 10.55 0.45 11 1 11ZM11 3L11 1C11 0.45 10.55 0 10 0C9.45 0 9 0.45 9 1L9 4C9 4.54 9.45 5 10 5L13 5C13.55 5 14 4.54 14 4C14 3.45 13.55 3 13 3L11 3Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 396 B |
3
src/assets/icons/fullscreen.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="14" height="14" viewBox="0 0 14 14" data-darkreader-inline-fill="" xmlns="http://www.w3.org/2000/svg">
|
||||
<path id="path" d="M1 9C0.45 9 0 9.45 0 10L0 13C0 13.55 0.45 14 1 14L4 14C4.55 14 5 13.55 5 13C5 12.45 4.55 12 4 12L2 12L2 10C2 9.45 1.55 9 1 9ZM9 1C9 1.54 9.45 2 10 2L12 2L12 4C12 4.54 12.45 5 13 5C13.55 5 14 4.54 14 4L14 1C14 0.45 13.55 0 13 0L10 0C9.45 0 9 0.45 9 1Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 396 B |
|
|
@ -1,8 +1,7 @@
|
|||
import Vue from "vue";
|
||||
import SvgIcon from "@/components/SvgIcon";
|
||||
import Vue from 'vue';
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
|
||||
Vue.component("svg-icon", SvgIcon);
|
||||
const requireAll = (requireContext) =>
|
||||
requireContext.keys().map(requireContext);
|
||||
const req = require.context("./", true, /\.svg$/);
|
||||
Vue.component('svg-icon', SvgIcon);
|
||||
const requireAll = requireContext => requireContext.keys().map(requireContext);
|
||||
const req = require.context('./', true, /\.svg$/);
|
||||
requireAll(req);
|
||||
|
|
|
|||
1
src/assets/icons/login.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="sign-in-alt" class="svg-inline--fa fa-sign-in-alt fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M416 448h-84c-6.6 0-12-5.4-12-12v-40c0-6.6 5.4-12 12-12h84c17.7 0 32-14.3 32-32V160c0-17.7-14.3-32-32-32h-84c-6.6 0-12-5.4-12-12V76c0-6.6 5.4-12 12-12h84c53 0 96 43 96 96v192c0 53-43 96-96 96zm-47-201L201 79c-15-15-41-4.5-41 17v96H24c-13.3 0-24 10.7-24 24v96c0 13.3 10.7 24 24 24h136v96c0 21.5 26 32 41 17l168-168c9.3-9.4 9.3-24.6 0-34z"></path></svg>
|
||||
|
After Width: | Height: | Size: 579 B |
1
src/assets/icons/plus.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg height="512px" id="Layer_1" style="enable-background:new 0 0 512 512;" version="1.1" viewBox="0 0 512 512" width="512px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M417.4,224H288V94.6c0-16.9-14.3-30.6-32-30.6c-17.7,0-32,13.7-32,30.6V224H94.6C77.7,224,64,238.3,64,256 c0,17.7,13.7,32,30.6,32H224v129.4c0,16.9,14.3,30.6,32,30.6c17.7,0,32-13.7,32-30.6V288h129.4c16.9,0,30.6-14.3,30.6-32 C448,238.3,434.3,224,417.4,224z"/></svg>
|
||||
|
After Width: | Height: | Size: 618 B |
2
src/assets/icons/sort-up.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="sort-amount-up" class="svg-inline--fa fa-sort-amount-up fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M304 416h-64a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h48v304a16 16 0 0 0 16 16h32a16 16 0 0 0 16-16V160h48c14.21 0 21.38-17.24 11.31-27.31l-80-96a16 16 0 0 0-22.62 0l-80 96C-5.35 142.74 1.77 160 16 160zm416 0H240a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h192a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm-64 128H240a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM496 32H240a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h256a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16z"></path></svg>
|
||||
|
After Width: | Height: | Size: 784 B |
1
src/assets/icons/thumbs-down.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="thumbs-down" class="svg-inline--fa fa-thumbs-down fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M0 56v240c0 13.255 10.745 24 24 24h80c13.255 0 24-10.745 24-24V56c0-13.255-10.745-24-24-24H24C10.745 32 0 42.745 0 56zm40 200c0-13.255 10.745-24 24-24s24 10.745 24 24-10.745 24-24 24-24-10.745-24-24zm272 256c-20.183 0-29.485-39.293-33.931-57.795-5.206-21.666-10.589-44.07-25.393-58.902-32.469-32.524-49.503-73.967-89.117-113.111a11.98 11.98 0 0 1-3.558-8.521V59.901c0-6.541 5.243-11.878 11.783-11.998 15.831-.29 36.694-9.079 52.651-16.178C256.189 17.598 295.709.017 343.995 0h2.844c42.777 0 93.363.413 113.774 29.737 8.392 12.057 10.446 27.034 6.148 44.632 16.312 17.053 25.063 48.863 16.382 74.757 17.544 23.432 19.143 56.132 9.308 79.469l.11.11c11.893 11.949 19.523 31.259 19.439 49.197-.156 30.352-26.157 58.098-59.553 58.098H350.723C358.03 364.34 384 388.132 384 430.548 384 504 336 512 312 512z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1 KiB |
|
|
@ -1,3 +0,0 @@
|
|||
<svg fill="currentColor" viewBox="0 0 1024 500">
|
||||
<path d="M152.1 236.2c-3.5-12.1-7.8-33.2-7.8-33.2h-.5s-4.3 21.1-7.8 33.2l-11.1 37.5H163zM616 96H336v320h280c13.3 0 24-10.7 24-24V120c0-13.3-10.7-24-24-24zm-24 120c0 6.6-5.4 12-12 12h-11.4c-6.9 23.6-21.7 47.4-42.7 69.9 8.4 6.4 17.1 12.5 26.1 18 5.5 3.4 7.3 10.5 4.1 16.2l-7.9 13.9c-3.4 5.9-10.9 7.8-16.7 4.3-12.6-7.8-24.5-16.1-35.4-24.9-10.9 8.7-22.7 17.1-35.4 24.9-5.8 3.5-13.3 1.6-16.7-4.3l-7.9-13.9c-3.2-5.6-1.4-12.8 4.2-16.2 9.3-5.7 18-11.7 26.1-18-7.9-8.4-14.9-17-21-25.7-4-5.7-2.2-13.6 3.7-17.1l6.5-3.9 7.3-4.3c5.4-3.2 12.4-1.7 16 3.4 5 7 10.8 14 17.4 20.9 13.5-14.2 23.8-28.9 30-43.2H412c-6.6 0-12-5.4-12-12v-16c0-6.6 5.4-12 12-12h64v-16c0-6.6 5.4-12 12-12h16c6.6 0 12 5.4 12 12v16h64c6.6 0 12 5.4 12 12zM0 120v272c0 13.3 10.7 24 24 24h280V96H24c-13.3 0-24 10.7-24 24zm58.9 216.1L116.4 167c1.7-4.9 6.2-8.1 11.4-8.1h32.5c5.1 0 9.7 3.3 11.4 8.1l57.5 169.1c2.6 7.8-3.1 15.9-11.4 15.9h-22.9a12 12 0 0 1-11.5-8.6l-9.4-31.9h-60.2l-9.1 31.8c-1.5 5.1-6.2 8.7-11.5 8.7H70.3c-8.2 0-14-8.1-11.4-15.9z"></path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1 KiB |