This repository has been archived on 2023-08-30. You can view files and clone it, but cannot push or open issues/pull-requests.
appcat
/
sonist
Archived
1
0
Fork 0

Compare commits

...

63 Commits

Author SHA1 Message Date
yutent 298dcd1ecb Update Readme.md 2023-08-25 15:37:06 +08:00
yutent c725ca63d8 update 2022-09-12 19:06:56 +08:00
yutent 55bdea11bb update 2022-08-29 00:43:21 +08:00
宇天 cabfa462a4 update 2020-12-25 14:20:19 +08:00
宇天 8348d50495 update 2020-12-14 19:26:26 +08:00
宇天 7a696c61b2 fixed 2020-12-13 22:08:25 +08:00
宇天 539a942906 update 2020-12-13 21:28:00 +08:00
宇天 82cc79a46f 增加tray控制按钮 2020-11-27 18:43:40 +08:00
宇天 991661d03c 更新图标, 更新tray 2020-11-27 17:48:30 +08:00
宇天 09b6b4c93c 更新图标 2020-11-27 11:30:59 +08:00
宇天 ea229c0a38 update 2020-11-23 22:39:19 +08:00
宇天 995a967312 修复播放切换,增加歌词搜索 2020-11-20 19:09:12 +08:00
宇天 bf84344f9c 优化歌曲切换 2020-11-20 18:35:03 +08:00
宇天 641199aa1f 完成播放功能 2020-11-20 18:23:53 +08:00
宇天 b21ec91170 重构音频播放 2020-11-19 20:49:53 +08:00
宇天 6bd72fcbac 优化封面 2020-11-18 19:53:07 +08:00
宇天 c8a7baf49d update 2020-11-18 19:33:09 +08:00
宇天 82b066a832 完成歌曲扫描 2020-11-18 19:24:49 +08:00
宇天 2f746df5fd 引入ID3读写模块 2020-11-18 18:32:20 +08:00
宇天 ea4bdddffa 重写进程通讯交互 2020-11-18 16:32:33 +08:00
宇天 cb66f9b287 update 2020-11-17 19:03:09 +08:00
宇天 db99d5c86b 一大波更新 2020-11-17 18:56:30 +08:00
宇天 df36541c70 大重构 2020-11-16 19:38:44 +08:00
宇天 6d5c72f235 2.x 2020-11-16 11:22:17 +08:00
宇天 039b70c4b6 update 2019-11-06 00:28:28 +08:00
宇天 faed0b59b3 Merge branch 'master' of github.com:yutent/sonist 2019-11-05 23:57:47 +08:00
宇天 19f429d163 更新组件库 2019-11-05 23:57:24 +08:00
宇天 38118ad788 一大波更新 2019-09-10 17:41:03 +08:00
宇天 dd2da3d4c7 一大波更新适配 2019-09-09 21:04:47 +08:00
宇天 691ef9f437 Merge branch 'master' of github.com:yutent/sonist 2019-09-09 20:21:52 +08:00
宇天 b72a8d4780 更新6.x的API 2019-09-09 00:56:32 +08:00
宇天 d1dcba7711 升级electron到6.0 2019-09-08 23:54:49 +08:00
宇天 a8f57fccba 更换组件库 2019-09-07 23:37:53 +08:00
宇天 a259a4693b UI更新 2019-09-02 00:05:51 +08:00
宇天 b1bcf6d14d 升级UI库 2019-09-01 23:20:34 +08:00
宇天 ba238b8aa2 update 2019-08-31 23:59:20 +08:00
宇天 8d9f8b9b26 u 2019-08-12 21:26:41 +08:00
宇天 a886037773 绑定全局快捷键 2019-02-24 02:57:47 +08:00
宇天 52cd2d9d49 大幅降低迷你模式和后台播放的cpu占用;优化交互逻辑;增加快捷键的绑定 2019-02-24 02:12:35 +08:00
宇天 57a5e14359 更新组件库, 专为electron打造,性能更优; 2019-02-21 17:24:36 +08:00
宇天 2f2593f0b5 增加快捷键说明 2019-02-05 01:10:31 +08:00
宇天 064ffa21b6 增加全局快捷键;优化CPU占用; 2019-02-05 00:01:54 +08:00
宇天 3d2f2aa725 1.0.1 2019-01-31 21:32:20 +08:00
宇天 c5dcf47601 将桌面歌词窗口放到渲染进程中创建,以解决linux下打包后的奇葩bug 2019-01-31 21:29:14 +08:00
宇天 c8c73747ad update 2019-01-31 20:09:35 +08:00
宇天 14b27fb326 fixed 2019-01-31 00:15:58 +08:00
宇天 1346ac7147 年前最后一波 v1.0.0 2019-01-30 17:29:52 +08:00
宇天 a575d7e3e5 完成在线搜索功能 2019-01-30 17:27:43 +08:00
宇天 19824ebc3e 优化ktv模式 2019-01-30 15:03:56 +08:00
宇天 13d2d5606b 优化ktv模式;优化音乐扫描; 2019-01-30 15:03:06 +08:00
宇天 26507f0867 0.9.6 2019-01-27 17:58:03 +08:00
宇天 68fe06367c 修复歌词调整导致app无响应的bug 2019-01-27 17:55:11 +08:00
宇天 47c8a1f9f3 修复迷你模式状态同步问题 2019-01-27 17:27:03 +08:00
宇天 61c1706058 0.9.5 2019-01-27 01:04:54 +08:00
宇天 d32da5945b 修复控制交互; 2019-01-27 01:02:46 +08:00
宇天 f9eddcec1c 优化桌面歌词 2019-01-27 00:32:06 +08:00
宇天 ac2daedc1b 完成迷你模式 2019-01-26 23:27:34 +08:00
宇天 22eea930c7 完成迷你模式 2019-01-26 23:26:49 +08:00
宇天 4bcc894ec3 一大波优化 2019-01-26 22:19:51 +08:00
宇天 b42d7ae19a 修复一个奇葩的DPI问题 2019-01-26 01:48:42 +08:00
宇天 53c62fd3f6 fixed svg mimetype 2019-01-24 18:00:33 +08:00
宇天 a77543a24b 因为electon的一些bug;无奈改为自建http静态服务 2019-01-23 03:45:26 +08:00
宇天 79edeb045d 优化代码结构;增加迷你模式 2019-01-21 21:42:44 +08:00
161 changed files with 2453 additions and 1869 deletions

View File

@ -2,14 +2,15 @@
> 一个音乐播放器, 主打本地音乐播放。支持 自动歌词/自动封面/均衡器等常见功能。 > 一个音乐播放器, 主打本地音乐播放。支持 自动歌词/自动封面/均衡器等常见功能。
>> 同时利用酷狗音乐的API(**来源于网络,仅供学习使用**), 获取实时的云音乐(**仅免费的那部分,付费部分无法提供**)。 >> 同时利用酷狗音乐的API(**来源于网络,仅供学习使用**), 获取实时的云音乐(**仅免费的那部分,付费部分无法提供**)。
# 此项目不再维护 (请关注Gtk版)
[Sonist-Gtk](https://github.com/app-cat/sonist-gtk
界面预览 界面预览
![demo](./demo1.jpg) ![demo](./demo1.png)
![demo](./demo2.jpg) ![demo](./demo2.png)
![demo](./demo3.jpg)
## 开发计划 & 进度 ## 开发计划 & 进度
@ -26,13 +27,14 @@
- [x] 歌曲ID3信息修改 - [x] 歌曲ID3信息修改
- [x] 酷狗歌手列表(完成20%) - [x] 酷狗歌手列表(完成20%)
- [x] 歌词编辑 - [x] 歌词编辑
- [x] 音乐在线搜索()
- [ ] 酷狗音乐排行榜 - [ ] 酷狗音乐排行榜
- [ ] 酷狗音乐MV - [ ] 酷狗音乐MV
- [ ] 试听列表 - [x] 试听列表
- [ ] 均衡器 - [ ] 均衡器
- [x] 桌面歌词 - [x] 桌面歌词
- [ ] 迷你模式 - [x] 迷你模式
- [ ] 多媒体快捷键 - [x] 多媒体快捷键
- [ ] 铃声制作(犹豫中) - [ ] 铃声制作(犹豫中)
- [ ] 等你来建议 - [ ] 等你来建议

BIN
demo1.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 KiB

BIN
demo1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

BIN
demo2.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

BIN
demo2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

BIN
demo3.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 67 KiB

BIN
icons/512x512.psd Normal file

Binary file not shown.

Binary file not shown.

BIN
icons/apps.icns Normal file

Binary file not shown.

BIN
icons/tray.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
icons/tray.psd Normal file

Binary file not shown.

View File

@ -1,32 +1,40 @@
{ {
"name": "sonist", "name": "sonist",
"version": "0.9.3", "version": "2.0.0-alpha-4",
"description": "Music Player", "description": "Music Player",
"main": "src/main.js", "main": "src/main.js",
"scripts": { "scripts": {
"start": "electron .", "start": "electron ."
"pack": "electron-builder"
}, },
"author": { "name": "yutent", "email": "yutent@doui.cc" }, "author": {
"homepage": "https://yutent.me", "name": "yutent",
"email": "yutent.io@gmail.com"
},
"homepage": "https://yutent.top",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"crypto.js": "^1.2.0", "crypto.js": "^2.0.2",
"iofs": "^1.1.0" "iofs": "^1.5.1",
}, "music-metadata": "^7.5.0"
"devDependencies": {
"electron": "^4.0.0",
"electron-builder": "^20.38.5"
}, },
"build": { "build": {
"appId": "cc.doui.sonist", "appId": "sonist",
"productName": "Sonist", "productName": "Sonist",
"copyright": "Copyright © 2019 ${author}", "copyright": "Copyright © 2019 ${author}",
"directories": { "directories": {
"buildResources": "icons", "buildResources": "icons",
"output": "build" "output": "build"
}, },
"files": ["src/**/*", "node_modules/iofs/*", "node_modules/crypto.js/*"], "electronDownload": {
"version": "10.1.5",
"mirror": "https://npm.taobao.org/mirrors/electron/"
},
"files": [
"src/**/*",
"node_modules/iofs/*",
"node_modules/crypto.js/*",
"node_modules/music-metadata/*"
],
"mac": { "mac": {
"category": "public.app-category.music", "category": "public.app-category.music",
"target": "dmg", "target": "dmg",
@ -48,15 +56,7 @@
"icon": "./icons/" "icon": "./icons/"
}, },
"deb": { "deb": {
"depends": [ "depends": []
"gconf2",
"gconf-service",
"libnotify4",
"libappindicator1",
"libxtst6",
"libnss3",
"ffmpeg"
]
} }
} }
} }

File diff suppressed because one or more lines are too long

View File

