重构音频播放
parent
6bd72fcbac
commit
b21ec91170
|
@ -9,12 +9,19 @@ import '/js/lib/scroll/index.js'
|
||||||
|
|
||||||
import Keyboard from '/js/lib/keyboard/index.js'
|
import Keyboard from '/js/lib/keyboard/index.js'
|
||||||
import app from '/js/lib/socket.js'
|
import app from '/js/lib/socket.js'
|
||||||
|
import fetch from '/js/lib/fetch/index.js'
|
||||||
|
|
||||||
|
import Player from '/js/lib/audio/index.js'
|
||||||
|
|
||||||
// const id3 = require('jsmediatags')
|
|
||||||
const id3 = require('music-metadata')
|
const id3 = require('music-metadata')
|
||||||
|
|
||||||
var kb = new Keyboard()
|
var kb = new Keyboard()
|
||||||
|
|
||||||
|
var player = new Player()
|
||||||
|
|
||||||
|
window.fetch = fetch
|
||||||
|
window.player = player
|
||||||
|
|
||||||
Anot({
|
Anot({
|
||||||
$id: 'app',
|
$id: 'app',
|
||||||
state: {
|
state: {
|
||||||
|
@ -52,6 +59,8 @@ Anot({
|
||||||
it.duration = duration
|
it.duration = duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
player.load(list.map(it => `sonist://${it.path}`))
|
||||||
|
|
||||||
kb.on(['left'], ev => {
|
kb.on(['left'], ev => {
|
||||||
var time = this.song.time - 5
|
var time = this.song.time - 5
|
||||||
if (time < 0) {
|
if (time < 0) {
|
||||||
|
@ -68,6 +77,12 @@ Anot({
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
load() {
|
||||||
|
// window.player = new Howl({
|
||||||
|
// src: [`sonist://${this.list[0].path}`]
|
||||||
|
// })
|
||||||
|
// window.player = this.__PLAYER__
|
||||||
|
},
|
||||||
play() {
|
play() {
|
||||||
this.isplaying = !this.isplaying
|
this.isplaying = !this.isplaying
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,214 +1,126 @@
|
||||||
/**
|
/**
|
||||||
* 播放器
|
*
|
||||||
* @author yutent<yutent@doui.cc>
|
* @author yutent<yutent.io@gmail.com>
|
||||||
* @date 2018/12/23 23:14:40
|
* @date 2020/11/19 17:32:19
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict'
|
import fetch from '../fetch/index.js'
|
||||||
|
|
||||||
const { exec } = require('child_process')
|
function hide(target, key, value) {
|
||||||
const { EventEmitter } = require('events')
|
Object.defineProperty(target, key, {
|
||||||
const util = require('util')
|
value,
|
||||||
const path = require('path')
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
class AudioPlayer {
|
export default class Player {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.__PLAYER__ = new Audio()
|
hide(this, '__LIST__', [])
|
||||||
this.__IS_PLAYED__ = false
|
hide(this, '__AC__', new AudioContext())
|
||||||
this.__CURR__ = -1 // 当前播放的歌曲的id
|
hide(this, '__AUDIO__', new Audio())
|
||||||
this.__LIST__ = [] //播放列表
|
hide(this, 'props', {
|
||||||
this.__PLAY_MODE__ = 'all' // all | single | random
|
curr: '',
|
||||||
this.__PLAYER__.volume = 0.7
|
stat: 'ready',
|
||||||
|
volume: 0.5,
|
||||||
this.__init__()
|
mode: 'all', // 循环模式, all, single, rand
|
||||||
|
time: 0,
|
||||||
|
duration: 0
|
||||||
|
})
|
||||||
|
hide(this, 'track', null)
|
||||||
}
|
}
|
||||||
|
|
||||||
__init__() {
|
load(list) {
|
||||||
this.__PLAYER__.addEventListener(
|
this.stop()
|
||||||
'timeupdate',
|
this.__LIST__ = list
|
||||||
_ => {
|
}
|
||||||
this.emit('play', this.__PLAYER__.currentTime)
|
|
||||||
},
|
async _getTrack(file) {
|
||||||
false
|
this.__AUDIO__.src = URL.createObjectURL(
|
||||||
)
|
await fetch(file).then(r => r.blob())
|
||||||
|
|
||||||
this.__PLAYER__.addEventListener(
|
|
||||||
'ended',
|
|
||||||
_ => {
|
|
||||||
this.emit('end')
|
|
||||||
},
|
|
||||||
false
|
|
||||||
)
|
)
|
||||||
|
console.log(this.__AUDIO__)
|
||||||
|
return this.__AC__.createMediaElementSource(this.__AUDIO__)
|
||||||
}
|
}
|
||||||
|
|
||||||
get stat() {
|
get volume() {
|
||||||
return this.__LIST__.length ? 'ready' : 'stop'
|
return this.props.volume
|
||||||
}
|
|
||||||
|
|
||||||
get IS_MUTED() {
|
|
||||||
return this.__PLAYER__.muted
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set volume(val) {
|
set volume(val) {
|
||||||
this.__PLAYER__.muted = false
|
val = +val || 0.5
|
||||||
this.__PLAYER__.volume = val / 100
|
if (val < 0) {
|
||||||
|
val = 0
|
||||||
|
}
|
||||||
|
if (val > 1) {
|
||||||
|
val = 1
|
||||||
|
}
|
||||||
|
this.props.volume = val
|
||||||
}
|
}
|
||||||
|
|
||||||
set mode(val = 'all') {
|
get mode() {
|
||||||
this.__PLAY_MODE__ = val
|
return this.props.mode
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
set mode(val) {
|
||||||
this.__LIST__ = []
|
this.props.mode = val
|
||||||
}
|
}
|
||||||
|
|
||||||
push(songs) {
|
get time() {
|
||||||
this.__LIST__.push.apply(this.__LIST__, songs)
|
return this.props.time
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrSong() {
|
get stat() {
|
||||||
if (this.__CURR__ > -1) {
|
return this.props.stat
|
||||||
return this.__LIST__[this.__CURR__]
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上一首
|
async play(id) {
|
||||||
prev() {
|
var url, gain
|
||||||
let id = this.__CURR__
|
|
||||||
|
|
||||||
switch (this.__PLAY_MODE__) {
|
if (id === undefined) {
|
||||||
case 'all':
|
if (this.track) {
|
||||||
id--
|
if (this.stat === 'playing') {
|
||||||
if (id < 0) {
|
this.props.time = this.track.context.currentTime
|
||||||
id = this.__LIST__.length - 1
|
this.__AUDIO__.pause()
|
||||||
|
this.props.stat = 'paused'
|
||||||
|
} else if (this.stat === 'paused') {
|
||||||
|
this.__AUDIO__.play()
|
||||||
|
this.props.stat = 'playing'
|
||||||
}
|
}
|
||||||
break
|
|
||||||
case 'random':
|
|
||||||
id = (Math.random() * this.__LIST__.length) >>> 0
|
|
||||||
break
|
|
||||||
// single
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.play(id)
|
|
||||||
return Promise.resolve(this.__LIST__[id])
|
|
||||||
}
|
|
||||||
|
|
||||||
// 下一首
|
|
||||||
next() {
|
|
||||||
let id = this.__CURR__
|
|
||||||
|
|
||||||
switch (this.__PLAY_MODE__) {
|
|
||||||
case 'all':
|
|
||||||
id++
|
|
||||||
if (id >= this.__LIST__.length) {
|
|
||||||
id = 0
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'random':
|
|
||||||
id = (Math.random() * this.__LIST__.length) >>> 0
|
|
||||||
break
|
|
||||||
// single
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
this.play(id)
|
|
||||||
return Promise.resolve(this.__LIST__[id])
|
|
||||||
}
|
|
||||||
|
|
||||||
// 播放
|
|
||||||
play(id) {
|
|
||||||
// 播放列表里没有数据的话, 不作任何处理
|
|
||||||
if (!this.__LIST__.length) {
|
|
||||||
return Promise.reject(this.__LIST__)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 有ID的话,不管之前是否在播放,都切换歌曲
|
|
||||||
if (id !== undefined) {
|
|
||||||
let song = this.__LIST__[id]
|
|
||||||
if (song) {
|
|
||||||
this.__CURR__ = id
|
|
||||||
this.__IS_PLAYED__ = true
|
|
||||||
|
|
||||||
this.__PLAYER__.pause()
|
|
||||||
this.__PLAYER__.currentTime = 0
|
|
||||||
this.__PLAYER__.src = song.path
|
|
||||||
this.__PLAYER__.play()
|
|
||||||
|
|
||||||
Anot.ls('last-play', id)
|
|
||||||
return Promise.resolve(song)
|
|
||||||
}
|
|
||||||
return Promise.reject('song not found')
|
|
||||||
} else {
|
} else {
|
||||||
if (!this.__IS_PLAYED__) {
|
url = this.__LIST__[id]
|
||||||
this.__IS_PLAYED__ = true
|
if (!url || this.props.curr === url) {
|
||||||
this.__PLAYER__.play()
|
|
||||||
}
|
|
||||||
return Promise.resolve(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 暂停
|
|
||||||
pause() {
|
|
||||||
if (!this.__IS_PLAYED__) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.__IS_PLAYED__ = false
|
|
||||||
|
|
||||||
this.__PLAYER__.pause()
|
gain = this.__AC__.createGain()
|
||||||
|
|
||||||
|
if (this.track) {
|
||||||
|
this.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 切换静音
|
gain.gain.value = this.volume
|
||||||
mute() {
|
|
||||||
if (this.__CURR__ < 0) {
|
this.track = await this._getTrack(url)
|
||||||
return false
|
this.track.connect(gain).connect(this.__AC__.destination)
|
||||||
|
|
||||||
|
this.__AUDIO__.play()
|
||||||
|
this.props.stat = 'playing'
|
||||||
}
|
}
|
||||||
this.__PLAYER__.muted = !this.__PLAYER__.muted
|
|
||||||
return this.__PLAYER__.muted
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 跳到指定位置播放
|
|
||||||
seek(time) {
|
seek(time) {
|
||||||
if (this.__CURR__ < 0) {
|
this.props.time = time
|
||||||
return
|
|
||||||
}
|
|
||||||
this.__PLAYER__.currentTime = time
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(AudioPlayer, EventEmitter)
|
stop() {
|
||||||
|
if (this.track) {
|
||||||
export const ID3 = song => {
|
this.__AUDIO__.pause()
|
||||||
let cmd = `ffprobe -v quiet -print_format json -show_entries format "${song}"`
|
this.track = null
|
||||||
let pc = exec(cmd)
|
this.props.time = 0
|
||||||
let buf = ''
|
this.props.stat = 'stoped'
|
||||||
return new Promise((resolve, reject) => {
|
}
|
||||||
pc.stdout.on('data', _ => {
|
|
||||||
buf += _
|
|
||||||
})
|
|
||||||
|
|
||||||
pc.stderr.on('data', reject)
|
|
||||||
|
|
||||||
pc.stdout.on('close', _ => {
|
|
||||||
try {
|
|
||||||
let { format } = JSON.parse(buf)
|
|
||||||
let name = path.basename(song)
|
|
||||||
format.tags = format.tags || {}
|
|
||||||
resolve({
|
|
||||||
title: format.tags.TITLE || format.tags.title || name,
|
|
||||||
album: format.tags.ALBUM || format.tags.album || '',
|
|
||||||
artist: format.tags.ARTIST || format.tags.artist || '',
|
|
||||||
duration: Math.ceil(format.duration),
|
|
||||||
size: +(format.size / 1024 / 1024).toFixed(2)
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
reject(err)
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AudioPlayer
|
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
/**
|
||||||
|
* 播放器
|
||||||
|
* @author yutent<yutent@doui.cc>
|
||||||
|
* @date 2018/12/23 23:14:40
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const { EventEmitter } = require('events')
|
||||||
|
const util = require('util')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
class AudioPlayer {
|
||||||
|
constructor() {
|
||||||
|
this.__PLAYER__ = new Audio()
|
||||||
|
this.__IS_PLAYED__ = false
|
||||||
|
this.__CURR__ = -1 // 当前播放的歌曲的id
|
||||||
|
this.__LIST__ = [] //播放列表
|
||||||
|
this.__PLAY_MODE__ = 'all' // all | single | random
|
||||||
|
this.__PLAYER__.volume = 0.7
|
||||||
|
|
||||||
|
this.__init__()
|
||||||
|
}
|
||||||
|
|
||||||
|
__init__() {
|
||||||
|
this.__PLAYER__.addEventListener(
|
||||||
|
'timeupdate',
|
||||||
|
_ => {
|
||||||
|
this.emit('play', this.__PLAYER__.currentTime)
|
||||||
|
},
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
this.__PLAYER__.addEventListener(
|
||||||
|
'ended',
|
||||||
|
_ => {
|
||||||
|
this.emit('stop')
|
||||||
|
},
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
get stat() {
|
||||||
|
return this.__LIST__.length ? 'ready' : 'stop'
|
||||||
|
}
|
||||||
|
|
||||||
|
get IS_MUTED() {
|
||||||
|
return this.__PLAYER__.muted
|
||||||
|
}
|
||||||
|
|
||||||
|
set volume(val) {
|
||||||
|
this.__PLAYER__.muted = false
|
||||||
|
this.__PLAYER__.volume = val / 100
|
||||||
|
}
|
||||||
|
|
||||||
|
set mode(val = 'all') {
|
||||||
|
this.__PLAY_MODE__ = val
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.__LIST__ = []
|
||||||
|
}
|
||||||
|
|
||||||
|
push(songs) {
|
||||||
|
this.__LIST__.push.apply(this.__LIST__, songs)
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrSong() {
|
||||||
|
if (this.__CURR__ > -1) {
|
||||||
|
return this.__LIST__[this.__CURR__]
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上一首
|
||||||
|
prev() {
|
||||||
|
let id = this.__CURR__
|
||||||
|
|
||||||
|
switch (this.__PLAY_MODE__) {
|
||||||
|
case 'all':
|
||||||
|
id--
|
||||||
|
if (id < 0) {
|
||||||
|
id = this.__LIST__.length - 1
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'random':
|
||||||
|
id = (Math.random() * this.__LIST__.length) >>> 0
|
||||||
|
break
|
||||||
|
// single
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
this.play(id)
|
||||||
|
return Promise.resolve(this.__LIST__[id])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下一首
|
||||||
|
next() {
|
||||||
|
let id = this.__CURR__
|
||||||
|
|
||||||
|
switch (this.__PLAY_MODE__) {
|
||||||
|
case 'all':
|
||||||
|
id++
|
||||||
|
if (id >= this.__LIST__.length) {
|
||||||
|
id = 0
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'random':
|
||||||
|
id = (Math.random() * this.__LIST__.length) >>> 0
|
||||||
|
break
|
||||||
|
// single
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
this.play(id)
|
||||||
|
return Promise.resolve(this.__LIST__[id])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 播放
|
||||||
|
play(id) {
|
||||||
|
// 播放列表里没有数据的话, 不作任何处理
|
||||||
|
if (!this.__LIST__.length) {
|
||||||
|
return Promise.reject(this.__LIST__)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 有ID的话,不管之前是否在播放,都切换歌曲
|
||||||
|
if (id !== undefined) {
|
||||||
|
let song = this.__LIST__[id]
|
||||||
|
if (song) {
|
||||||
|
this.__CURR__ = id
|
||||||
|
this.__IS_PLAYED__ = true
|
||||||
|
|
||||||
|
this.__PLAYER__.pause()
|
||||||
|
this.__PLAYER__.currentTime = 0
|
||||||
|
this.__PLAYER__.src = song.path
|
||||||
|
this.__PLAYER__.play()
|
||||||
|
|
||||||
|
Anot.ls('last-play', id)
|
||||||
|
return Promise.resolve(song)
|
||||||
|
}
|
||||||
|
return Promise.reject('song not found')
|
||||||
|
} else {
|
||||||
|
if (!this.__IS_PLAYED__) {
|
||||||
|
this.__IS_PLAYED__ = true
|
||||||
|
this.__PLAYER__.play()
|
||||||
|
}
|
||||||
|
return Promise.resolve(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暂停
|
||||||
|
pause() {
|
||||||
|
if (!this.__IS_PLAYED__) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.__IS_PLAYED__ = false
|
||||||
|
|
||||||
|
this.__PLAYER__.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换静音
|
||||||
|
mute() {
|
||||||
|
if (this.__CURR__ < 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
this.__PLAYER__.muted = !this.__PLAYER__.muted
|
||||||
|
return this.__PLAYER__.muted
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳到指定位置播放
|
||||||
|
seek(time) {
|
||||||
|
if (this.__CURR__ < 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.__PLAYER__.currentTime = time
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
util.inherits(AudioPlayer, EventEmitter)
|
||||||
|
|
||||||
|
export default AudioPlayer
|
|
@ -1,85 +0,0 @@
|
||||||
# 拖拽插件
|
|
||||||
> 该插件可以让任意一个元素可以被拖拽,而不需要该元素是否具有定位属性。
|
|
||||||
> 使用时,在目标元素上添加`:drag`属性即可以实现拖拽功能。
|
|
||||||
|
|
||||||
## 依赖
|
|
||||||
> 依赖`Anot`框架
|
|
||||||
|
|
||||||
## 浏览器兼容性
|
|
||||||
+ chrome
|
|
||||||
+ firefox
|
|
||||||
+ safari
|
|
||||||
+ IE10+
|
|
||||||
|
|
||||||
|
|
||||||
## 用法
|
|
||||||
> 只需要在要拖拽的元素上添加`:drag`即可;
|
|
||||||
> 如果要拖拽的元素不是当前元素,只需要给该属性增加一个值为想要拖拽元素的类名或ID。
|
|
||||||
> 具体请看示例:
|
|
||||||
> **注意:** `拖拽的元素不是本身时,只会往父级一级一级找相匹配的`
|
|
||||||
|
|
||||||
```html
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<style>
|
|
||||||
* {margin:0;padding:0}
|
|
||||||
.box {width:200px;height:100px;background:#aaa;}
|
|
||||||
.box .handle {width:200px;height:30px;background:#f30;}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body :controller="test">
|
|
||||||
|
|
||||||
<div class="box" :drag></div>
|
|
||||||
|
|
||||||
<div class="box">
|
|
||||||
<div class="handle" :drag="box"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Anot from 'lib/drag/index.js'
|
|
||||||
Anot({
|
|
||||||
$id: 'test'
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## 额外参数
|
|
||||||
|
|
||||||
### `data-limit`
|
|
||||||
> 用于限制元素的拖动范围,默认没有限制。 可选值为 "window"和"parent", 分别为 "限制在可视区"和"限制在父级元素的范围"
|
|
||||||
|
|
||||||
### `data-axis`
|
|
||||||
> 用于限制拖动的方向, 默认值为 "xy",即不限制方向。可选值为 "x"和"y", 即只能在"x轴"或"y轴"方向拖动。
|
|
||||||
|
|
||||||
### `data-beforedrag`
|
|
||||||
> 拖动前的回调,如果有设置回调方法, 则该回调的返回值,可决定该元素是否能被拖拽, 可用于在特殊场景下,临时禁用拖拽。
|
|
||||||
> `注:`
|
|
||||||
> 1. 该回调方法,会传入3个参数, 第1个为被拖拽的元素(dom对象), 第2个参数为 该元素的x轴绝对坐标, 第3个元素为y轴绝对坐标;
|
|
||||||
> 2. 该回调方法, 返回false时, 本次拖拽将临时失效, 返回其他值,或没有返回值,则忽略。
|
|
||||||
|
|
||||||
|
|
||||||
### `data-dragging`
|
|
||||||
> 元素被拖动时的回调。
|
|
||||||
> `注:`
|
|
||||||
> 1.该回调方法,会传入3个参数, 第1个为被拖拽的元素(dom对象), 第2个参数为 该元素的x轴绝对坐标, 第3个元素为y轴绝对坐标;
|
|
||||||
|
|
||||||
|
|
||||||
### `data-dragged`
|
|
||||||
> 元素被拖动结束后的回调。
|
|
||||||
> `注:`
|
|
||||||
> 1. 该回调方法,会传入3个参数, 第1个为被拖拽的元素(dom对象), 第2个参数为 该元素的x轴绝对坐标, 第3个元素为y轴绝对坐标;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
@ -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("&")}};
|
|
@ -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"};class _Request{constructor(e="",t={},{BASE_URL:o,__INIT__:r}){if(!e)throw new Error("Argument url is required");e=e.replace(/#.*$/,""),o&&(/^([a-z]+:|\/\/)/.test(e)||(e=o+e)),t.method=(t.method||"get").toUpperCase(),this.options={headers:{"X-Requested-With":"XMLHttpRequest","content-type":FORM_TYPES.form},body:null,cache:"default",signal:null,timeout:3e4},t.signal||(this.control=new AbortController,t.signal=this.control.signal);var s=this.options.headers;return r.headers&&Object.assign(s,r.headers),t.headers&&(Object.assign(s,t.headers),delete t.headers),Object.assign(this.options,r,t,{url:e,headers:s}),this.__next__()}__next__(){var e=this.options,t=null,o=!1,r=!1,s=NOBODY_METHODS.includes(e.method);if(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",o=(t=Format.parseForm(e.body)).constructor===FormData;else if(e.body.constructor===FormData)o=!0,s&&(e.method="POST"),t=e.body;else{for(let t in e.body)if("[object File]"===toS.call(e.body[t])){o=!0;break}o?(s&&(e.method="POST"),t=Format.mkFormData(e.body)):t=e.body}}o&&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&&"omit"===e.credentials&&delete e.headers["X-Requested-With"],s?((t=Format.param(t))&&(e.url+=(~e.url.indexOf("?")?"&":"?")+t),"no-store"===e.cache&&(e.url+=(~e.url.indexOf("?")?"&":"?")+"_t_="+Date.now())):o||(t=~e.headers["content-type"].indexOf("json")?JSON.stringify(t):Format.param(t)),e.timeout&&e.timeout>0&&(this.timer=setTimeout(e=>{this.abort()},e.timeout),delete e.timeout);var n=e.url;delete e.url;for(let t in e)null!==e[t]&&void 0!==e[t]&&""!==e[t]||delete e[t];return window.fetch(n,e).then(e=>{return clearTimeout(this.timer),e.status>=200&&e.status<400?e:Promise.reject(e)}).catch(e=>(clearTimeout(this.timer),Promise.reject(e)))}abort(){this.control.abort()}__type__(e){this.options.headers["content-type"]=FORM_TYPES[e]}}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(o,r){return new _Request(o,r,{BASE_URL:e,__INIT__:t})}};export default _fetch;
|
21
src/main.js
21
src/main.js
|
@ -23,7 +23,14 @@ const MIME_TYPES = {
|
||||||
'.png': 'image/png',
|
'.png': 'image/png',
|
||||||
'.gif': 'image/gif',
|
'.gif': 'image/gif',
|
||||||
'.svg': 'image/svg+xml',
|
'.svg': 'image/svg+xml',
|
||||||
'.ico': 'image/ico'
|
'.ico': 'image/ico',
|
||||||
|
'.mp3': 'audio/mpeg',
|
||||||
|
'.m4a': 'audio/m4a',
|
||||||
|
'.aac': 'audio/x-aac',
|
||||||
|
'.ogg': 'audio/ogg',
|
||||||
|
'.wav': 'audio/x-wav',
|
||||||
|
'.flac': 'audio/flac',
|
||||||
|
all: 'audio/*'
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------- */
|
/* ----------------------------------------------------- */
|
||||||
|
@ -31,7 +38,8 @@ app.commandLine.appendSwitch('--lang', 'zh-CN')
|
||||||
app.commandLine.appendSwitch('--autoplay-policy', 'no-user-gesture-required')
|
app.commandLine.appendSwitch('--autoplay-policy', 'no-user-gesture-required')
|
||||||
|
|
||||||
protocol.registerSchemesAsPrivileged([
|
protocol.registerSchemesAsPrivileged([
|
||||||
{ scheme: 'app', privileges: { secure: true, standard: true } }
|
{ scheme: 'app', privileges: { secure: true, standard: true } },
|
||||||
|
{ scheme: 'sonist', privileges: { secure: true, standard: true } }
|
||||||
])
|
])
|
||||||
|
|
||||||
/* ----------------------------------------------------- */
|
/* ----------------------------------------------------- */
|
||||||
|
@ -40,11 +48,18 @@ protocol.registerSchemesAsPrivileged([
|
||||||
app.once('ready', () => {
|
app.once('ready', () => {
|
||||||
// 注册协议
|
// 注册协议
|
||||||
protocol.registerBufferProtocol('app', (req, cb) => {
|
protocol.registerBufferProtocol('app', (req, cb) => {
|
||||||
let file = req.url.replace(/^app:\/\/local\//, '')
|
let file = decodeURIComponent(req.url.replace(/^app:\/\/local\//, ''))
|
||||||
let ext = path.extname(req.url)
|
let ext = path.extname(req.url)
|
||||||
let buff = fs.cat(path.resolve(__dirname, file))
|
let buff = fs.cat(path.resolve(__dirname, file))
|
||||||
cb({ data: buff, mimeType: MIME_TYPES[ext] })
|
cb({ data: buff, mimeType: MIME_TYPES[ext] })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
protocol.registerBufferProtocol('sonist', (req, cb) => {
|
||||||
|
let file = decodeURIComponent(req.url.replace(/^sonist:[\/]+/, '/'))
|
||||||
|
let ext = path.extname(req.url)
|
||||||
|
let buff = fs.cat(file)
|
||||||
|
cb({ data: buff, mimeType: MIME_TYPES[ext] || MIME_TYPES.all })
|
||||||
|
})
|
||||||
// 修改app的UA
|
// 修改app的UA
|
||||||
session.defaultSession.setUserAgent(
|
session.defaultSession.setUserAgent(
|
||||||
'KugouMusic/2.9.5 (Mac OS X Version 10.15.7 (Build 19H2))'
|
'KugouMusic/2.9.5 (Mac OS X Version 10.15.7 (Build 19H2))'
|
||||||
|
|
Reference in New Issue