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

完成在线搜索功能

2.x
宇天 2019-01-30 17:27:43 +08:00
parent 19824ebc3e
commit a575d7e3e5
23 changed files with 204 additions and 70 deletions

View File

@ -26,6 +26,7 @@
- [x] 歌曲ID3信息修改
- [x] 酷狗歌手列表(完成20%)
- [x] 歌词编辑
- [x] 音乐在线搜索()
- [ ] 酷狗音乐排行榜
- [ ] 酷狗音乐MV
- [ ] 试听列表

View File

@ -1,6 +1,6 @@
{
"name": "sonist",
"version": "0.9.6",
"version": "1.0.0",
"description": "Music Player",
"main": "src/main.js",
"scripts": {

File diff suppressed because one or more lines are too long

View File

@ -8,7 +8,7 @@
@import "./var.scss";
.do-mod-app {position:relative;display:flex;flex-flow:column wrap;width:100%;height:100%;background:#fff;
#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;

File diff suppressed because one or more lines are too long

View File

@ -42,6 +42,9 @@ table {overflow:auto;display:table;width:100%;line-height:2.5rem;
&:hover {background:rgba(29, 35, 44, .08);}
}
tbody td {padding:.9rem .8rem}
.ac {text-align:center}
.idx {color:nth($cp, 3);text-shadow:0 .1rem 0 rgba(255, 255, 255, 0.6)}
.active {color:nth($ct, 1);background:rgba(255, 255, 255, 0.6);font-weight:bold}
}
::-webkit-scrollbar {width:.6rem;height:.6rem;background:none;}

View File

@ -1 +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}
#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}

View File

@ -9,7 +9,7 @@
@import "./var.scss";
.app {display:flex;width:100%;height:100%;
#app {display:flex;width:100%;height:100%;
.cover {overflow:hidden;flex:0 6rem;box-shadow:0 0 .1rem rgba(0, 0, 0, .1);

File diff suppressed because one or more lines are too long

View File

@ -83,13 +83,7 @@
}
}
.table {overflow:auto;flex:1;
.stat {width:2.6rem;height:2.6rem;line-height:2.6rem;}
.ac {text-align:center}
.idx {color:nth($cp, 3);text-shadow:0 .1rem 0 rgba(255, 255, 255, 0.6)}
.active {color:nth($ct, 1);background:rgba(255, 255, 255, 0.6);font-weight:bold}
}
.table {overflow:auto;flex:1;}
.edit-form {position:absolute;left:0;top:0;z-index:90;display:flex;justify-content:center;align-items:center;width:100%;height:100%;
@ -125,21 +119,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;
.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)}
}
}
.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);}
}
}
}
}

View File

@ -12,7 +12,7 @@
</head>
<body class="do-fn-noselect" anot="app" :css="{'background-image': coverBG}">
<div class="do-mod-app" :class="{blur: isPlaying && !ktvMode, ktv: ktvMode}">
<div id="app" :class="{blur: isPlaying && !ktvMode, ktv: ktvMode}">
<div class="title-bar do-fn-drag">

View File