@ -5,353 +5,442 @@
* @date 2018/12/16 17:15:07 * @date 2018/12/16 17:15:07
*/ */
@import './var.scss';
@import "./var.scss"; .app {
position: relative;
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
@font-face {font-family: "sonist font"; .album-cover {
src: url('data:application/x-font-woff;charset=utf-8;base64,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') format('woff') overflow: hidden;
} position: absolute;
z-index: -1;
left: 0;
top: 0;
width: 100%;
height: 394px;
[class^="s-icon-"], [class*=" s-icon-"] {display:inline-block;font-family:"sonist font" !important;font-style:normal;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;} img {
width: 100%;
.s-icon-all:before { content: "\e714"; } height: 100%;
.s-icon-eq:before { content: "\e715"; } object-fit: cover;
.s-icon-heart:before { content: "\e716"; } filter: blur(35px) opacity(0.8);
.s-icon-music:before { content: "\e717"; } }
.s-icon-play-list:before { content: "\e719"; }
.s-icon-mv:before { content: "\e71d"; }
.s-icon-rank:before { content: "\e71e"; }
.s-icon-singer:before { content: "\e71f"; }
.s-icon-random:before { content: "\e720"; }
.s-icon-radio:before { content: "\e721"; }
.s-icon-single:before { content: "\e722"; }
.s-icon-next:before { content: "\e723"; }
.s-icon-prev:before { content: "\e724"; }
.s-icon-pause:before { content: "\e725"; }
.s-icon-play:before { content: "\e726"; }
.do-fn-drag {-webkit-app-region:drag;user-select: none;}
.do-fn-nodrag {-webkit-app-region:no-drag;}
html {font-size:62.5%}
body {position:fixed;left:0;top:0;display:flex;width:100%;height:100%;line-height:1.5;background:#fff;font-size:1.4rem;color:nth($cd, 1);background-size:cover;background-repeat:no-repeat;}
table {overflow:auto;display:table;width:100%;line-height:2.5rem;
thead tr {height:4.5rem;border-bottom:.1rem solid rgba(200, 200, 200, .15)}
thead th {padding:1rem .8rem;border:0;}
tbody tr {height:auto;@include ts(background, .3s);
&:hover {background:rgba(63, 194, 168, .08);}
} }
tbody td {padding:.9rem .8rem}
}
::-webkit-scrollbar {width:.6rem;height:.6rem;background:none;}
::-webkit-scrollbar:hover {background:rgba(255, 255, 255, .1);}
::-webkit-scrollbar-button {display:none;}
::-webkit-scrollbar-thumb {background:nth($cp, 3);}
::-webkit-scrollbar-thumb:hover {background:nth($ct, 1);}
.do-mod-app {position:relative;display:flex;flex-flow:column wrap;width:100%;height:100%;background:#fff;
// //
.title-bar {position:relative;z-index:9;display:flex;flex:0 5rem;
.btn-box {position:absolute;left:1.2rem;top:0;width:auto;height:3rem;padding:.9rem 0;
.item {display:inline-block;width:1.2rem;height:1.2rem;margin:0 .2rem;background:url(/images/btn-grey.svg) no-repeat;background-size:cover;}
&.focus {
.quit {background-image:url(/images/btn-close.svg);}
.min {background-image:url(/images/btn-mini.svg);}
// .max {background-image:url(/images/btn-maxi.svg);}
}
&:hover {
.quit {background-image:url(/images/btn-close_a.svg);}
.min {background-image:url(/images/btn-mini_a.svg);}
// .max {background-image:url(/images/btn-maxi_a.svg);}
}
}
.btn-box-win {position:absolute;right:1.2rem;top:0;width:auto;height:4rem;padding:.9rem 0;line-height:1.8rem;
.item,
.opt {position:relative;display:inline-block;width:2.2rem;height:2.2rem;margin:0 .2rem;padding:.2rem;font-size:1.6rem;
}
.item {text-align:center;
&:hover {background:rgba(0, 0, 0, .05);}
&.quit:hover {color:nth($cr, 1);}
&.disabled {color:nth($cp, 3);background:none}
}
.opt i {font-size:1.8rem;}
.opt-list {position:absolute;z-index:100;right:0;top:2.2rem;width:13rem;height:auto;padding:.8rem 0;background:#fff;box-shadow:0 .5rem 2rem rgba(0, 0, 0, .1);font-size:1.4rem;
span {display:flex;align-items:center;height:3rem;padding:0 2rem;line-height:3rem;
i {padding-right:.8rem;}
&.pipe {height:.1rem;margin:.5rem 0;border-bottom:.1rem solid nth($cp, 1)}
&:hover {background:nth($cp, 1)}
}
}
}
//
.holder {flex:0 22rem;height:100%;background:nth($cp, 1);}
.tools {flex:1;padding:1rem;
.search {position:relative;display:inline-block;line-height:3rem;}
.icon {position:absolute;right:0;top:0;width:2.6rem;height:3rem;}
input {width:20rem;padding:0 1.3rem;border-radius:1.5rem;}
}
}
//
.main-body {flex:1;display:flex;
//
.sidebar {flex:0 22rem;position:relative;height:100%;background:nth($cp, 1);
//
.user-box {width:18rem;height:16.5rem;margin:0 2rem;text-align:center;
.avatar {overflow:hidden;width:12rem;height:12rem;margin:0 3rem;border:.6rem solid #fff;border-radius:50%;box-shadow:0 .5rem 1.5rem rgba(0, 0, 0, .15);}
img {width:100%;height:100%;}
.uname {line-height:2;font-weight:normal;}
}
//
.music-box {width:100%;height:auto;padding:0 1.5rem;
dt.title {line-height:4rem;color:nth($cgr, 1)}
dd.item {height:3rem;margin:.3rem 0;padding:0 .8rem;line-height:3rem;color:nth($cgr, 3);
.icon {float:left;width:3rem;height:3rem;padding:0 .5rem;font-size:2.4rem;}
&:hover {padding-left:.9rem;color:nth($ct, 1);}
&.active {border-radius:.3rem;background:nth($ct, 1);color:#fff;}
&.disabled {opacity:.25}
}
}
}
//
.module {position:relative;flex:1;display:flex;flex-flow:column wrap;}
}
.contrl-bar {position:relative;z-index:99;display:flex;flex:0 8rem;background:nth($cp, 1);
//
.play-box {flex:0 22rem;display:flex;justify-content:center;align-items:center;height:8rem;padding:1rem 2rem;text-align:center;
.item {flex:0 5rem;margin:0 .5rem;line-height:1;font-size:4.2rem;color:nth($ct, 2);@include ts();
&:hover {color:nth($co, 1)}
&:active {color:nth($ct, 1);transform:scale(1.1)}
}
.play {font-size:5rem;}
}
//
.stat-box {position:relative;flex:1;display:flex;justify-content:center;align-items:center;
.song-stat {flex:1;height:8rem;margin:0 2rem 0 0;
canvas {display:flex;width:100%;height:100%;}
}
.ctrl {position:relative;flex:0 1 3.5rem;height:3rem;line-height:3rem;text-align:center;color:nth($ct, 2);font-size:2rem;
&:hover {color:nth($ct, 1)}
&:active {color:nth($ct, 3)}
&.lrc {margin-right:2rem;font-size:1.6rem;}
.volume-ctrl {display:none;flex-direction:column;justify-content:flex-end;position:absolute;left:.5rem;bottom:3rem;width:2.4rem;height:12rem;padding:1rem .8rem;background:#fff;border-radius:.3rem;box-shadow:0 0 1rem rgba(0, 0, 0, .1);
em {flex:0 0;border-radius:.5rem;background:nth($ct, 1)}
}
&.volume:hover .volume-ctrl {display:flex}
}
}
}
//
&.blur {background:rgba(255, 255, 255, .85);backdrop-filter:blur(1rem);
.title-bar { .title-bar {
.holder {background:rgba(255, 255, 255, .3);} position: relative;
.tools input {background:rgba(255, 255, 255, .8);} display: flex;
align-items: center;
z-index: 9;
height: 32px;
background: rgba(32, 32, 32, 0.5);
.btns {
display: flex;
height: 16px;
padding: 0 4px;
}
.btn {
width: 16px;
height: 16px;
margin: 0 6px;
border-radius: 50%;
background: #86909b no-repeat;
background-size: 100%;
}
.btns.active {
.btn.close {
background: #ff5061;
}
.btn.mini {
background: #ffb618;
}
}
} }
.main-body { .main-body {
overflow: hidden;
flex: 1;
display: flex;
justify-content: space-between;
background: rgba(32, 32, 32, 0.5);
.sidebar {background:rgba(255, 255, 255, .3);} .aside {
width: 180px;
height: 100%;
padding: 0 16px;
line-height: 2;
fieldset {
border: 0;
color: #ebebeb;
font-size: 12px;
legend {
font-size: 18px;
background: linear-gradient(to bottom, #58ffdf 50%, #459888);
background-clip: text;
color: transparent;
} }
.contrl-bar {background:rgba(255, 255, 255, .35)} .item {
padding-left: 12px;
line-height: 1.75;
} }
// ktv button {
&.ktv { width: 42px;
.contrl-bar {background:rgba(233, 233, 233, .1); height: 16px;
margin-left: 12px;
.play-box .item, font-size: 10px;
.stat-box .ctrl {color:#fff; border: 0;
border-radius: 9px;
&:hover {color:nth($cr, 1)} background: nth($cb, 1);
color: #fff;
} }
} }
} }
.song-box {
display: flex;
flex-direction: column;
width: 618px;
.ktv-box {overflow:hidden;position:absolute;z-index:80;left:0;top:0;width:100%;height:100%;background-color:nth($cd, 3);background-size:cover;background-repeat:no-repeat;color:#fff; .preview {
position: relative;
display: flex;
align-items: center;
width: 100%;
height: 99px;
padding-bottom: 16px;
border-bottom: 1px solid rgba(200, 200, 200, 0.1);
.inner-content {display:flex;flex-flow:column wrap;width:100%;height:100%;padding-bottom:8rem;background:rgba(29, 35, 44, 0.767);backdrop-filter:blur(1rem); .album {
width: 80px;
.info {flex:1;display:flex;justify-content:center;align-items:center;padding:0 10rem;line-height:2; height: 80px;
img {
img {width:30rem;height:30rem;border:.5rem solid rgba(255, 255, 255, .5);border-radius:50%;} width: 100%;
.summary {flex:1; padding:0 5rem} height: 100%;
pre {overflow:auto;height:30rem}
h3 {line-height:3;font-size:1.8rem;}
}
.lrc-box {flex:0 10rem;display:flex;flex-flow:column wrap;padding:0 5rem;line-height:5rem;color:#fff;font-size:3rem;
section {flex:1;display:flex;
&.left {justify-content:flex-start;}
&.right {justify-content:flex-end}
span {background-clip:text!important;color:transparent;}
}
}
.tool-box {position:absolute;right:0;top:15rem;width:13rem;height:auto;padding:1.5rem 0;background:rgba(255, 255, 255, .1);border-radius:.3rem 0 0 .3rem;opacity:.3;transform:translateX(8.8rem);@include ts();
.item {height:3.4rem;padding:0 .8rem;line-height:3.4rem;
&:hover {background:rgba(255, 255, 255, .1);}
}
i {padding:0 1rem 0 .8rem}
&:hover {opacity:1;transform:translateX(0)}
}
.search-box {display:flex;justify-content:center;align-items:center;position:absolute;left:0;top:0;z-index:82;width:100%;height:56rem;background:rgba(29, 35, 44, 0.5);backdrop-filter:blur(.4rem);
.content {width:60rem;height:auto;padding:2rem;background:rgba(255, 255, 255, .8);color:nth($cd, 1);
.title {height:3rem;line-height:2rem;font-size:1.4rem;text-align:center;
i {float:right;font-size:2rem;color:nth($cr, 1);}
}
.section {height:3.5rem;
input {width:100%;}
}
.result {overflow-y:auto;width:100%;max-height:30rem;padding:1rem;background:rgba(255, 255, 255, .2);
.item {display:flex;justify-content:center;align-items:center;margin:.3rem 0;text-align:center;
&:nth-child(1) {line-height:2;border-bottom:.1rem solid nth($cgr, 1);}
span {flex:1;
&:nth-child(1) {flex:3}
} }
} }
.info {
display: flex;
flex-direction: column;
justify-content: center;
width: 320px;
margin-left: 32px;
strong {
height: 36px;
font-size: 18px;
font-weight: normal;
} }
cite {
font-size: 12px;
font-style: normal;
}
p {
font-size: 12px;
color: #bdbdbd;
}
} }
.total {
position: absolute;
right: 32px;
bottom: 16px;
font-size: 16px;
font-weight: bold;
font-family: Raleway;
}
} }
.scroll-box {
overflow: hidden;
flex: 1;
width: 100%;
padding: 16px 6px;
border-top: 1px solid rgba(32, 32, 32, 0.1);
} }
.list {
height: 100%;
font-size: 12px;
.item {
display: flex;
align-items: center;
height: 26px;
padding: 0 12px;
border-radius: 13px;
.idx {
position: relative;
width: 64px;
padding-left: 16px;
font-size: 12px;
font-family: Raleway;
} }
.name {
.loading {position:fixed;left:0;top:0;z-index:65536;display:flex;justify-content:center;align-items:center;width:100%;height:100%; flex: 1;
.box {position:relative;display:flex;justify-content:center;align-items:center;width:8rem;height:8rem;
i {position:absolute;width:8rem;height:8rem;border:3px solid nth($ct, 1);border-radius:50%;opacity:.5;
&:nth-child(1) {animation:load 2.5s ease-in-out infinite;}
&:nth-child(2) {animation:load 2.5s .5s ease-in-out infinite;}
&:nth-child(3) {animation:load 2.5s 1s ease-in-out infinite;}
&:nth-child(4) {animation:load 2.5s 1.5s ease-in-out infinite;}
&:nth-child(5) {animation:load 2.5s 2s ease-in-out infinite;}
} }
span {position:absolute;width:8rem;height:8rem;background:url(/images/load1.png) no-repeat center center;background-size:cover; .artist {
width: 128px;
&:nth-child(6) {animation:play 1.5s linear infinite;} margin-left: 12px;
&:nth-child(7) {background-image:url(/images/load2.png);animation:load2 2.5s linear infinite;}
} }
cite {font-size:2.4rem} .duration {
width: 42px;
margin-left: 12px;
} }
&.on {
color: #feac23;
font-size: 14px;
.idx::before {
position: absolute;
left: 0;
top: 3px;
font-size: 10px;
content: '';
}
} }
&.active,
&:hover {
color: #58ffdf;
background: rgba(29, 77, 68, 0.15);
}
}
}
}
}
.play-bar {
height: 66px;
width: 100%;
background: rgba(24, 24, 24, 0.3);
color: #fff;
.stat-bar {
display: flex;
align-items: center;
justify-content: space-between;
height: 20px;
color: #ebebeb;
.time {
width: 42px;
margin: 0 6px;
text-align: center;
font-size: 12px;
}
.progress {
position: relative;
flex: 1;
display: flex;
align-items: flex-start;
height: 3px;
background: #b2cfe3;
input {
position: absolute;
left: 0;
top: 0;
display: block;
width: 100%;
height: 3px;
opacity: 0;
}
}
.thumb {
// width: 0;
height: 3px;
background: #58ffdf;
}
}
.ctrl-box {
display: flex;
align-items: center;
justify-content: space-between;
height: 42px;
padding: 0 16px;
.play-btn {
display: flex;
align-items: center;
justify-content: space-between;
width: 120px;
height: 42px;
.item {
width: 22px;
height: 22px;
background-repeat: no-repeat;
background-size: cover;
transition: background 0.1s ease-in-out;
cursor: pointer;
&.prev {
background-image: url(/images/ctrl/prev.png);
&:hover,
&:active {
background-image: url(/images/ctrl/prev_a.png);
}
}
&.on,
&.off {
width: 42px;
height: 42px;
}
&.on {
background-image: url(/images/ctrl/play.png);
animation: round 2s linear infinite;
&:hover,
&:active {
background-image: url(/images/ctrl/play_a.png);
}
}
&.off {
background-image: url(/images/ctrl/pause.png);
&:hover,
&:active {
background-image: url(/images/ctrl/pause_a.png);
}
}
&.next {
background-image: url(/images/ctrl/next.png);
&:hover,
&:active {
background-image: url(/images/ctrl/next_a.png);
}
}
}
}
.play-action {
display: flex;
align-items: center;
justify-content: space-between;
width: 52px;
height: 22px;
.item {
width: 22px;
height: 22px;
background-repeat: no-repeat;
background-size: cover;
transition: background 0.1s ease-in-out;
cursor: pointer;
&.volume,
&.mute {
position: relative;
a {
display: block;
width: 22px;
height: 22px;
background-repeat: no-repeat;
background-size: cover;
transition: background 0.1s ease-in-out;
}
.range {
visibility: hidden;
position: absolute;
right: -5px;
bottom: 20px;
width: 32px;
height: 128px;
padding: 14px;
border-radius: 3px;
background: rgba(128, 128, 128, 0.8);
input {
display: block;
width: 4px;
height: 100px;
border-radius: 3px;
appearance: slider-vertical;
}
}
&:hover {
.range {
visibility: visible;
}
}
}
&.volume a {
background-image: url(/images/ctrl/volume.png);
&:hover,
&:active {
background-image: url(/images/ctrl/volume_a.png);
}
}
&.mute a {
background-image: url(/images/ctrl/mute.png);
&:hover,
&:active {
background-image: url(/images/ctrl/mute_a.png);
}
}
&.single {
background-image: url(/images/ctrl/single.png);
&:hover,
&:active {
background-image: url(/images/ctrl/single_a.png);
}
}
&.all {
background-image: url(/images/ctrl/all.png);
&:hover,
&:active {
background-image: url(/images/ctrl/all_a.png);
}
}
&.rand {
background-image: url(/images/ctrl/rand.png);
&:hover,
&:active {
background-image: url(/images/ctrl/rand_a.png);
}
}
}
}
}
}
} }
@keyframes round {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
.do-mod-contextmenu {width:145px;height:auto;padding:8px 0;line-height:35px;font-size:1.3rem;
li {overflow:hidden;width:100%;height:35px;padding:0 10px;@include ts(background);cursor:default;
&:hover {background:nth($cp, 1)}
i {padding:0 3px;font-size:1.6rem;vertical-align:bottom;}
} }
} }
.do-layer .layer-box.do-mod-contextmenu__fixed {padding:0}
@keyframes load {
from {opacity: .5; transform: scale(1)}
to {opacity: 0; transform: scale(1.5)}
}
@keyframes load2 {
from {transform:rotate(360deg)}
to {transform:rotate(0deg)}
}
@keyframes play {
from {transform:rotate(0deg)}
to {transform:rotate(360deg)}
}

1
src/css/common.css Normal file
View File

@ -0,0 +1 @@
.app-drag{-webkit-app-region:drag;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.app-nodrag{-webkit-app-region:no-drag}body{position:fixed;left:0;top:0;width:100%;height:100%;line-height:1.5;font-size:14px;color:#fff}

21
src/css/common.scss Normal file
View File

@ -0,0 +1,21 @@
@charset "UTF-8";
/**
*
* @authors yutent<yutent@doui.cc>
* @date 2019/01/22 18:16:57
*/
@import "./var.scss";
.app-drag {-webkit-app-region:drag;user-select: none;}
.app-nodrag {-webkit-app-region:no-drag;}
body {
position:fixed;left:0;top:0;
width:100%;height:100%;
line-height:1.5;
font-size:14px;
color:#fff;
}

View File

@ -1 +1 @@
.do-fn-drag{-webkit-app-region:drag;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.do-fn-nodrag{-webkit-app-region:no-drag}html{font-size:62.5%}body{position:fixed;left:0;top:0;display:flex;width:100%;height:100%;line-height:1.5;background:transparent;font-size:1.4rem;color:#62778d}body .lrc-box{position:absolute;left:0;top:0;display:flex;flex-flow:column wrap;width:100%;height:100%;padding:0 5rem;line-height:5rem;color:#fff;font-size:3rem}body .lrc-box section{flex:1;display:flex}body .lrc-box section.left{justify-content:flex-start}body .lrc-box section.right{justify-content:flex-end}body .lrc-box section span{-webkit-background-clip:text !important;background-clip:text !important;color:transparent}body .lrc-box section span.shadow{text-shadow:0 0 0.5rem rgba(0,0,0,0.5)}body .touch-bar{position:absolute;left:0;top:0;z-index:9;width:100%;height:2rem}body .quit,body .lock{visibility:hidden;position:absolute;right:1rem;top:.5rem;font-size:1.8rem;font-weight:bold}body .quit:hover,body .lock:hover{color:#ff5061}body .lock{right:4rem}body .lock.actived{color:#dae1e9}body:hover{background:rgba(29,35,44,0.2)}body:hover .quit,body:hover .lock{visibility:visible} body{background:transparent}body .lrc-box{position:absolute;left:0;top:0;display:flex;flex-flow:column wrap;width:100%;height:100%;padding:0 5rem;line-height:5rem;color:#fff;font-size:3rem}body .lrc-box section{flex:1;display:flex}body .lrc-box section.left{justify-content:flex-start}body .lrc-box section.right{justify-content:flex-end}body .lrc-box section span{-webkit-background-clip:text !important;background-clip:text !important;color:transparent}body .lrc-box section span.shadow{text-shadow:0 0 0.5rem rgba(0,0,0,0.5)}body .touch-bar{position:absolute;right:0;top:0;z-index:9;height:2rem}body .quit,body .lock{visibility:hidden;position:absolute;right:1rem;top:.5rem;font-size:1.8rem;font-weight:bold;text-shadow:0 0 .1rem #fff}body .quit:hover,body .lock:hover{color:#ff5061}body .quit:active,body .lock:active{color:#ffb618}body .lock{right:4rem}body .lock.active{color:#3fc2a7}body:hover{background:rgba(29,35,44,0.15)}body:hover .quit,body:hover .lock{visibility:visible}

View File

@ -8,12 +8,8 @@
@import "./var.scss"; @import "./var.scss";
.do-fn-drag {-webkit-app-region:drag;user-select: none;}
.do-fn-nodrag {-webkit-app-region:no-drag;}
html {font-size:62.5%}
body {position:fixed;left:0;top:0;display:flex;width:100%;height:100%;line-height:1.5;background:transparent;font-size:1.4rem;color:nth($cd, 1);
body {background:transparent;
.lrc-box {position:absolute;left:0;top:0;display:flex;flex-flow:column wrap;width:100%;height:100%;padding:0 5rem;line-height:5rem;color:#fff;font-size:3rem; .lrc-box {position:absolute;left:0;top:0;display:flex;flex-flow:column wrap;width:100%;height:100%;padding:0 5rem;line-height:5rem;color:#fff;font-size:3rem;
@ -30,19 +26,20 @@ body {position:fixed;left:0;top:0;display:flex;width:100%;height:100%;line-heigh
} }
.touch-bar {position:absolute;left:0;top:0;z-index:9;width:100%;height:2rem;} .touch-bar {position:absolute;right:0;top:0;z-index:9;height:2rem;}
.quit, .quit,
.lock {visibility:hidden;position:absolute;right:1rem;top:.5rem;font-size:1.8rem;font-weight:bold; .lock {visibility:hidden;position:absolute;right:1rem;top:.5rem;font-size:1.8rem;font-weight:bold;text-shadow:0 0 .1rem #fff;
&:hover {color:nth($cr, 1)} &:hover {color:nth($cr, 1)}
&:active {color:nth($co, 1)}
} }
.lock {right:4rem; .lock {right:4rem;
&.actived {color:nth($cp, 3)} &.active {color:nth($ct, 1)}
} }
&:hover {background:rgba(29, 35, 44, 0.2); &:hover {background:rgba(29, 35, 44, 0.15);
.quit, .lock {visibility:visible;} .quit, .lock {visibility:visible;}
} }

1
src/css/mini-win.css Normal file
View File

@ -0,0 +1 @@
#app{display:flex;width:100%;height:100%}#app .cover{overflow:hidden;flex:0 6rem;box-shadow:0 0 0.1rem rgba(0,0,0,0.1)}#app .cover img{width:100%}#app .ctrl{flex:1;padding:.8rem;line-height:2.2rem}#app .ctrl .title{font-weight:normal;font-size:1.2rem}#app .ctrl .btns{font-size:2.2rem}#app .ctrl .btns span:hover{color:#ffb618}#app .tools,#app .actions{display:flex;justify-content:center;align-items:center;position:absolute;right:.5rem;top:.5rem;line-height:2rem;font-size:1.6rem;text-align:center;color:#98acae}#app .tools span,#app .actions span{margin:0 .2rem}#app .tools span:hover,#app .actions span:hover{color:#3fc2a7}#app .tools span.close:hover,#app .actions span.close:hover{color:#ff5061}#app .tools span.active,#app .actions span.active{font-weight:bold}#app .actions{top:auto;bottom:.5rem}#app .actions span:nth-child(1){font-size:1.2rem}

45
src/css/mini-win.scss Normal file
View File

@ -0,0 +1,45 @@
@charset "UTF-8";
/**
*
* @authors yutent<yutent@doui.cc>
* @date 2019/01/22 18:01:58
*/
@import "./var.scss";
#app {display:flex;width:100%;height:100%;
.cover {overflow:hidden;flex:0 6rem;box-shadow:0 0 .1rem rgba(0, 0, 0, .1);
img {width:100%;}
}
.ctrl {flex:1;padding:.8rem;line-height:2.2rem;
.title {font-weight:normal;font-size:1.2rem}
.btns {font-size:2.2rem;
span:hover {color:nth($co, 1);}
}
}
.tools,
.actions {display:flex;justify-content:center;align-items:center;position:absolute;right:.5rem;top:.5rem;line-height:2rem;font-size:1.6rem;text-align:center;color:nth($cgr, 1);
span {margin:0 .2rem;
&:hover {color:nth($ct, 1)}
&.close:hover {color:nth($cr, 1)}
&.active {font-weight:bold;}
}
}
.actions {top:auto;bottom:.5rem;
span {
&:nth-child(1) {font-size:1.2rem}
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -74,7 +74,10 @@
// //
.do-mod-local {flex:1;display:flex;flex-flow:column wrap; .do-mod-local {flex:1;display:flex;flex-flow:column wrap;
.tabbar {flex:0 1 3rem;padding:0 1rem;line-height:2.9rem;border-bottom:.1rem solid rgba(200, 200, 200, .3); .tabbar {
flex:0 1 3rem;
padding:0 1rem;line-height:2.9rem;
border-bottom:.1rem solid rgba(200, 200, 200, .3);
.refresh {margin-left:1rem;color:nth($ct, 1);text-decoration:underline; .refresh {margin-left:1rem;color:nth($ct, 1);text-decoration:underline;
@ -83,19 +86,13 @@
} }
} }
.table {overflow:auto;flex:1; .table {flex:1;}
.stat {width:2.6rem;height:2.6rem;line-height:2.6rem;}
.ac {text-align:center}
.active {color:nth($ct, 1);background:rgba(255, 255, 255, 0.6);;
i {animation: play 2s infinite linear;}
}
}
.edit-form {position:absolute;left:0;top:0;z-index:90;display:flex;justify-content:center;align-items:center;width:100%;height:100%; .edit-form {
position:absolute;left:0;top:0;z-index:90;
display:flex;justify-content:center;align-items:center;
width:100%;height:100%;
.form {position:relative;display:flex;flex-flow:column wrap;flex:0 40rem;width:5rem;height:auto;padding:.5rem 4rem 2rem;background:#fff;box-shadow:0 .5rem 2rem rgba(0, 0, 0, .2); .form {position:relative;display:flex;flex-flow:column wrap;flex:0 40rem;width:5rem;height:auto;padding:.5rem 4rem 2rem;background:#fff;box-shadow:0 .5rem 2rem rgba(0, 0, 0, .2);
@ -108,7 +105,10 @@
} }
.label {flex:0 5rem;padding-right:2rem;color:nth($cgr, 1);text-align:right;} .label {flex:0 5rem;padding-right:2rem;color:nth($cgr, 1);text-align:right;}
.field {flex:1} .field {flex:1;
&.path {border:0;font-size:1.3rem;color:nth($cd, 2)}
}
} }
} }
@ -125,21 +125,28 @@
.tabbar {flex:0 1 3rem;display:flex;padding:0 .5rem;line-height:2.9rem;border-bottom:.1rem solid nth($cp, 2);text-align:center; .tabbar {flex:0 1 3rem;display:flex;padding:0 .5rem;line-height:2.9rem;border-bottom:.1rem solid nth($cp, 2);text-align:center;
.item {flex:0 0 7.5rem;height:3rem;margin:0 .3rem;border:.1rem solid rgba(200, 200, 200, .3);background:rgba(255, 255, 255, .3);color:nth($cp, 3); .item {flex:0 0 7.5rem;height:3rem;margin:0 .3rem;border:.1rem solid rgba(200, 200, 200, .3);border-bottom:0;color:nth($cp, 3);
&.active {border-bottom-color:transparent;color:nth($cd, 1);} &.active {color:nth($cd, 1);}
i {color:nth($cr, 1)} i {color:nth($cr, 1)}
} }
} }
.table {overflow:auto;flex:1; .table {overflow:auto;flex:1;}
.active {color:nth($ct, 1)}
.ac {text-align:center} }
#app.blur {
.do-mod-search {
.tabbar {
.item {border-color:nth($cp, 3);color:nth($cp, 1);
&.active {color:nth($cd, 1);}
}
}
} }
} }

82
src/css/reset-basic.css Normal file
View File

@ -0,0 +1,82 @@
@charset "UTF-8";
/**
*
* @authors yutent (yutent.io@gmail.com)
* @date 2014-10-10 00:45:09
*
* CSS
*
* ,
*
*
* 1 display float position overflow z-index /
* 2 width height margin padding border
* 3 line-height font-size vertical-align text-align user-select outline ....
* 4 color background opacity cursor ...
* 5 content list-style quotes ...
*
*/
* {margin: 0;padding: 0;vertical-align: baseline;box-sizing: border-box;}
::before, ::after {box-sizing: border-box;}
/* HTML5 display-role reset for older browsers */
article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section,content {display: block;}
img {border: 0;display: inline-block;}
ol,ul {list-style: none;}
blockquote, q {quotes: none;}
blockquote::before, blockquote::after, q::before, q::after {content: '';content: none;}
table {border-collapse: collapse;border-spacing: 0;}
a:focus,input,textarea,button:focus,input:focus,textarea:focus {outline: none;}
::-moz-focus-inner {border: none;outline: none;}
body {font-family: 'Helvetica Neue', Arial, 'WenQuanYi Micro Hei', 'PingFang SC', 'Hiragino Sans GB', 'Segoe UI', 'Microsoft Yahei', sans-serif;-webkit-font-smoothing: antialiased;text-size-adjust: 100%;-webkit-tap-highlight-color: transparent;}
code, pre, samp {font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;white-space:pre-wrap;}
[anot],[\:repeat],[\:if] {visibility: hidden;}
.noselect {-webkit-touch-callout: none;-webkit-user-select: none;-moz-user-select: none;user-select: none;}
.noselect img, .noselect a {-webkit-user-drag: none;}
.text-ell {overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}
.text-thin {-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;}
:root {
/* primary */
--color-teal-a: rgba(72, 201, 176, 0.35);
--color-teal-1: rgb(72, 201, 176);
--color-teal-2: rgb(26, 188, 156);
--color-teal-3: rgb(22, 160, 133);
/* success */
--color-green-a: rgba(70, 221, 126, 0.35);
--color-green-1: rgb(70, 221, 126);
--color-green-2: rgb(47, 208, 105);
--color-green-3: rgb(26, 196, 88);
/* info */
--color-blue-a: rgba(100, 181, 246, 0.35);
--color-blue-1: rgb(100, 181, 246);
--color-blue-2: rgb(66, 165, 245);
--color-blue-3: rgb(33, 150, 243);
/* danger */
--color-red-a: rgba(252, 118, 97, 0.35);
--color-red-1: #fc7661;
--color-red-2: #ff5f45;
--color-red-3: #f33e22;
/* warning */
--color-orange-a: rgba(254, 174, 117, 0.35);
--color-orange-1: #feae75;
--color-orange-2: #fd964b;
--color-orange-3: #f97316;
/* default1 */
--color-plain-a: rgba(150, 204, 248, 0.35);
--color-plain-1: rgb(242, 245, 252);
--color-plain-2: rgb(232, 235, 244);
--color-plain-3: rgb(218, 225, 233);
/* default2 */
--color-grey-a: rgba(206, 214, 224, 0.35);
--color-grey-1: rgb(206, 214, 224);
--color-grey-2: rgb(164, 176, 190);
--color-grey-3: rgb(134, 144, 155);
/* inverse */
--color-dark-a: rgba(100, 116, 139, 0.35);
--color-dark-1: #64748B;
--color-dark-2: #475569;
--color-dark-3: #2c3441;
}

View File

@ -1,14 +1,17 @@
$ct: #3fc2a7 #19b491 #16967a; $ct: #4db6ac #26a69a #009688;
$cg: #58d68d #2ecc71 #27ae60; $cg: #81c784 #66bb6a #4caf50;
$cpp: #ac61ce #9b59b6 #8e44ad; $cpp: #9575cd #9575cd #673ab7;
$cb: #52a3de #2d8dd6 #2776b1; $cb: #64b5f6 #42a5f5 #2196f3;
$cr: #ff5061 #eb3b48 #ce3742; $cr: #ff5061 #eb3b48 #ce3742;
$co: #ffb618 #f39c12 #e67e22; $co: #ffb618 #f39c12 #e67e22;
$cp: #f3f5fb #e8ebf4 #dae1e9; $cp: #f2f5fc #e8ebf4 #dae1e9;
$cgr: #98acae #8a9b9c #748182; $cgr: #bdbdbd #9e9e9e #757575;
$cd: #62778d #526273 #425064; $cd: #62778d #526273 #425064;
@mixin ts($c: all, $t: .2s, $m: ease-in-out){ @mixin ts($c: all, $t: .2s, $m: ease-in-out){
transition:$c $t $m; transition:$c $t $m;
} }
@function px($n: 1) {
@return ($n / 10) + rem;
}

View File

@ -1,35 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<link href="lib/css/reset-basic.css" rel="stylesheet">
<link href="lib/css/elem-ui.css" rel="stylesheet">
<style>
.do-fn-drag {-webkit-app-region:drag;user-select: none;}
.do-fn-nodrag {-webkit-app-region:no-drag;}
html {font-size:62.5%}
body {position:fixed;left:0;top:0;width:100%;height:100%;padding:4rem 3rem;line-height:2;font-size:1.4rem;color:#62778d;}
span {color:#ff5061;font-weight:bold}
cite {color:#dae1e9;font-style:italic}
</style>
</head>
<body class="do-fn-drag">
<h1>欢迎使用Sonist!</h1>
<pre>
检测到你系统中没有安装<span>ffmpeg</span>
请先安装<span>ffmpeg</span>
如果你的系统是MacOS, 可以使用brew来安装:
brew install ffmpeg
如果你的系统是Linux, 可以使用包管理器"apt/yum/pacman等来安装":
sudo apt install ffmpeg <cite># 以debian系为例</cite>
</pre>
</body>
</html>

View File

@ -1,35 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<link href="lib/css/reset-basic.css" rel="stylesheet">
<link href="/css/desktop-lrc.css" rel="stylesheet">
<script>window.LIBS_BASE_URL = location.origin + '/lib';</script>
<script type="module" src="js/desktop-lrc.js"></script>
</head>
<body class="do-fn-noselect" anot="lrc">
<div class="lrc-box">
<section class="left">
<span class="shadow" :text="lrc.l.txt"></span>
</section>
<section class="right">
<span class="shadow" :text="lrc.r.txt"></span>
</section>
</div>
<div class="lrc-box">
<section class="left">
<span :text="lrc.l.txt" :css="{background: lrc.l.bg}"></span>
</section>
<section class="right">
<span :text="lrc.r.txt" :css="{background: lrc.r.bg}"></span>
</section>
</div>
<div class="touch-bar do-fn-drag">
<a class="do-icon-lock lock do-fn-nodrag" :class="{actived: isLock}" :click="lock"></a>
<a class="do-icon-close quit do-fn-nodrag" :click="quit"></a>
</div>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

View File

@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #f55449;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
</style>
</defs>
<title>btn_close_hover</title>
<g id="red">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 594 B

View File

@ -1,25 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #f55449;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
.cls-3 {
opacity: 0.7;
}
</style>
</defs>
<title>btn_close_hover</title>
<g id="red">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
<rect class="cls-3" x="192" y="476" width="640" height="72" rx="36" ry="36" transform="translate(512 1236.08) rotate(-135)"/>
<rect class="cls-3" x="191" y="475" width="640" height="72" rx="36" ry="36" transform="translate(1233.66 511) rotate(135)"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 899 B

View File

@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #dee1e3;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
</style>
</defs>
<title>btn_grey</title>
<g id="grey">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 588 B

View File

@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #39ea49;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
</style>
</defs>
<title>btn_max_hover</title>
<g id="green">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 594 B

View File

@ -1,24 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #39ea49;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
.cls-3 {
opacity: 0.7;
}
</style>
</defs>
<title>btn_max_hover</title>
<g id="green">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
<path class="cls-3" d="M395.93,267.8a31.32,31.32,0,0,1,22.86-9.48l319.33-2.55A31.25,31.25,0,0,1,770,287.64l-2.5,319.29A32,32,0,0,1,758,629.79a31.06,31.06,0,0,1-22.86,9.48,30.67,30.67,0,0,1-22.72-9.15L395.65,313.33c-6.22-6.13-9.24-13.67-9.15-22.72a31.28,31.28,0,0,1,9.43-22.82ZM629.84,758A31.32,31.32,0,0,1,607,767.45L287.64,770a31.25,31.25,0,0,1-31.87-31.87l2.5-319.29A32,32,0,0,1,267.75,396a31.06,31.06,0,0,1,22.86-9.48,30.67,30.67,0,0,1,22.72,9.15L630.12,712.44c6.22,6.13,9.24,13.67,9.15,22.72A31.28,31.28,0,0,1,629.84,758Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #fac536;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
</style>
</defs>
<title>btn_min_hover</title>
<g id="yellow">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 595 B

View File

@ -1,24 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #fac536;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
.cls-3 {
opacity: 0.7;
}
</style>
</defs>
<title>btn_min_hover</title>
<g id="yellow">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
<rect class="cls-3" x="192" y="476" width="640" height="72" rx="36" ry="36"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 723 B

BIN
src/images/ctrl/all.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
src/images/ctrl/all_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
src/images/ctrl/mute.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
src/images/ctrl/mute_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
src/images/ctrl/next.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src/images/ctrl/next_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
src/images/ctrl/pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
src/images/ctrl/pause_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
src/images/ctrl/play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
src/images/ctrl/play_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
src/images/ctrl/prev.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
src/images/ctrl/prev_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src/images/ctrl/rand.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
src/images/ctrl/rand_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
src/images/ctrl/single.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src/images/ctrl/volume.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1008 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 687 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1017 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
src/images/trays/tray.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 939 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 797 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 915 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -2,177 +2,84 @@
<html lang="zh-CN"> <html lang="zh-CN">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <link href="css/reset-basic.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"> <link href="css/common.css" rel="stylesheet">
<link href="lib/css/reset-basic.css" rel="stylesheet">
<link href="lib/css/elem-ui.css" rel="stylesheet">
<link href="css/app.css" rel="stylesheet"> <link href="css/app.css" rel="stylesheet">
<link href="css/modules.css" rel="stylesheet">
<script>window.LIBS_BASE_URL = location.origin + '/lib';</script>
<script type="module" src="js/app.js"></script> <script type="module" src="js/app.js"></script>
</head> </head>
<body class="do-fn-noselect" anot="app" :css="{'background-image': coverBG}"> <body class="noselect">
<div class="do-mod-app" :class="{blur: isPlaying && !ktvMode, ktv: ktvMode}"> <div class="app" anot="app">
<div class="title-bar do-fn-drag"> <div class="album-cover">
<img :src="preview.cover || defaultCover">
</div>
<nav class="btn-box do-fn-nodrag" :if="theme === 1" :class="{focus: winFocus}"> <div class="title-bar app-drag">
<i class="item quit" :click="quit(false)"></i> <div class="btns active">
<i class="item min" :click="minimize"></i> <a class="btn close"></a>
<i class="item max" :click="maximize"></i> <a class="btn mini"></a>
</nav>
<!-- 背景点位标签 -->
<div class="holder"></div>
<div class="tools do-fn-drag">
<div class="search do-fn-nodrag">
<input
class="do-ui-input"
placeholder="搜索 音乐/歌手/专辑"
:keyup="searchMusic"
:duplex="searchTxt">
<i class="icon do-icon-search"></i>
</div> </div>
</div> </div>
<main class="main-body">
<nav class="btn-box-win do-fn-nodrag"> </main>
<div class="opt">
<i class="do-icon-menu-right" :click="toggleOptBox"></i> <div class="play-bar">
<section class="opt-list" :visible="optBoxShow"> <section class="stat-bar">
<span><i class="do-icon-maximized"></i> 迷你模式</span> <span class="time" :text="song.time | time"></span>
<span :click="toggleModule('profile')"> <div class="progress">
<i class="do-icon-setting"></i> 首选项 <span class="thumb" :css="{width: ((100 * song.time) / song.duration).toFixed(1) + '%'}"></span>
</span> <input type="range" max="712" min="0" step="1" :duplex="progress">
<span class="pipe"></span> </div>
<span :click="quit(true)"><i class="do-icon-logout"></i> 退出</span> <span class="time" :text="song.duration | time"></span>
</section> </section>
</div>
<span :if="theme === 2"> <section class="ctrl-box">
<i class="item do-icon-minimize" :click="minimize"></i> <wc-icon is="menu-dot"></wc-icon>
<i class="item do-icon-maximize disabled" :click="maximize"></i>
<i class="item do-icon-close quit" :click="quit(false)"></i> <div class="play-btn">
</span> <a class="item prev" @click="play(-1)"></a>
</nav> <a class="item"
:class="{on: isplaying, off: !isplaying}"
@click="play(0)">
</a>
<a class="item next" @click="play(1)"></a>
</div> </div>
<div class="play-action">
<item class="item" :class="{
<div class="main-body"> all: playmode === 1,
single: playmode === 2,
<aside class="sidebar do-fn-drag"> rand: playmode === 3,
<div class="user-box">
<div class="avatar">
<img src="/images/avatar.jpg" alt="yutent">
</div>
<h2 class="uname">yutent</h2>
</div>
<dl class="music-box do-fn-nodrag">
<dt class="title">酷狗在线</dt>
<dd class="item"
:click="toggleModule('rank')"
:class="{active: mod === 'rank'}">
<i class="s-icon-rank"></i> 排行榜
</dd>
<dd class="item"
:click="toggleModule('artist')"
:class="{active: mod === 'artist'}">
<i class="s-icon-singer"></i> 歌手
</dd>
<dd class="item disabled"
:click="toggleModule('mv')"
:class="{active: mod === 'mv'}">
<i class="s-icon-mv"></i> MV
</dd>
<dt class="title">我的音乐</dt>
<dd class="item"
:click="toggleModule('search')"
:class="{active: mod === 'search'}">
<i class="s-icon-heart"></i> 试听列表
</dd>
<dd class="item"
:click="toggleModule('local')"
:class="{active: mod === 'local'}">
<i class="s-icon-play-list"></i> 本地音乐
</dd>
</dl>
</aside>
<content class="module" :include="views" data-cache="true"></content>
</div>
<div
class="ktv-box"
:if="ktvMode"
:include="'/views/ktv.htm'"
:css="{'background-image': coverBG}">
</div>
<div class="contrl-bar">
<div class="play-box">
<span class="item prev s-icon-prev" :click="nextSong(-1)"></span>
<span
class="item play"
:class="{'s-icon-play': !isPlaying, 's-icon-pause': isPlaying}"
:click="play(null)">
</span>
<span class="item next s-icon-next" :click="nextSong(1)"></span>
</div>
<div class="stat-box">
<div class="song-stat">
<canvas ref="player"></canvas>
</div>
<span
class="ctrl"
:class="{
's-icon-all': playMode === 0,
's-icon-single': playMode === 1,
's-icon-random': playMode === 2
}" }"
:click="togglePlayMode"> @click="switchMode">
</span> </item>
<section class="ctrl volume"> <item class="item" :class="{mute: mute || volume <= 0, volume: !mute && volume > 0}">
<i <div class="range">
:class="{ <input type="range" max="100" min="0" step="1" :duplex="volume">
'do-icon-unmute' : volume > 0, </div>
'do-icon-mute' : volume === 0 <a @click="toggleMute"></a>
}"> </item>
</i> </div>
<span
class="volume-ctrl"
:click="changeValume">
<em :css="{flex: '0 ' + volume + '%'}"></em>
</span>
</section> </section>
<span class="ctrl s-icon-eq"></span>
<span class="ctrl lrc" :click="toggleDesktopLrc"></span>
</div> </div>
<div class="scroll-box">
<wc-scroll class="list" ref="list">
<section
class="item"
@click="previewSong(it, i)"
@dblclick="playSong(i)"
:class="{on: curr === i}"
:for="i it in list">
<span class="idx" :text="i + 1"></span>
<span class="name text-ell" :text="it.name"></span>
<span class="artist text-ell" :text="it.artist || '未知歌手'"></span>
<span class="duration" :text="it.duration | time"></span>
</section>
</wc-scroll>
</div> </div>
<div class="loading" :if="loading">
<div class="box">
<i></i><i></i><i></i><i></i><i></i>
<span></span>
<span></span>
<cite>{{progress}}%</cite>
</div>
</div>
</div> </div>
</body> </body>

View File

@ -1,137 +1,63 @@
/** /**
* 音乐APP接口 * 部分远程接口
* @author yutent<yutent@doui.cc> * @author yutent<yutent.io@gmail.com>
* @date 2018/12/24 16:02:00 * @date 2020/11/20 19:07:32
*/ */
'use strict' import fetch from './lib/fetch/index.js'
import request from '/lib/request/index.js' function tojson(r) {
return r.json()
const log = console.log
const BASE_API_URI = 'http://mobilecdnbj.kugou.com'
const get = uri => {
return request.get(BASE_API_URI + uri)
}
const post = uri => {
return request.post(BASE_API_URI + uri)
} }
export default { export default {
getLastHot100Artists() { searchLrc(keyword, duration) {
return get('/api/v5/singer/list') return fetch('http://lyrics.kugou.com/search', {
.send({ body: {
sort: 1, client: 'pc',
showtype: 1, duration: duration * 1000,
sextype: 0,
musician: 0,
pagesize: 100,
plat: 2,
type: 0,
page: 1
})
.then(res => {
if (res.status === 200) {
return JSON.parse(res.text)
}
})
},
getArtistList(sextype = 1, type = 1) {
return get('/api/v5/singer/list')
.send({
showtype: 2,
musician: 0,
type,
sextype
})
.then(res => {
if (res.status === 200) {
return JSON.parse(res.text)
}
})
},
getArtistInfo(singerid) {
return get('/api/v3/singer/info')
.send({ singerid })
.then(res => {
if (res.status === 200) {
return JSON.parse(res.text)
}
})
},
getArtistInfo(singerid) {
return get('/api/v3/singer/info')
.send({ singerid })
.then(res => {
if (res.status === 200) {
return JSON.parse(res.text)
}
})
},
getArtistSongs(singerid, page = 1) {
return get('/api/v3/singer/song')
.send({
sorttype: 2,
pagesize: 50,
singerid,
area_code: 1,
page
})
.then(res => {
if (res.status === 200) {
return JSON.parse(res.text)
}
})
},
getArtistAlbums(singerid, page = 1) {
return get('/api/v3/singer/album')
.send({
pagesize: 50,
singerid,
area_code: 1,
page
})
.then(res => {
if (res.status === 200) {
return JSON.parse(res.text)
}
})
},
search(keyword) {
return request
.get('https://songsearch.kugou.com/song_search_v2')
.send({
keyword, keyword,
platform: 'WebFilter', man: 'no',
tag: '' ver: 1
})
.then(res => {
if (res.status === 200) {
return JSON.parse(res.text).data.lists
} }
}) })
.then(tojson)
.then(r => {
if (r.candidates && r.candidates.length) {
return r.candidates.map(it => ({
id: it.id,
accesskey: it.accesskey,
duration: it.duration,
singer: it.singer,
song: it.song
}))
}
return []
})
}, },
getSongInfoByHash(hash, album_id = '') { downloadLrc(id, accesskey) {
return request return fetch('http://lyrics.kugou.com/download', {
.get('https://wwwapi.kugou.com/yy') body: {
.send({ client: 'pc',
r: 'play/getdata', id,
hash, accesskey,
album_id ver: 1,
fmt: 'lrc',
charset: 'utf8'
}
}) })
.then(res => { .then(tojson)
if (res.status === 200) { .then(r => {
return JSON.parse(res.text).data if (r.status === 200 && r.content) {
var lrc
try {
lrc = Buffer.from(r.content, 'base64').toString()
lrc = lrc.split(/[\r\n]+/).join('\n')
} catch (e) {
console.error(e, r)
}
return lrc
} }
}) })
} }

View File

@ -1,474 +1,257 @@
/** /**
* {sonist app} *
* @author yutent<yutent@doui.cc> * @author yutent<yutent.io@gmail.com>
* @date 2018/12/16 17:15:57 * @date 2020/11/16 16:21:52
*/ */
import '/lib/anot.next.js' import Anot from '/js/lib/anot.js'
import layer from '/lib/layer/index.js' import '/js/lib/scroll/index.js'
import store from '/lib/store/index.js' import '/js/lib/icon/index.js'
import AudioPlayer from '/lib/audio/index.js'
import Lyrics from '/lib/lyrics/index.js'
import Keyboard from '/js/lib/keyboard/index.js'
// import app from '/js/lib/socket.js'
import fetch from '/js/lib/fetch/index.js'
import Api from '/js/api.js' import Api from '/js/api.js'
import Artist from '/js/modules/artist.js' import Player from '/js/lib/audio/index.js'
import Local from '/js/modules/local.js'
import Profile from '/js/modules/profile.js'
import Search from '/js/modules/search.js'
import KTV from '/js/modules/ktv.js' // const id3 = require('music-metadata')
const log = console.log const MODE_DICT = { 1: 'all', 2: 'single', 3: 'random' }
const fs = require('iofs') var kb = new Keyboard()
const path = require('path')
const { remote } = require('electron') var player = new Player()
const WIN = remote.getCurrentWindow() window.fetch = fetch
const CURR_SCREEN = remote.screen.getPrimaryDisplay() window.player = player
const HOME_PATH = remote.app.getPath('appData')
const APP_INI_PATH = path.join(HOME_PATH, 'app.ini')
const LYRICS_PATH = path.join(HOME_PATH, 'lyrics')
const PLAY_MODE = {
0: 'all',
1: 'single',
2: 'random'
}
const COLORS = [
{
title: '#62778d',
lrc: '#98acae',
bar1: '#dae1e9',
bar2: '#3fc2a7'
},
{
title: '#fff',
lrc: '#d7d8db',
bar1: '#454545',
bar2: '#fff'
}
]
const FONTS_NAME =
' Helvetica, Arial,"WenQuanYi Micro Hei","PingFang SC","Hiragino Sans GB","Segoe UI", "Microsoft Yahei", sans-serif'
// 本地音乐和试用音乐列表
window.LS = store.collection('local')
window.TS = store.collection('temp')
// 音乐播放器
window.SONIST = new AudioPlayer()
window.LYRICS = new Lyrics()
let appInit = fs.cat(APP_INI_PATH)
Anot.ss('app-init', appInit + '')
appInit = JSON.parse(appInit)
const LRC_WIN = new remote.BrowserWindow({
title: '',
width: 1024,
height: 100,
frame: false,
resizable: false,
alwaysOnTop: true,
x: (CURR_SCREEN.size.width - 1024) / 2,
y: CURR_SCREEN.size.height - 100,
skipTaskbar: true,
hasShadow: false,
thickFrame: false,
transparent: true,
show: false
})
LRC_WIN.loadURL('app://sonist/desktop-lrc.html')
Anot({ Anot({
$id: 'app', $id: 'app',
state: { state: {
theme: appInit.theme || 1, // 1:macos, 2: deepin defaultCover: '/images/disk.png',
winFocus: false, isplaying: false,
mod: 'local', playmode: +Anot.ls('app_mode') || 1,
searchTxt: '', mute: false,
playMode: Anot.ls('play-mode') >>> 0, // 0:all | 1:single | 2:random volume: +Anot.ls('app_volume') || 50,
ktvMode: 0, preview: {
isPlaying: false, name: '',
optBoxShow: false,
volumeCtrlShow: false,
volume: Anot.ls('volume') || 70,
curr: {
id: '',
title: '',
artist: '',
album: '', album: '',
artist: '',
cover: ''
},
song: {
name: '',
artist: '',
src: '',
time: 0, time: 0,
duration: 0 duration: 0
}, },
ctrlLrc: '暂无歌词...', progress: 0,
...KTV.data, curr: -1,
loading: false, list: []
progress: 0
}, },
skip: [], async mounted() {
computed: { // var list = app.dispatch('get-all-songs')
views() { // var list = app.dispatch('scan-dir', { dir: '/Volumes/ooc/music' })
if (!this.mod) {
return // app.on('tray-play', ev => {
// this.play(0)
// })
// app.on('tray-prev', ev => {
// this.play(-1)
// })
// app.on('tray-next', ev => {
// this.play(1)
// })
kb.on(['left'], ev => {
var time = this.song.time - 5
if (time < 0) {
time = 0
} }
return '/views/' + this.mod + '.htm' this.song.time = time
}, })
coverBG() { kb.on(['right'], ev => {
if (this.curr.cover) { var time = this.song.time + 5
return `url(${this.curr.cover})` if (time > this.song.duration) {
} else { time = this.song.duration
return 'none'
} }
this.song.time = time
})
kb.on(['down'], ev => {
var vol = +this.volume - 5
if (vol < 0) {
vol = 0
} }
this.volume = vol
})
kb.on(['up'], ev => {
var vol = +this.volume + 5
if (vol > 100) {
vol = 100
}
this.volume = vol
})
// for (let it of list) {
// let { album, artist, title, duration } = await this.getID3(it.file_path)
// it.name = title || it.name
// it.artist = artist
// it.album = album
// it.duration = duration
// app.dispatch('add-song', {
// name: it.name,
// artist,
// album,
// duration,
// file_path: it.file_path
// })
// }
// console.log(list)
// this.list = list
player.volume = this.volume / 100
// player.load(list.map(it => `sonist://${it.file_path}`))
player.on('play', time => {
this.song.time = time
})
player.on('stop', _ => {
var idx = this.curr
var repeat = false
switch (this.playmode) {
case 1: // all
idx++
if (idx >= this.list.length) {
idx = 0
}
break
case 2: // single
repeat = true
break
case 3: // random
idx = ~~(Math.random() * this.list.length)
break
}
this.playSong(idx, repeat)
})
}, },
watch: { watch: {
mod(val) { volume(v) {
this.activeModule(val) Anot.ls('app_volume', v)
} player.volume = v / 100
}, },
mounted() { playmode(v) {
let canvas = this.$refs.player Anot.ls('app_mode', v)
// 画布放大4倍, 以解决模糊的问题
this.__WIDTH__ = canvas.clientWidth * 4
this.__HEIGHT__ = canvas.clientHeight * 4
canvas.width = this.__WIDTH__
canvas.height = this.__HEIGHT__
this.__CTX__ = canvas.getContext('2d')
this.draw(true)
// 修改歌曲进度
canvas.addEventListener(
'click',
ev => {
if (!this.curr.id) {
return
}
let rect = canvas.getBoundingClientRect()
let aw = rect.width
let ax = ev.pageX - rect.left
let ay = ev.pageY - rect.top
if (ax < 80) {
this.ktvMode = this.ktvMode ^ 1
return
}
if (ax > 124 && ay > 55 && ay < 64) {
let pp = (ax - 124) / (aw - 124)
this.curr.time = pp * this.curr.duration
SONIST.seek(this.curr.time)
LYRICS.seek(this.curr.time)
if (!this.isPlaying) {
this.draw()
}
}
}, },
false progress(v) {
) var t = +(this.song.duration * (v / 712)).toFixed(2)
player.seek(t)
// 设置循环模式 }
SONIST.mode = PLAY_MODE[this.playMode]
SONIST.volume = this.volume
SONIST.on('play', time => {
this.curr.time = time
LYRICS.update(time)
})
SONIST.on('end', time => {
this.nextSong(1)
})
// 控制条的单行歌词
LYRICS.on('ctrl-lrc', lrc => {
this.ctrlLrc = lrc
})
// ktv模式的歌词
LYRICS.on('ktv-lrc', lrc => {
this.lrc = lrc
LRC_WIN.emit('ktv-lrc', lrc)
})
// ktv模式的歌词
LYRICS.on('view-all', lrc => {
this.allLrc = lrc
})
this.activeModule(this.mod)
remote.app.on('browser-window-focus', _ => {
this.winFocus = true
})
remote.app.on('browser-window-blur', _ => {
this.winFocus = false
})
}, },
methods: { methods: {
quit(force) { play(act) {
if (force) { var idx = this.curr
remote.app.exit() var repeat = false
} else {
if (appInit.allowPlayOnBack) {
WIN.hide()
} else {
remote.app.exit()
}
}
},
minimize() {
WIN.minimize()
},
maximize() {},
activeModule(mod) { switch (act) {
switch (mod) { case 0:
case 'artist': if (idx > -1) {
Artist.__init__() player.play(-1)
break this.isplaying = !this.isplaying
case 'local': } else {
Local.__init__() this.playSong(0)
break }
case 'profile':
Profile.__init__()
break
case 'search':
Search.__init__()
break break
default: default:
switch (this.playmode) {
case 1: // all
idx += act
if (idx < 0) {
idx = this.list.length - 1
}
if (idx >= this.list.length) {
idx = 0
}
break
case 2: // single
if (idx === -1) {
idx = ~~(Math.random() * this.list.length)
}
repeat = true
break
case 3: // random
idx = ~~(Math.random() * this.list.length)
break
}
this.playSong(idx, repeat)
break break
} }
}, },
toggleOptBox() { switchMode() {
this.optBoxShow = !this.optBoxShow var n = this.playmode + 1
}, if (n > 3) {
toggleDesktopLrc() { n = 1
if (LRC_WIN.isVisible()) {
LRC_WIN.hide()
} else {
LRC_WIN.showInactive()
} }
this.playmode = n
}, },
toggleModule(mod) { toggleMute() {
if ('mv' === mod) { this.mute = !this.mute
},
getID3(file) {
return id3.parseFile(file).then(res => {
let {
common: { album, artist, title },
format: { duration }
} = res
return { album, artist, title, duration: ~~duration }
})
},
previewSong(it) {
var { album, artist, name, cover } = it
Object.assign(this.preview, { album, artist, name, cover })
},
playSong(i, repeat) {
//
var it = this.list[i]
if (this.curr === i) {
if (repeat) {
this.song.time = 0
player.play(-1, repeat)
}
return return
} }
this.optBoxShow = false // player.stop()
this.mod = mod this.curr = i
}, this.song.name = it.name
// 设置保存 回调 this.song.artist = it.artist
onProfileSaved() { this.song.duration = it.duration
this.toggleModule('local') this.song.src = `file://${it.file_path}`
appInit = JSON.parse(Anot.ss('app-init')) this.song.time = 0
}, this.isplaying = true
this.previewSong(it)
player.play(i)
this.$refs.list.scrollTop = (i - 3) * 26
togglePlayMode() { app.dispatch('update-lrc', { lrc: '正在播放: ' + it.name })
let mod = this.playMode
mod++ // if (!it.lrc) {
if (mod > 2) { // Api.searchLrc(it.name, it.duration).then(list => {
mod = 0 // var { id, accesskey } = list[0]
// Api.downloadLrc(id, accesskey).then(lrc => {
// console.log(lrc)
// })
// })
// }
} }
this.playMode = mod
SONIST.mode = PLAY_MODE[mod]
Anot.ls('play-mode', mod)
},
// 修改音量
changeValume(ev) {
let volume = 575 - ev.pageY
if (volume < 0) {
volume = 0
}
if (volume > 100) {
volume = 100
}
this.volume = volume
SONIST.volume = volume
Anot.ls('volume', volume)
},
searchMusic(ev) {
if (ev.keyCode === 13) {
if (this.searchTxt === ':debug:') {
log('----- 调试模式 -----')
this.searchTxt = ''
WIN.openDevTools()
}
}
},
__draw__() {
let play = this.isPlaying
let rx = (play ? 112 : 40) + this.__HEIGHT__ / 2 // 旋转唱片的圆心坐标X
let ry = this.__HEIGHT__ / 2 // 旋转唱片的圆心坐标Y
let pw = this.__WIDTH__ - this.__HEIGHT__ - 180 // 进度条总长度
let wl = this.__HEIGHT__ + 180 // 文字的坐标X
let { time, duration, title, artist } = this.curr
let lrc = this.ctrlLrc
let pp = time / duration // 进度百分比
time = Anot.filters.time(time)
duration = Anot.filters.time(duration)
this.__CTX__.clearRect(0, 0, this.__WIDTH__, this.__HEIGHT__)
this.__CTX__.save()
// 将原点移到唱片圆心, 旋转完再回到初始值
this.__CTX__.translate(rx, ry)
this.__CTX__.rotate(this.__DEG__ * Math.PI)
this.__CTX__.translate(-rx, -ry)
this.__CTX__.drawImage(
this.__img1__,
play ? 112 : 40,
0,
this.__HEIGHT__,
this.__HEIGHT__
)
this.__CTX__.restore()
this.__CTX__.drawImage(
this.__img2__,
0,
0,
this.__HEIGHT__,
this.__HEIGHT__
)
// 歌曲标题和歌手
this.__CTX__.fillStyle = COLORS[this.ktvMode].title
this.__CTX__.font = '56px' + FONTS_NAME
this.__CTX__.fillText(`${title} - ${artist}`, wl, 100)
// 时间
this.__CTX__.fillStyle = COLORS[this.ktvMode].lrc
this.__CTX__.font = '48px' + FONTS_NAME
this.__CTX__.fillText(`${time} / ${duration}`, this.__WIDTH__ - 280, 100)
// 歌词
this.__CTX__.fillStyle = COLORS[this.ktvMode].lrc
this.__CTX__.font = '48px' + FONTS_NAME
this.__CTX__.fillText(lrc, wl, 180)
// 进度条
this.__CTX__.fillStyle = COLORS[this.ktvMode].bar1
this.__CTX__.fillRect(wl, 230, pw, 16)
this.__CTX__.fillStyle = COLORS[this.ktvMode].bar2
this.__CTX__.fillRect(wl, 230, pw * pp, 16)
this.__DEG__ += 0.01
},
draw(force) {
if (force) {
this.__img1__ = new Image()
this.__img2__ = new Image()
let p1 = Promise.defer()
let p2 = Promise.defer()
this.__img1__.onload = p1.resolve
this.__img2__.onload = p2.resolve
this.__img1__.src = '/images/disk.png'
this.__img2__.src = this.curr.cover || '/images/album.png'
Promise.all([p1.promise, p2.promise]).then(_ => {
clearInterval(this.timer)
this.__DEG__ = 0.01
if (this.isPlaying) {
this.timer = setInterval(_ => {
this.__draw__()
}, 20)
} else {
this.__draw__()
}
})
} else {
clearInterval(this.timer)
if (this.isPlaying) {
this.timer = setInterval(_ => {
this.__draw__()
}, 20)
} else {
this.__draw__()
}
}
},
nextSong(step) {
let _p = null
if (step > 0) {
_p = SONIST.next()
} else {
_p = SONIST.prev()
}
this.isPlaying = false
_p.then(it => {
if (this.mod === 'local') {
Local.__updateSong__(it)
}
// 通知子模块歌曲已经改变
this.$fire('child!curr', it.id)
this.play(it)
})
},
pause() {
this.isPlaying = false
},
updateCurr(obj) {
let old = this.curr.$model
this.curr = Object.assign(old, obj)
},
play(song) {
// 有参数的,说明是播放回调通知
// 此时仅更新播放控制条的信息即可
if (song) {
song.time = 0
this.ctrlLrc = '暂无歌词...'
this.updateCurr(song)
this.isPlaying = true
this.draw(true)
LYRICS.__init__(song.lyrics)
} else {
if (SONIST.stat === 'ready') {
let played = this.isPlaying
this.isPlaying = !this.isPlaying
if (this.curr.id) {
if (played) {
SONIST.pause()
} else {
SONIST.play()
}
this.draw()
} else {
let lastPlay = Anot.ls('last-play') || 0
SONIST.play(lastPlay).then(it => {
it.time = 0
this.ctrlLrc = '暂无歌词...'
this.updateCurr(it)
this.draw(true)
// this.ktvMode = 1
LYRICS.__init__(it.lyrics)
})
}
}
}
},
...KTV.methods
} }
}) })

View File

@ -6,34 +6,41 @@
'use strict' 'use strict'
import '/lib/anot.next.js' import '/lib/anot.js'
const { remote } = require('electron') const { remote } = require('electron')
const WIN = remote.getCurrentWindow() const WIN = remote.getCurrentWindow()
const $doc = Anot(document)
const log = console.log
window.WIN = WIN
Anot({ Anot({
$id: 'lrc', $id: 'lrc',
state: { state: {
isMac: process.platform === 'darwin',
lrc: { lrc: {
l: { bg: '#fff', txt: '暂无歌词...' }, l: { bg: '#fff', txt: '暂无歌词...' },
r: { bg: '', txt: '' } r: { bg: '', txt: '' }
}, },
isLock: false isLock: +Anot.ls('lock-lrc')
}, },
mounted() { mounted() {
WIN.on('ktv-lrc', lrc => { WIN.on('ktv-lrc', lrc => {
this.lrc = lrc this.lrc = lrc
}) })
}, },
skip: ['isMac'],
methods: { methods: {
quit(force) { quit(force) {
// WIN.close()
WIN.hide() WIN.hide()
}, },
lock() { lock() {
WIN.setMovable(this.isLock) WIN.setMovable(!!this.isLock)
this.isLock = !this.isLock this.isLock = this.isLock ^ 1
Anot.ls('lock-lrc', this.isLock)
} }
} }
}) })

8
src/js/lib/anot.js Normal file

File diff suppressed because one or more lines are too long

177
src/js/lib/audio/index.js Normal file
View File

@ -0,0 +1,177 @@
/**
*
* @author yutent<yutent.io@gmail.com>
* @date 2020/11/19 17:32:19
*/
import $ from '../utils.js'
import fetch from '../fetch/index.js'
import Events from '../events.js'
const AC = new AudioContext()
function hide(target, key, value) {
Object.defineProperty(target, key, {
value,
writable: true,
enumerable: false,
configurable: true
})
}
class AudioTrack extends Events {
constructor(elem) {
super()
this._el = elem
this.gain = AC.createGain()
this._track = AC.createMediaElementSource(elem)
this._track.connect(this.gain)
this.gain.connect(AC.destination)
this.__playFn = $.bind(elem, 'timeupdate', _ => {
this.$emit('play', elem.currentTime)
})
this.__stopFn = $.bind(elem, 'ended', _ => {
this.$emit('stop')
})
}
set volume(val) {
if (this.gain) {
this.gain.gain.value = val
}
}
destroy() {
$.unbind(this._el, 'timeupdate', this.__playFn)
$.unbind(this._el, 'ended', this.__stopFn)
this.removeAllListeners()
this._track.disconnect()
// this._track.disconnect(AC.destination)
this._el.src = ''
this._el.currentTime = 0
this._el.pause()
delete this.__playFn
delete this.__stopFn
delete this._el
delete this.gain
delete this._track
}
}
export default class Player extends Events {
constructor() {
super()
hide(this, '__LIST__', [])
hide(this, 'props', {
curr: '',
stat: 'ready',
volume: 0.5,
duration: 0
})
hide(this, 'track', null)
}
load(list) {
this.stop()
this.__LIST__ = list
}
async _getTrack(file) {
hide(this, '__AUDIO__', new Audio())
this.__AUDIO__.src = URL.createObjectURL(await fetch(file).then(r => r.blob()))
this.track = new AudioTrack(this.__AUDIO__)
this.track.volume = this.props.volume
this.track.$on('play', t => this.$emit('play', t))
this.track.$on('stop', _ => this.$emit('stop'))
}
get volume() {
return this.props.volume
}
set volume(val) {
val = +val
if (val < 0) {
val = 0
}
if (val > 1) {
val = 1
}
this.props.volume = val
if (this.track) {
this.track.volume = val
}
}
get time() {
return this.__AUDIO__.currentTime
}
get stat() {
return this.props.stat
}
/**
* id: 歌曲序号
* force: 强制重新播放
*/
async play(id, force = false) {
if (id === -1) {
if (this.track) {
if (force) {
this.seek(0)
this.props.stat = 'paused'
}
if (this.stat === 'playing') {
this.__AUDIO__.pause()
this.props.stat = 'paused'
} else if (this.stat === 'paused') {
this.__AUDIO__.play()
this.props.stat = 'playing'
}
}
} else {
var url = this.__LIST__[id]
if (!url || this.props.curr === url) {
return
}
if (this.track) {
this.stop()
}
this.props.curr = url
await this._getTrack(url)
this.__AUDIO__.play()
this.props.stat = 'playing'
}
}
seek(time) {
if (this.track) {
this.__AUDIO__.currentTime = time
}
}
stop() {
if (this.track) {
this.track.destroy()
this.props.stat = 'stoped'
}
}
}

1
src/js/lib/drag/core.js Normal file
View File

@ -0,0 +1 @@
import e from"../utils.js";const c={axis:"",limit:!1,overflow:!0};class g{constructor(t){this.$elem=t,this._init()}_init(){this.$elem.style.transform="";var{x:t,y:n}=this.$elem.getBoundingClientRect();this.pos={x:t,y:n,_x:0,_y:0}}by(t,n={}){return this.$drag=t,this.opt=Object.assign(Object.create(null),c,n),this.opt.limit!==!1&&(this.opt.overflow=!1),t.style.cursor="move",this._handleResize=e.bind(window,"resize",this._init.bind(this)),this._handleMousedown=e.bind(t,"mousedown",p=>{if(this.disabled)return;var l=this.$elem.getBoundingClientRect();l.x-this.pos._x!==this.pos.x&&(this.pos.x=l.x-this.pos._x),l.y-this.pos._y!==this.pos.y&&(this.pos.y=l.y-this.pos._y);let r=p.pageX,a=p.pageY,u=document.documentElement.clientWidth,f=document.documentElement.clientHeight,d=l.width,m=l.height,s=[0,u-d,f-m,0];if(this.opt.limit==="parent"){let i=this.$elem.parentNode.getBoundingClientRect();s=[i.top,i.right-d,i.bottom-m,i.left]}let x=e.bind(document,"mousemove",i=>{i.preventDefault();let o=i.pageX-r+(l.x-this.pos.x),h=i.pageY-a+(l.y-this.pos.y);this.opt.axis==="x"&&(h=0),this.opt.axis==="y"&&(o=0),this.opt.overflow===!1&&(o<s[3]-this.pos.x?o=s[3]-this.pos.x:o>s[1]-this.pos.x&&(o=s[1]-this.pos.x),h<s[0]-this.pos.y?h=s[0]-this.pos.y:h>s[2]-this.pos.y&&(h=s[2]-this.pos.y)),this.pos._x=o,this.pos._y=h,this.$elem.dispatchEvent(new CustomEvent("dragging",{detail:{offset:{x:this.pos.x+o,y:this.pos.y+h},move:{x:o,y:h}}})),this.$elem.style.transform=`translate(${o}px, ${h}px)`}),y=e.bind(document,"mouseup",i=>{this.$elem.dispatchEvent(new CustomEvent("dragged",{detail:{offset:{x:this.pos.x+this.pos._x,y:this.pos.y+this.pos._y},move:{x:this.pos._x,y:this.pos._y}}})),e.unbind(document,"mousemove",x),e.unbind(document,"mouseup",y)})}),this}on(t,n){if(!(!t||typeof n!="function"))return e.bind(this,t,n)}off(t,n){e.unbind(this,t,n)}destroy(){e.unbind(window,"resize",this._handleResize),e.unbind(this.$drag,"mousedown",this._handleMousedown),delete this.$elem,delete this.$drag}}export{g as default};

1
src/js/lib/drag/index.js Normal file
View File

@ -0,0 +1 @@
import a from"./core.js";Anot.directive("drag",{priority:1500,init:function(e){e.expr='"'+e.expr+'"',e.overflow=!0,e.axis="xy",e.element.dataset.axis&&(e.axis=e.element.dataset.axis,delete e.element.dataset.axis),e.limit=!1,e.element.dataset.limit&&(e.limit=e.element.dataset.limit,e.overflow=!1,delete e.element.dataset.limit)},update:function(e){var t=this.element;if(e)for(t=this.element.parentNode;t;){if(t.classList||Anot.error(`${this.name}=${this.expr}, \u89E3\u6790\u5F02\u5E38[\u5143\u7D20\u4E0D\u5B58\u5728]`),t.tagName==="WC-LAYER"&&e==="layer"){t=t.root.children[1];break}if(t.classList.contains(e)||t.id===e)break;t=t.parentNode}new a(t).by(this.element,{limit:this.limit,axis:this.axis,overflow:this.overflow})}});

38
src/js/lib/events.js Normal file
View File

@ -0,0 +1,38 @@
export default class EventEmitter {
//
#events = Object.create(null)
$on(name, fn) {
if (this.#events[name]) {
this.#events[name].push(fn)
} else {
this.#events[name] = [fn]
}
}
$off(name, fn) {
if (this.#events[name]) {
if (fn) {
this.#events[name] = this.#events[name].filter(it => it !== fn)
} else {
this.#events[name] = []
}
}
}
$emit(name, ...args) {
if (this.#events[name]) {
for (let fn of this.#events[name]) {
try {
fn.apply(this, args)
} catch (e) {
console.error(e)
}
}
}
}
$destroy() {
this.#events = Object.create(null)
}
}

View File

@ -0,0 +1 @@
import{Format,toS}from"./lib/format.js";const noop=function(e,t){this.defer.resolve(t)},NOBODY_METHODS=["GET","HEAD"],FORM_TYPES={form:"application/x-www-form-urlencoded; charset=UTF-8",json:"application/json; charset=UTF-8",text:"text/plain; charset=UTF-8"},ERRORS={10001:"Argument url is required",10012:"Parse error",10100:"Request canceled",10104:"Request pending...",10200:"Ok",10204:"No content",10304:"Not modified",10500:"Internal Server Error",10504:"Connected timeout"};Promise.defer=function(){var e={};return e.promise=new Promise(function(t,s){e.resolve=t,e.reject=s}),e};class _Request{constructor(e="",t={},{BASE_URL:s,__INIT__:r}){if(!e)throw new Error(ERRORS[10001]);if(e=e.replace(/#.*$/,""),s&&(/^([a-z]+:|\/\/)/.test(e)||(e=s+e)),t.method=(t.method||"get").toUpperCase(),this.xhr=new XMLHttpRequest,this.defer=Promise.defer(),this.options={headers:{"X-Requested-With":"XMLHttpRequest","content-type":FORM_TYPES.form},body:null,cache:"default",credentials:!1,signal:null,timeout:3e4},!t.signal){var o=new AbortController;t.signal=o.signal}this.defer.promise.abort=function(){o.abort()};var i=this.options.headers;return r.headers&&Object.assign(i,r.headers),t.headers&&(Object.assign(i,t.headers),delete t.headers),Object.assign(this.options,r,t,{url:e,headers:i}),this.__next__(),this.defer.promise}__next__(){var e=this.options,t=null,s=!1,r=!1,o=NOBODY_METHODS.includes(e.method);if(e.signal.onabort=(e=>{this.cancel=!0,this.xhr.abort()}),e.body)switch(typeof e.body){case"number":case"string":this.__type__("text"),t=e.body;break;case"object":if("FORM"===e.body.nodeName)e.method=e.body.method.toUpperCase()||"POST",s=(t=Format.parseForm(e.body)).constructor===FormData;else if(e.body.constructor===FormData)s=!0,o&&(e.method="POST"),t=e.body;else{for(let t in e.body)if("[object File]"===toS.call(e.body[t])){s=!0;break}s?(o&&(e.method="POST"),t=Format.mkFormData(e.body)):t=e.body}}s&&delete e.headers["content-type"];try{let t=document.createElement("a");t.href=e.url,r=location.protocol!==t.protocol||location.host!==t.host}catch(e){}r&&(e.credentials?this.xhr.withCredentials=!0:delete e.headers["X-Requested-With"]),o?((t=Format.param(t))&&(e.url+=(~e.url.indexOf("?")?"&":"?")+t),"no-store"===e.cache&&(e.url+=(~e.url.indexOf("?")?"&":"?")+"_t_="+Date.now())):s||(t=~e.headers["content-type"].indexOf("json")?JSON.stringify(t):Format.param(t)),this.xhr.responseType="blob",this.xhr.onreadystatechange=(t=>{e.timeout>0&&(e["time"+this.xhr.readyState]=t.timeStamp,4===this.xhr.readyState&&(e.isTimeout=e.time4-e.time1>e.timeout)),4===this.xhr.readyState&&this.__dispatch__(e.isTimeout)}),this.xhr.open(e.method,e.url);for(let t in e.headers)this.xhr.setRequestHeader(t,e.headers[t]);this.xhr.send(t),e.timeout&&e.timeout>0&&(this.xhr.timeout=e.timeout)}__type__(e){this.options.headers["content-type"]=FORM_TYPES[e]}__dispatch__(e){let t={status:200,statusText:"ok",body:"",headers:Object.create(null)};if(this.cancel)return this.__cancel__();if(e)return this.__timeout__();let s=this.xhr.status>=200&&this.xhr.status<400,r=this.xhr.getAllResponseHeaders().split("\n")||[];for(let e of r)if(e=e.trim()){let s=(e=e.split(":")).shift().toLowerCase();e=e.join(":").trim(),t.headers[s]=e}s?(t.status=this.xhr.status,204===t.status?t.statusText=ERRORS[10204]:304===t.status&&(t.statusText=ERRORS[10304])):(t.status=this.xhr.status||500,t.statusText=this.xhr.statusText||ERRORS[10500]),t.body=this.xhr.response,this.__success__(s,t)}__success__(e,t){var s=new _Response(t.status,t.statusText,t.body,t.headers);e?this.defer.resolve(s):this.defer.reject(s),delete this.xhr,delete this.options,delete this.defer}__cancel__(e){var t=new _Response(0,ERRORS[10100],Object.create(null));this.defer.reject(t),delete this.xhr,delete this.options,delete this.defer}__timeout__(e){var t=new _Response(504,ERRORS[10504],Object.create(null));this.defer.reject(t),delete this.xhr,delete this.options,delete this.defer}}class _Response{constructor(e=200,t="OK",s=null,r={}){this.status=e,this.statusText=t,this.ok=e>=200&&e<400,this.headers=r,Object.defineProperty(this,"__R__",{value:s,writable:!0,enumerable:!1,configurable:!0})}text(){return this.__R__.text()}json(){return this.__R__.text().then(e=>JSON.parse(e))}blob(){return this.__R__}arrayBuffer(){return this.__R__.arrayBuffer()}}const _fetch=function(e,t){return new _Request(e,t,{BASE_URL:_fetch.BASE_URL,__INIT__:_fetch.__INIT__||Object.create(null)})};_fetch.create=function(e,t=Object.create(null)){return function(s,r){return new _Request(s,r,{BASE_URL:e,__INIT__:t})}};export default _fetch;

View File

@ -0,0 +1 @@
export const toS=Object.prototype.toString;export const encode=encodeURIComponent;export const decode=decodeURIComponent;function serialize(e,t,o){var a;if(Array.isArray(t))t.forEach(function(t,r){a=e?`${e}[${Array.isArray(t)?r:""}]`:r,"object"==typeof t?serialize(a,t,o):o(a,t)});else for(let r in t)a=e?`${e}[${r}]`:r,"object"==typeof t[r]?serialize(a,t[r],o):o(a,t[r])}export const Format={parseForm(e){let t={},o=!1;for(let a,r=0;a=e.elements[r++];)switch(a.type){case"select-one":case"select-multiple":if(a.name.length&&!a.disabled)for(let e,o=0;e=a.options[o++];)e.selected&&(t[a.name]=e.value||e.text);break;case"file":a.name.length&&!a.disabled&&(t[a.name]=a.files[0],o=!0);break;case void 0:case"submit":case"reset":case"button":break;case"radio":case"checkbox":if(!a.checked)break;default:a.name.length&&!a.disabled&&(t[a.name]=a.value)}return o?this.mkFormData(t):t},mkFormData(e){let t=new FormData;for(let o in e){let a=e[o];Array.isArray(a)?a.forEach(function(e){t.append(o+"[]",e)}):t.append(o,e[o])}return t},param(e){if(!e||"string"==typeof e||"number"==typeof e)return e;let t=[];return"object"==typeof e&&serialize("",e,function(e,o){/native code/.test(o)||(o="function"==typeof o?o():o,o="[object File]"===toS.call(o)?o:encode(o),t.push(encode(e)+"="+o))}),t.join("&")}};

Some files were not shown because too many files have changed in this diff Show More