Compare commits
No commits in common. "master" and "v0.3.3" have entirely different histories.
|
|
@ -1,16 +0,0 @@
|
|||
node_modules
|
||||
npm-debug.log
|
||||
Dockerfile*
|
||||
docker-compose*
|
||||
.dockerignore
|
||||
.git
|
||||
.github
|
||||
.gitignore
|
||||
README.md
|
||||
LICENSE
|
||||
.vscode
|
||||
dist
|
||||
dist_electron
|
||||
build
|
||||
images
|
||||
script
|
||||
10
.env.example
|
|
@ -1,7 +1,5 @@
|
|||
VUE_APP_NETEASE_API_URL=/api
|
||||
VUE_APP_NETEASE_API_URL=http://127.0.0.1:3000
|
||||
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
|
||||
DEV_SERVER_PORT=20201
|
||||
|
||||
VUE_APP_ELECTRON_API_URL_DEV=http://127.0.0.1:3000
|
||||
VUE_APP_ENABLE_SENTRY=false
|
||||
DEV_SERVER_PORT=20201
|
||||
4
.envrc
|
|
@ -1,4 +0,0 @@
|
|||
source_url "https://raw.githubusercontent.com/cachix/devenv/82c0147677e510b247d8b9165c54f73d32dfd899/direnvrc" "sha256-7u4iDd1nZpxL4tCzmPG0dQgC5V+/44Ba+tHkPob1v2k="
|
||||
|
||||
export NIXPKGS_ALLOW_INSECURE=1
|
||||
use devenv
|
||||
7
.gitattributes
vendored
|
|
@ -1,7 +0,0 @@
|
|||
* 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,15 +1,6 @@
|
|||
name: Release
|
||||
name: Build/release
|
||||
|
||||
env:
|
||||
YARN_INSTALL_NOPT: yarn add --ignore-platform --ignore-optional
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- v*
|
||||
workflow_dispatch:
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
release:
|
||||
|
|
@ -17,75 +8,19 @@ jobs:
|
|||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest, ubuntu-22.04]
|
||||
os: [macos-latest, windows-latest]
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: "recursive"
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js, NPM and Yarn
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
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
|
||||
node-version: 12.16.3
|
||||
|
||||
- name: Build/release Electron app
|
||||
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
|
||||
uses: samuelmeuli/action-electron-builder@v1
|
||||
with:
|
||||
# GitHub token, automatically provided to the action
|
||||
# (No need to define this secret in the repo settings)
|
||||
|
|
@ -97,20 +32,14 @@ jobs:
|
|||
|
||||
use_vue_cli: true
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: YesPlayMusic-mac
|
||||
path: dist_electron/*-universal.dmg
|
||||
path: dist_electron/*.dmg
|
||||
if-no-files-found: ignore
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: YesPlayMusic-win
|
||||
path: dist_electron/*Setup*.exe
|
||||
if-no-files-found: ignore
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: YesPlayMusic-linux
|
||||
path: dist_electron/*.AppImage
|
||||
path: dist_electron/*.exe
|
||||
if-no-files-found: ignore
|
||||
|
|
|
|||
48
.github/workflows/sync.yml
vendored
|
|
@ -1,48 +0,0 @@
|
|||
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,6 +1,8 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
dist_electron
|
||||
Icon?
|
||||
|
||||
# local env files
|
||||
.env
|
||||
|
|
@ -23,21 +25,9 @@ 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
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# 如果发现 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
|
|
@ -1 +0,0 @@
|
|||
14
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
build
|
||||
coverage
|
||||
dist
|
||||
netease_api
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@
|
|||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"singleQuote": false,
|
||||
"jsxSingleQuote": true,
|
||||
"arrowParens": "avoid",
|
||||
"jsxBracketSameLine": false,
|
||||
"arrowParens": "always",
|
||||
"endOfLine": "lf",
|
||||
"bracketSpacing": true,
|
||||
"htmlWhitespaceSensitivity": "strict"
|
||||
|
|
|
|||
25
Dockerfile
|
|
@ -1,25 +0,0 @@
|
|||
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
|
||||
4
LICENSE
|
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020-2023 qier222
|
||||
Copyright (c) 2020 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
|
||||
|
|
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
SOFTWARE.
|
||||
204
README.md
|
|
@ -8,221 +8,69 @@
|
|||
<p align="center">
|
||||
高颜值的第三方网易云播放器
|
||||
<br />
|
||||
<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>
|
||||
<a href="https://music.qier222.com" target="blank"><strong>🌎 访问DEMO</strong></a> |
|
||||
<a href="#%EF%B8%8F-安装" target="blank"><strong>📦️ 下载安装包</strong></a>
|
||||
<br />
|
||||
<br />
|
||||
</p>
|
||||
</p>
|
||||
|
||||
[![Library][library-screenshot]](https://music.ineko.cc)
|
||||
|
||||
|
||||
## 全新版本
|
||||
全新2.0 Alpha测试版已发布,欢迎前往 [Releases](https://github.com/qier222/YesPlayMusic/releases) 页面下载。
|
||||
当前版本将会进入维护模式,除重大bug修复外,不会再更新新功能。
|
||||
[![Library][library-screenshot]](https://music.qier222.com)
|
||||
|
||||
## ✨ 特性
|
||||
|
||||
- ✅ 使用 Vue.js 全家桶开发
|
||||
- 🔴 网易云账号登录(扫码/手机/邮箱登录)
|
||||
- 📺 支持 MV 播放
|
||||
- 🔴 网易云账号登录
|
||||
- 📺 MV 播放
|
||||
- 📃 支持歌词显示
|
||||
- 📻 支持私人 FM / 每日推荐歌曲
|
||||
- 🚫🤝 无任何社交功能
|
||||
- 🌎️ 海外用户可直接播放(需要登录网易云账号)
|
||||
- 🔐 支持 [UnblockNeteaseMusic](https://github.com/UnblockNeteaseMusic/server#音源清单),自动使用[各类音源](https://github.com/UnblockNeteaseMusic/server#音源清单)替换变灰歌曲链接 (网页版不支持)
|
||||
- 「各类音源」指默认启用的音源。
|
||||
- YouTube 音源需自行安装 `yt-dlp`。
|
||||
- 🔐 支持 [UnblockNeteaseMusic](https://github.com/nondanee/UnblockNeteaseMusic),自动使用 QQ/酷狗/酷我音源替换变灰歌曲链接 (网页版不支持)
|
||||
- ⏭️ 支持 MediaSession API,可以使用系统快捷键操作上一首下一首
|
||||
- ✔️ 每日自动签到(手机端和电脑端同时签到)
|
||||
- 🌚 Light/Dark Mode 自动切换
|
||||
- 👆 支持 Touch Bar
|
||||
- 🖥️ 支持 PWA,可在 Chrome/Edge 里点击地址栏右边的 ➕ 安装到电脑
|
||||
- 🟥 支持 Last.fm Scrobble
|
||||
- ☁️ 支持音乐云盘
|
||||
- ⌨️ 自定义快捷键和全局快捷键
|
||||
- 🎧 支持 Mpris
|
||||
- 🙉 支持显示歌曲和专辑的 Explicit 标志
|
||||
- 🛠 更多特性开发中
|
||||
|
||||
## 📦️ 安装
|
||||
|
||||
Electron 版本由 [@hawtim](https://github.com/hawtim) 和 [@qier222](https://github.com/qier222) 适配并维护,支持 macOS、Windows、Linux。
|
||||
|
||||
访问本项目的 [Releases](https://github.com/qier222/YesPlayMusic/releases)
|
||||
页面下载安装包。
|
||||
访问本项目的 [Releases](https://github.com/qier222/YesPlayMusic/releases) 页面下载安装包,或者访问 [镜像下载站 (大陆访问更快)](https://dl.qier222.com/YesPlayMusic/) 下载。
|
||||
|
||||
- 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 --recursive https://github.com/qier222/YesPlayMusic.git
|
||||
git clone https://github.com/qier222/YesPlayMusic.git
|
||||
```
|
||||
|
||||
3. 安装依赖
|
||||
|
||||
```sh
|
||||
yarn install
|
||||
|
||||
```
|
||||
|
||||
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`。
|
||||
4. 复制 `/.env.example` 文件为 `/.env`,修改里面 `VUE_APP_NETEASE_API_URL` 的值为网易云 API 地址。本地开发的话可以填写 API 地址为 `http://localhost:3000`,YesPlayMusic 地址为 `http://localhost:8080`
|
||||
|
||||
```
|
||||
VUE_APP_NETEASE_API_URL=http://localhost:3000
|
||||
```
|
||||
|
||||
6. 编译打包
|
||||
5. 编译打包
|
||||
|
||||
```sh
|
||||
yarn run build
|
||||
```
|
||||
|
||||
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
|
||||
```
|
||||
6. 将 `/dist` 目录下的文件上传到你的 Web 服务器
|
||||
|
||||
## ☑️ Todo
|
||||
|
||||
|
|
@ -238,8 +86,6 @@ yarn netease_api:run
|
|||
|
||||
## 灵感来源
|
||||
|
||||
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)
|
||||
|
|
@ -247,14 +93,12 @@ API 源代码来自 [Binaryify/NeteaseCloudMusicApi](https://github.com/Binaryif
|
|||
|
||||
## 🖼️ 截图
|
||||
|
||||
![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]
|
||||
[![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)
|
||||
|
||||
<!-- MARKDOWN LINKS & IMAGES -->
|
||||
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
|
||||
|
|
@ -263,8 +107,6 @@ API 源代码来自 [Binaryify/NeteaseCloudMusicApi](https://github.com/Binaryif
|
|||
[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
|
||||
[library-dark-screenshot]: images/library-dark.png
|
||||
[playlist-screenshot]: images/playlist.png
|
||||
[search-screenshot]: images/search.png
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
[
|
||||
'@vue/cli-plugin-babel/preset',
|
||||
{
|
||||
useBuiltIns: 'usage',
|
||||
shippedProposals: true,
|
||||
},
|
||||
],
|
||||
presets: ["@vue/cli-plugin-babel/preset"],
|
||||
plugins: [
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
],
|
||||
};
|
||||
|
|
|
|||
132
devenv.lock
|
|
@ -1,132 +0,0 @@
|
|||
{
|
||||
"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
|
|
@ -1,53 +0,0 @@
|
|||
{ 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
|
|
@ -1,19 +0,0 @@
|
|||
# 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
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
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
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
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: 253 KiB After Width: | Height: | Size: 804 KiB |
|
Before Width: | Height: | Size: 228 KiB After Width: | Height: | Size: 730 KiB |
|
Before Width: | Height: | Size: 354 KiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 312 KiB |
BIN
images/home.png
|
Before Width: | Height: | Size: 389 KiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 335 KiB |
|
Before Width: | Height: | Size: 324 KiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 339 KiB |
BIN
images/playlist.png
Normal file
|
After Width: | Height: | Size: 1 MiB |
|
Before Width: | Height: | Size: 276 KiB After Width: | Height: | Size: 1 MiB |
|
|
@ -1,28 +0,0 @@
|
|||
#!/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,8 +7,7 @@
|
|||
},
|
||||
"target": "ES6",
|
||||
"module": "commonjs",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"jsx": "preserve"
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
|
|
|
|||
83
package.json
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"name": "yesplaymusic",
|
||||
"version": "0.4.9",
|
||||
"name": "YesPlayMusic",
|
||||
"version": "0.3.3",
|
||||
"private": true,
|
||||
"description": "A third party music player for Netease Music",
|
||||
"author": "qier222<qier222@outlook.com>",
|
||||
"description": "A third party music application for Netease Music",
|
||||
"author": "hawtim<hawtimzhang@gmail.com>",
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
|
|
@ -18,68 +18,57 @@
|
|||
"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",
|
||||
"netease_api:run": "npx NeteaseCloudMusicApi"
|
||||
"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"
|
||||
},
|
||||
"main": "background.js",
|
||||
"engines": {
|
||||
"node": "14 || 16"
|
||||
},
|
||||
"dependencies": {
|
||||
"@unblockneteasemusic/rust-napi": "^0.4.0",
|
||||
"NeteaseCloudMusicApi": "^4.23.3",
|
||||
"axios": "^0.26.1",
|
||||
"@njzy/unblockneteasemusic": "^0.25.3",
|
||||
"axios": "^0.21.0",
|
||||
"big-integer": "^1.6.48",
|
||||
"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",
|
||||
"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": "11.0.2",
|
||||
"electron-context-menu": "^2.3.0",
|
||||
"electron-debug": "^3.1.0",
|
||||
"electron-devtools-installer": "^3.2",
|
||||
"electron-icon-builder": "^2.0.1",
|
||||
"electron-is-dev": "^2.0.0",
|
||||
"electron-devtools-installer": "^3.1.1",
|
||||
"electron-icon-builder": "^1.0.2",
|
||||
"electron-is-dev": "^1.2.0",
|
||||
"electron-log": "^4.3.0",
|
||||
"electron-store": "^8.0.1",
|
||||
"electron-updater": "^5.0.1",
|
||||
"esbuild": "^0.20.1",
|
||||
"esbuild-loader": "^4.0.3",
|
||||
"electron-store": "^6.0.1",
|
||||
"electron-updater": "^4.3.5",
|
||||
"express": "^4.17.1",
|
||||
"express-fileupload": "^1.2.0",
|
||||
"express-http-proxy": "^1.6.2",
|
||||
"extract-zip": "^2.0.1",
|
||||
"howler": "^2.2.3",
|
||||
"howler": "^2.2.0",
|
||||
"js-cookie": "^2.2.1",
|
||||
"jsbi": "^4.1.0",
|
||||
"localforage": "^1.9.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",
|
||||
"qrcode": "^1.4.4",
|
||||
"prettier": "2.1.2",
|
||||
"register-service-worker": "^1.7.1",
|
||||
"svg-sprite-loader": "^6.0.11",
|
||||
"svg-sprite-loader": "^5.0.0",
|
||||
"tunnel": "^0.0.6",
|
||||
"vscode-codicons": "^0.0.17",
|
||||
"vue": "^2.6.11",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
"vue-gtag": "1",
|
||||
"vue-analytics": "^5.22.1",
|
||||
"vue-electron": "^1.0.6",
|
||||
"vue-i18n": "^8.22.0",
|
||||
"vue-router": "^3.4.3",
|
||||
"vue-slider-component": "^3.2.5",
|
||||
"vuex": "^3.4.0",
|
||||
"x11": "^2.3.0"
|
||||
"vuex": "^3.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^17.0.0",
|
||||
"@sentry/browser": "^5.27.0",
|
||||
"@sentry/integrations": "^5.27.0",
|
||||
"@sentry/tracing": "^5.27.0",
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-plugin-pwa": "~4.5.0",
|
||||
|
|
@ -87,21 +76,13 @@
|
|||
"@vue/cli-service": "~4.5.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-config-prettier": "^8.1.0",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
"eslint-plugin-vue": "^7.9.0",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"husky": "^4.3.0",
|
||||
"prettier": "2.5.1",
|
||||
"sass": "^1.26.11",
|
||||
"sass-loader": "^10.0.2",
|
||||
"vue-cli-plugin-electron-builder": "~2.1.1",
|
||||
"vue-cli-plugin-electron-builder": "~2.0.0-rc.4",
|
||||
"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": {
|
||||
|
|
@ -110,8 +91,6 @@
|
|||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"plugin:vue/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 223 B |
|
Before Width: | Height: | Size: 191 B |
|
Before Width: | Height: | Size: 308 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 953 B |
|
Before Width: | Height: | Size: 396 B |
|
Before Width: | Height: | Size: 344 B |
|
Before Width: | Height: | Size: 218 B |
|
Before Width: | Height: | Size: 932 B |
|
Before Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 816 B |
|
Before Width: | Height: | Size: 750 B |
|
Before Width: | Height: | Size: 801 B |
|
Before Width: | Height: | Size: 779 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 871 B |
|
Before Width: | Height: | Size: 855 B |
|
Before Width: | Height: | Size: 656 B |
|
Before Width: | Height: | Size: 610 B |
|
Before Width: | Height: | Size: 499 B |
|
Before Width: | Height: | Size: 746 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
|
@ -1,25 +1,23 @@
|
|||
<!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>
|
||||
|
||||
<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
|
||||
<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:
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
commit_template: 'style: with ${restyler.name}'
|
||||
restylers:
|
||||
- prettier
|
||||
- prettier-json
|
||||
- prettier-markdown
|
||||
- prettier-yaml
|
||||
- whitespace
|
||||
103
script/pull.js
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
// 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,43 +1,39 @@
|
|||
<template>
|
||||
<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"
|
||||
>
|
||||
<div id="app">
|
||||
<Navbar ref="navbar" />
|
||||
<main v-show="!this.$store.state.showLyrics">
|
||||
<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="enablePlayer" v-show="showPlayer" ref="player" />
|
||||
</transition>
|
||||
<Player
|
||||
v-if="this.$store.state.player.enabled"
|
||||
ref="player"
|
||||
v-show="showPlayer"
|
||||
/></transition>
|
||||
<Toast />
|
||||
<ModalAddTrackToPlaylist v-if="isAccountLoggedIn" />
|
||||
<ModalNewPlaylist v-if="isAccountLoggedIn" />
|
||||
<transition v-if="enablePlayer" name="slide-up">
|
||||
<Lyrics v-show="showLyrics" />
|
||||
<transition name="slide-up" v-if="this.$store.state.player.enabled">
|
||||
<Lyrics v-show="this.$store.state.showLyrics" />
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
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';
|
||||
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";
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
name: "App",
|
||||
components: {
|
||||
Navbar,
|
||||
Player,
|
||||
|
|
@ -45,104 +41,163 @@ 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',
|
||||
'lastfmCallback',
|
||||
].includes(this.$route.name) === false
|
||||
["mv", "loginUsername", "login", "loginAccount"].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);
|
||||
this.fetchData();
|
||||
if (this.isElectron) {
|
||||
ipcRenderer(this);
|
||||
}
|
||||
window.addEventListener("keydown", this.handleKeydown);
|
||||
},
|
||||
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.player.playOrPause();
|
||||
this.$refs.player.play();
|
||||
}
|
||||
},
|
||||
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 {
|
||||
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
|
||||
margin-top: 84px;
|
||||
margin-bottom: 96px;
|
||||
padding: {
|
||||
right: 10vw;
|
||||
left: 10vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1336px) {
|
||||
main {
|
||||
padding: 64px 5vw 96px 5vw;
|
||||
padding: 0 5vw;
|
||||
}
|
||||
}
|
||||
|
||||
main::-webkit-scrollbar {
|
||||
width: 0px;
|
||||
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);
|
||||
}
|
||||
|
||||
.slide-up-enter-active,
|
||||
.slide-up-leave-active {
|
||||
transition: transform 0.4s;
|
||||
transition: all 0.4s;
|
||||
}
|
||||
.slide-up-enter,
|
||||
.slide-up-leave-to {
|
||||
.slide-up-enter, .slide-up-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
[data-electron="yes"] {
|
||||
button,
|
||||
.navigation-links a,
|
||||
.playlist-info .description {
|
||||
cursor: default !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import request from '@/utils/request';
|
||||
import { mapTrackPlayableStatus } from '@/utils/common';
|
||||
import { cacheAlbum, getAlbumFromCache } from '@/utils/db';
|
||||
import request from "@/utils/request";
|
||||
import { mapTrackPlayableStatus } from "@/utils/common";
|
||||
|
||||
/**
|
||||
* 获取专辑内容
|
||||
|
|
@ -8,23 +7,15 @@ import { cacheAlbum, getAlbumFromCache } from '@/utils/db';
|
|||
* @param {number} id
|
||||
*/
|
||||
export function getAlbum(id) {
|
||||
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();
|
||||
return request({
|
||||
url: "/album",
|
||||
method: "get",
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
}).then((data) => {
|
||||
data.songs = mapTrackPlayableStatus(data.songs);
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -41,8 +32,8 @@ export function getAlbum(id) {
|
|||
*/
|
||||
export function newAlbums(params) {
|
||||
return request({
|
||||
url: '/album/new',
|
||||
method: 'get',
|
||||
url: "/album/new",
|
||||
method: "get",
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -55,8 +46,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() },
|
||||
});
|
||||
}
|
||||
|
|
@ -72,8 +63,8 @@ export function albumDynamicDetail(id) {
|
|||
*/
|
||||
export function likeAAlbum(params) {
|
||||
return request({
|
||||
url: '/album/sub',
|
||||
method: 'post',
|
||||
url: "/album/sub",
|
||||
method: "post",
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import request from '@/utils/request';
|
||||
import { mapTrackPlayableStatus } from '@/utils/common';
|
||||
import { isAccountLoggedIn } from '@/utils/auth';
|
||||
import { getTrackDetail } from '@/api/track';
|
||||
import request from "@/utils/request";
|
||||
import { mapTrackPlayableStatus } from "@/utils/common";
|
||||
|
||||
/**
|
||||
* 获取歌手单曲
|
||||
|
|
@ -10,19 +8,13 @@ import { getTrackDetail } from '@/api/track';
|
|||
*/
|
||||
export function getArtist(id) {
|
||||
return request({
|
||||
url: '/artists',
|
||||
method: 'get',
|
||||
url: "/artists",
|
||||
method: "get",
|
||||
params: {
|
||||
id,
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
}).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;
|
||||
}
|
||||
}).then((data) => {
|
||||
data.hotSongs = mapTrackPlayableStatus(data.hotSongs);
|
||||
return data;
|
||||
});
|
||||
|
|
@ -41,8 +33,8 @@ export function getArtist(id) {
|
|||
*/
|
||||
export function getArtistAlbum(params) {
|
||||
return request({
|
||||
url: '/artist/album',
|
||||
method: 'get',
|
||||
url: "/artist/album",
|
||||
method: "get",
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -58,14 +50,12 @@ 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,
|
||||
url: "/toplist/artist",
|
||||
method: "get",
|
||||
params: {
|
||||
type,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
|
|
@ -77,8 +67,8 @@ export function toplistOfArtists(type = null) {
|
|||
*/
|
||||
export function artistMv(params) {
|
||||
return request({
|
||||
url: '/artist/mv',
|
||||
method: 'get',
|
||||
url: "/artist/mv",
|
||||
method: "get",
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -94,8 +84,8 @@ export function artistMv(params) {
|
|||
*/
|
||||
export function followAArtist(params) {
|
||||
return request({
|
||||
url: '/artist/sub',
|
||||
method: 'post',
|
||||
url: "/artist/sub",
|
||||
method: "post",
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -108,8 +98,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,12 +14,11 @@ import request from '@/utils/request';
|
|||
*/
|
||||
export function loginWithPhone(params) {
|
||||
return request({
|
||||
url: '/login/cellphone',
|
||||
method: 'post',
|
||||
url: "/login/cellphone",
|
||||
method: "post",
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 邮箱登录
|
||||
* - email: 163 网易邮箱
|
||||
|
|
@ -32,60 +31,12 @@ 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(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新登录
|
||||
* 说明 : 调用此接口 , 可刷新登录状态
|
||||
|
|
@ -93,8 +44,8 @@ export function loginQrCodeCheck(key) {
|
|||
*/
|
||||
export function refreshCookie() {
|
||||
return request({
|
||||
url: '/login/refresh',
|
||||
method: 'post',
|
||||
url: "/login/refresh",
|
||||
method: "post",
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +55,7 @@ export function refreshCookie() {
|
|||
*/
|
||||
export function logout() {
|
||||
return request({
|
||||
url: '/logout',
|
||||
method: 'post',
|
||||
url: "/logout",
|
||||
method: "post",
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
// 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,33 +18,12 @@ 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,12 +24,9 @@ export function recommendPlaylist(params) {
|
|||
*/
|
||||
export function dailyRecommendPlaylist(params) {
|
||||
return request({
|
||||
url: '/recommend/resource',
|
||||
method: 'get',
|
||||
params: {
|
||||
params,
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
url: "/recommend/resource",
|
||||
method: "get",
|
||||
params,
|
||||
});
|
||||
}
|
||||
/**
|
||||
|
|
@ -46,16 +43,14 @@ 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 => {
|
||||
if (data.playlist) {
|
||||
data.playlist.tracks = mapTrackPlayableStatus(
|
||||
data.playlist.tracks,
|
||||
data.privileges || []
|
||||
);
|
||||
}
|
||||
}).then((data) => {
|
||||
data.playlist.tracks = mapTrackPlayableStatus(
|
||||
data.playlist.tracks,
|
||||
data.privileges || []
|
||||
);
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
|
@ -72,8 +67,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,
|
||||
});
|
||||
}
|
||||
|
|
@ -91,8 +86,8 @@ export function highQualityPlaylist(params) {
|
|||
*/
|
||||
export function topPlaylist(params) {
|
||||
return request({
|
||||
url: '/top/playlist',
|
||||
method: 'get',
|
||||
url: "/top/playlist",
|
||||
method: "get",
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -103,8 +98,8 @@ export function topPlaylist(params) {
|
|||
*/
|
||||
export function playlistCatlist() {
|
||||
return request({
|
||||
url: '/playlist/catlist',
|
||||
method: 'get',
|
||||
url: "/playlist/catlist",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -114,8 +109,8 @@ export function playlistCatlist() {
|
|||
*/
|
||||
export function toplists() {
|
||||
return request({
|
||||
url: '/toplist',
|
||||
method: 'get',
|
||||
url: "/toplist",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -131,8 +126,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,
|
||||
});
|
||||
}
|
||||
|
|
@ -145,8 +140,8 @@ export function subscribePlaylist(params) {
|
|||
*/
|
||||
export function deletePlaylist(id) {
|
||||
return request({
|
||||
url: '/playlist/delete',
|
||||
method: 'post',
|
||||
url: "/playlist/delete",
|
||||
method: "post",
|
||||
params: { id },
|
||||
});
|
||||
}
|
||||
|
|
@ -165,8 +160,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,
|
||||
});
|
||||
}
|
||||
|
|
@ -183,47 +178,8 @@ export function createPlaylist(params) {
|
|||
export function addOrRemoveTrackFromPlaylist(params) {
|
||||
params.timestamp = new Date().getTime();
|
||||
return request({
|
||||
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',
|
||||
url: "/playlist/tracks",
|
||||
method: "post",
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
106
src/api/track.js
|
|
@ -1,13 +1,6 @@
|
|||
import store from '@/store';
|
||||
import request from '@/utils/request';
|
||||
import { mapTrackPlayableStatus } from '@/utils/common';
|
||||
import {
|
||||
cacheTrackDetail,
|
||||
getTrackDetailFromCache,
|
||||
cacheLyric,
|
||||
getLyricFromCache,
|
||||
} from '@/utils/db';
|
||||
|
||||
import store from "@/store";
|
||||
import request from "@/utils/request";
|
||||
import { mapTrackPlayableStatus } from "@/utils/common";
|
||||
/**
|
||||
* 获取音乐 url
|
||||
* 说明 : 使用歌单详情接口后 , 能得到的音乐的 id, 但不能得到的音乐 url, 调用此接口, 传入的音乐 id( 可多个 , 用逗号隔开 ), 可以获取对应的音乐的 url,
|
||||
|
|
@ -15,85 +8,51 @@ import {
|
|||
* @param {string} id - 音乐的 id,例如 id=405998841,33894312
|
||||
*/
|
||||
export function getMP3(id) {
|
||||
const getBr = () => {
|
||||
// 当返回的 quality >= 400000时,就会优先返回 hi-res
|
||||
const quality = store.state.settings?.musicQuality ?? '320000';
|
||||
return quality === 'flac' ? '350000' : quality;
|
||||
};
|
||||
|
||||
let br =
|
||||
store.state.settings?.musicQuality !== undefined
|
||||
? store.state.settings.musicQuality
|
||||
: 320000;
|
||||
return request({
|
||||
url: '/song/url',
|
||||
method: 'get',
|
||||
url: "/song/url",
|
||||
method: "get",
|
||||
params: {
|
||||
id,
|
||||
br: getBr(),
|
||||
br,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取歌曲详情
|
||||
* 说明 : 调用此接口 , 传入音乐 id(支持多个 id, 用 , 隔开), 可获得歌曲详情(注意:歌曲封面现在需要通过专辑内容接口获取)
|
||||
* @param {string} ids - 音乐 id, 例如 ids=405998841,33894312
|
||||
*/
|
||||
export function getTrackDetail(ids) {
|
||||
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();
|
||||
return request({
|
||||
url: "/song/detail",
|
||||
method: "get",
|
||||
params: {
|
||||
ids,
|
||||
},
|
||||
}).then((data) => {
|
||||
data.songs = mapTrackPlayableStatus(data.songs, data.privileges);
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取歌词
|
||||
* 说明 : 调用此接口 , 传入音乐 id 可获得对应音乐的歌词 ( 不需要登录 )
|
||||
* @param {number} id - 音乐 id
|
||||
*/
|
||||
|
||||
export function getLyric(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();
|
||||
return request({
|
||||
url: "/lyric",
|
||||
method: "get",
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 新歌速递
|
||||
* 说明 : 调用此接口 , 可获取新歌速递
|
||||
|
|
@ -101,14 +60,13 @@ export function getLyric(id) {
|
|||
*/
|
||||
export function topSong(type) {
|
||||
return request({
|
||||
url: '/top/song',
|
||||
method: 'get',
|
||||
url: "/top/song",
|
||||
method: "get",
|
||||
params: {
|
||||
type,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 喜欢音乐
|
||||
* 说明 : 调用此接口 , 传入音乐 id, 可喜欢该音乐
|
||||
|
|
@ -121,8 +79,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,
|
||||
});
|
||||
}
|
||||
|
|
@ -141,8 +99,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,25 +8,10 @@ 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(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -44,25 +29,8 @@ export function userAccount() {
|
|||
*/
|
||||
export function userPlaylist(params) {
|
||||
return request({
|
||||
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',
|
||||
url: "/user/playlist",
|
||||
method: "get",
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -75,8 +43,8 @@ export function userPlayHistory(params) {
|
|||
*/
|
||||
export function userLikedSongsIDs(uid) {
|
||||
return request({
|
||||
url: '/likelist',
|
||||
method: 'get',
|
||||
url: "/likelist",
|
||||
method: "get",
|
||||
params: {
|
||||
uid,
|
||||
timestamp: new Date().getTime(),
|
||||
|
|
@ -92,8 +60,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(),
|
||||
|
|
@ -104,18 +72,17 @@ export function dailySignin(type = 0) {
|
|||
/**
|
||||
* 获取收藏的专辑(需要登录)
|
||||
* 说明 : 调用此接口可获取到用户收藏的专辑
|
||||
* - limit : 返回数量 , 默认为 25
|
||||
* - offset : 偏移数量,用于分页 , 如 :( 页数 -1)*25, 其中 25 为 limit 的值 , 默认为 0
|
||||
* - limit : 返回数量 , 默认为 30
|
||||
* - offset : 偏移数量,用于分页 , 如 :( 页数 -1)*30, 其中 30 为 limit 的值 , 默认为 0
|
||||
* @param {Object} params
|
||||
* @param {number} params.limit
|
||||
* @param {number=} params.offset
|
||||
*/
|
||||
export function likedAlbums(params) {
|
||||
export function likedAlbums() {
|
||||
return request({
|
||||
url: '/album/sublist',
|
||||
method: 'get',
|
||||
url: "/album/sublist",
|
||||
method: "get",
|
||||
params: {
|
||||
limit: params.limit,
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
});
|
||||
|
|
@ -125,12 +92,11 @@ export function likedAlbums(params) {
|
|||
* 获取收藏的歌手(需要登录)
|
||||
* 说明 : 调用此接口可获取到用户收藏的歌手
|
||||
*/
|
||||
export function likedArtists(params) {
|
||||
export function likedArtists() {
|
||||
return request({
|
||||
url: '/artist/sublist',
|
||||
method: 'get',
|
||||
url: "/artist/sublist",
|
||||
method: "get",
|
||||
params: {
|
||||
limit: params.limit,
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
});
|
||||
|
|
@ -140,82 +106,12 @@ export function likedArtists(params) {
|
|||
* 获取收藏的MV(需要登录)
|
||||
* 说明 : 调用此接口可获取到用户收藏的MV
|
||||
*/
|
||||
export function likedMVs(params) {
|
||||
export function likedMVs() {
|
||||
return request({
|
||||
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',
|
||||
url: "/mv/sublist",
|
||||
method: "get",
|
||||
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,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,135 +0,0 @@
|
|||
@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,11 +100,6 @@
|
|||
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);
|
||||
|
|
@ -126,19 +121,10 @@
|
|||
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 {
|
||||
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 {
|
||||
body[data-theme="dark"] .lyrics-page .vue-slider-dot-handle {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
<?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>
|
||||
|
Before Width: | Height: | Size: 666 B |
|
|
@ -1 +0,0 @@
|
|||
<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>
|
||||
|
Before Width: | Height: | Size: 487 B |
|
|
@ -1 +0,0 @@
|
|||
<?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>
|
||||
|
Before Width: | Height: | Size: 668 B |
1
src/assets/icons/circle.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<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>
|
||||
|
After Width: | Height: | Size: 301 B |
|
|
@ -1 +0,0 @@
|
|||
<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>
|
||||
|
Before Width: | Height: | Size: 359 B |
1
src/assets/icons/expand.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<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>
|
||||
|
After Width: | Height: | Size: 749 B |
|
|
@ -1 +0,0 @@
|
|||
<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>
|
||||
|
Before Width: | Height: | Size: 733 B |
|
|
@ -1,3 +0,0 @@
|
|||
<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>
|
||||
|
Before Width: | Height: | Size: 396 B |
|
|
@ -1,3 +0,0 @@
|
|||
<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>
|
||||
|
Before Width: | Height: | Size: 396 B |
|
|
@ -1,7 +1,8 @@
|
|||
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 +0,0 @@
|
|||
<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>
|
||||
|
Before Width: | Height: | Size: 579 B |
|
|
@ -1 +0,0 @@
|
|||
<?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>
|
||||
|
Before Width: | Height: | Size: 618 B |
|
|
@ -1,2 +0,0 @@
|
|||
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 784 B |
|
|
@ -1 +0,0 @@
|
|||
<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>
|
||||
|
Before Width: | Height: | Size: 1 KiB |
3
src/assets/icons/translation.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<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>
|
||||
|
After Width: | Height: | Size: 1 KiB |