@ -4,7 +4,7 @@
* @date 2018/12/16 17:15:57
*/
import '/lib/anot.next.js'
import '/lib/anot.js'
import layer from '/lib/layer/index.js'
import store from '/lib/store/index.js'
import AudioPlayer from '/lib/audio/index.js'
@ -47,13 +47,15 @@ let appInit = ipcRenderer.sendSync('get-init')
Anot.ss('app-init', appInit)
SONIST.target = 'local' //播放目标是本地音乐
Anot({
$id: 'app',
state: {
theme: appInit.theme || 1, // 1:macos, 2: deepin
winFocus: false,
mod: 'search',
searchTxt: '安羽苏',
mod: 'local',
searchTxt: '',
playMode: Anot.ls('play-mode') >>> 0, // 0:all | 1:single | 2:random
ktvMode: 0,
isPlaying: false,

View File

@ -6,7 +6,7 @@
'use strict'
import '/lib/anot.next.js'
import '/lib/anot.js'
const { remote } = require('electron')

View File

@ -6,7 +6,7 @@
'use strict'
import '/lib/anot.next.js'
import '/lib/anot.js'
const { remote, ipcRenderer } = require('electron')

View File

@ -79,6 +79,9 @@ export default {
if (json.lyrics) {
let { id } = SONIST.getCurrSong()
// 本地音乐才需要更新一些额外字段
// 试听列表的音乐, 只更新歌词
if (SONIST.target === 'local') {
let song = LS.get(id)
song.album = json.album_name
@ -92,12 +95,13 @@ export default {
SONIST.clear()
SONIST.push(LS.getAll())
this.updateCurr(song)
this.draw(true)
ipcRenderer.send('save-lrc', { id, lrc: json.lyrics })
ipcRenderer.send('set-music', LS.getAll())
this.updateCurr(song)
this.draw(true)
}
ipcRenderer.send('save-lrc', { id, lrc: json.lyrics })
LYRICS.__init__(id)
layer.toast('歌词应用成功...')

View File

@ -61,6 +61,14 @@ export default Anot({
if (song.id === this.curr) {
return
}
// 如果之前不是本地播放, 则将播放列表清空再切为本地音乐
if (SONIST.target === 'temp') {
SONIST.target = 'local'
SONIST.clear()
SONIST.push(LS.getAll())
}
SONIST.play(idx).then(it => {
this.__APP__.play(it)
this.curr = it.id

View File

@ -8,6 +8,8 @@
import Api from '/js/api.js'
const { ipcRenderer } = require('electron')
const log = console.log
const dict = {}
@ -15,17 +17,103 @@ const dict = {}
export default Anot({
$id: 'search',
state: {
curr: '',
tab: 'audition',
curr: '', //当前播放
history: [], // 搜索历史
list: [] // 搜索结果列表
},
mounted() {
dict.audition = ipcRenderer.sendSync('get-temp')
TS.insert(dict.audition)
this.__APP__ = Anot.vmodels.app
this.__init__()
},
methods: {
__init__() {},
play(it, idx) {
// SONIST.clear()
log(it.hash, it.albumId)
Api.getSongInfoByHash(it.hash, it.albumId).then(json => {
__init__() {
if (!this.list.length) {
this.list.pushArray(dict.audition)
}
},
play(item, idx) {
let song = item.$model
// 如果之前是本地播放, 则将播放列表清空再切为试听列表
if (SONIST.target === 'local') {
SONIST.target = 'temp'
SONIST.clear()
SONIST.push(TS.getAll())
}
/**------------------------------
* 在试听列表
------------------------------- */
if (this.tab === 'audition') {
if (song.id === this.curr) {
return
}
return SONIST.play(idx).then(it => {
this.__APP__.play(it)
this.curr = it.id
})
}
/**------------------------------
* 在搜索列表
------------------------------- */
// 避免重复增加
if (TS.get(song.kgHash)) {
if (song.kgHash === this.curr) {
return
}
// 找到索引id
for (let i = 0; i < dict.audition.length; i++) {
if (dict.audition[i].id === song.kgHash) {
idx = i
break
}
}
return SONIST.play(idx).then(it => {
this.__APP__.play(it)
this.curr = it.id
})
}
song.id = song.kgHash
Api.getSongInfoByHash(song.kgHash, song.albumId).then(json => {
log(json)
song.cover = json.img
ipcRenderer.send('save-lrc', { id: song.id, lrc: json.lyrics })
fetch(json.play_url)
.then(res => {
return res.arrayBuffer()
})
.then(blob => {
song.path = ipcRenderer.sendSync('save-cache', {
buff: Buffer.from(blob),
file: song.kgHash
})
log(song)
TS.insert(song)
dict.audition.push(song)
SONIST.push([song])
SONIST.play(dict.audition.length - 1).then(it => {
this.__APP__.play(it)
this.curr = it.id
})
ipcRenderer.send('set-temp', TS.getAll())
})
})
},
delThis(it, ev) {
@ -33,7 +121,7 @@ export default Anot({
delete dict[it.key]
},
toggleHistory(it) {
this.curr = it.key
this.tab = it.key
this.list.clear()
this.list.pushArray(dict[it.key])
},
@ -45,13 +133,16 @@ export default Anot({
let load = layer.load(1)
this.list.clear()
let key = Buffer.from(txt).toString('base64')
this.history.push({ txt, key })
this.curr = key
Api.search(txt, 1, 50).then(list => {
layer.close(load)
if (!Array.isArray(list) || list.length < 1) {
return layer.toast(`没有找到有关[${txt}]的音乐`)
}
let key = Buffer.from(txt).toString('base64')
this.history.push({ txt, key })
this.tab = key
dict[key] = list.map(it => {
return {
title: it.SongName,
@ -59,10 +150,10 @@ export default Anot({
album: it.AlbumName,
albumId: it.AlbumID,
duration: it.Duration,
hash: it.FileHash
kgHash: it.FileHash
}
})
layer.close(load)
this.list.pushArray(dict[key])
})
}

8
src/lib/anot.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -122,7 +122,7 @@ class AudioPlayer {
play(id) {
// 播放列表里没有数据的话, 不作任何处理
if (!this.__LIST__.length) {
return
return Promise.reject(this.__LIST__)
}
// 有ID的话,不管之前是否在播放,都切换歌曲

View File

@ -10,7 +10,7 @@
<script type="module" src="js/mini-win.js"></script>
</head>
<body class="do-fn-noselect" anot="mini">
<div class="app">
<div id="app">
<span class="cover do-fn-drag">
<img :attr-src="curr.cover || '/images/album.png'" />
</span>

View File

@ -8,6 +8,7 @@
const { app, ipcMain } = require('electron')
const path = require('path')
// const http = require('http')
const fs = require('iofs')
/* ********** 修复环境变量 start *********** */
@ -30,12 +31,14 @@ const LRC_DIR = path.join(APP_ROOT, 'lyrics')
const CACHE_DIR = path.join(APP_ROOT, 'cache')
const INIT_FILE = path.join(APP_ROOT, 'app.ini')
const DB_FILE = path.join(APP_ROOT, 'music.db')
const TEMP_DB = path.join(APP_ROOT, 'temp.db')
if (!fs.exists(APP_ROOT)) {
fs.mkdir(APP_ROOT)
fs.mkdir(LRC_DIR)
fs.mkdir(CACHE_DIR)
fs.echo('{}', INIT_FILE)
fs.echo('[]', TEMP_DB)
fs.echo('[]', DB_FILE)
}
const SUPPORTED_EXTS = ['.mp3', '.webm', '.ogg', '.flac', '.m4a', '.aac']
@ -68,6 +71,18 @@ ipcMain.on('set-music', (ev, val) => {
fs.echo(JSON.stringify(val), DB_FILE)
})
// 获取临时音乐数据库
ipcMain.on('get-temp', (ev, val) => {
let cache = (fs.cat(TEMP_DB) || '[]').toString('utf-8')
cache = JSON.parse(cache)
ev.returnValue = cache
})
// 更新临时音乐数据库
ipcMain.on('set-temp', (ev, val) => {
fs.echo(JSON.stringify(val), TEMP_DB)
})
/**
* 保存歌词文件
*/
@ -91,7 +106,9 @@ ipcMain.on('read-lrc', (ev, id) => {
* 保存音乐文件
*/
ipcMain.on('save-cache', (ev, obj) => {
fs.echo(obj.data, path.join(CACHE_DIR, obj.file))
let savefile = path.join(CACHE_DIR, obj.file)
fs.echo(obj.buff, savefile)
ev.returnValue = `file://${savefile}`
})
/**

View File

@ -1,11 +1,17 @@
<div class="do-mod-search" anot="search">
<div class="tabbar">
<span class="item"
:class="{active: tab === 'audition'}"
:click="toggleHistory({ key: 'audition' })">
试听列表
</span>
<span
class="item"
:class="{active: curr === it.key}"
:class="{active: tab === it.key}"
:click="toggleHistory(it)"
:for="it in history">
{{it.txt}} <i class="do-icon-close" :click="delThis(it, $event)"></i>
:for="it in history"
:attr-title="it.txt">
{{it.txt | truncate(3)}} <i class="do-icon-close" :click="delThis(it, $event)"></i>
</span>
</div>
@ -23,8 +29,9 @@
<tbody>
<tr
:for="it in list"
:class="{active: it.kgHash === curr}"
:dblclick="play(it, $index)">
<td :text="$index + 1"></td>
<td class="idx" :text="$index + 1 + '.'"></td>
<td :text="it.title"></td>
<td class="ac" :text="it.artist"></td>
<td class="ac" :text="it.album"></td>