重构音频播放
parent
6bd72fcbac
commit
b21ec91170
|
@ -9,12 +9,19 @@ import '/js/lib/scroll/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 Player from '/js/lib/audio/index.js'
|
||||
|
||||
// const id3 = require('jsmediatags')
|
||||
const id3 = require('music-metadata')
|
||||
|
||||
var kb = new Keyboard()
|
||||
|
||||
var player = new Player()
|
||||
|
||||
window.fetch = fetch
|
||||
window.player = player
|
||||
|
||||
Anot({
|
||||
$id: 'app',
|
||||
state: {
|
||||
|
@ -52,6 +59,8 @@ Anot({
|
|||
it.duration = duration
|
||||
}
|
||||
|
||||
player.load(list.map(it => `sonist://${it.path}`))
|
||||
|
||||
kb.on(['left'], ev => {
|
||||
var time = this.song.time - 5
|
||||
if (time < 0) {
|
||||
|
@ -68,6 +77,12 @@ Anot({
|
|||
})
|
||||
},
|
||||
methods: {
|
||||
load() {
|
||||
// window.player = new Howl({
|
||||
// src: [`sonist://${this.list[0].path}`]
|
||||
// })
|
||||
// window.player = this.__PLAYER__
|
||||
},
|
||||
play() {
|
||||
this.isplaying = !this.isplaying
|
||||
},
|
||||
|
|
|
@ -1,214 +1,126 @@
|
|||
/**
|
||||
* 播放器
|
||||
* @author yutent<yutent@doui.cc>
|
||||
* @date 2018/12/23 23:14:40
|
||||
*
|
||||
* @author yutent<yutent.io@gmail.com>
|
||||
* @date 2020/11/19 17:32:19
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
import fetch from '../fetch/index.js'
|
||||
|
||||
const { exec } = require('child_process')
|
||||
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('end')
|
||||
},
|
||||
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 const ID3 = song => {
|
||||
let cmd = `ffprobe -v quiet -print_format json -show_entries format "${song}"`
|
||||
let pc = exec(cmd)
|
||||
let buf = ''
|
||||
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)
|
||||
}
|
||||
})
|
||||
function hide(target, key, value) {
|
||||
Object.defineProperty(target, key, {
|
||||
value,
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
})
|
||||
}
|
||||
|
||||
export default AudioPlayer
|
||||
export default class Player {
|
||||
constructor() {
|
||||
hide(this, '__LIST__', [])
|
||||
hide(this, '__AC__', new AudioContext())
|
||||
hide(this, '__AUDIO__', new Audio())
|
||||
hide(this, 'props', {
|
||||
curr: '',
|
||||
stat: 'ready',
|
||||
volume: 0.5,
|
||||
mode: 'all', // 循环模式, all, single, rand
|
||||
time: 0,
|
||||
duration: 0
|
||||
})
|
||||
hide(this, 'track', null)
|
||||
}
|
||||
|
||||
load(list) {
|
||||
this.stop()
|
||||
this.__LIST__ = list
|
||||
}
|
||||
|
||||
async _getTrack(file) {
|
||||
this.__AUDIO__.src = URL.createObjectURL(
|
||||
await fetch(file).then(r => r.blob())
|
||||
)
|
||||
console.log(this.__AUDIO__)
|
||||
return this.__AC__.createMediaElementSource(this.__AUDIO__)
|
||||
}
|
||||
|
||||
get volume() {
|
||||
return this.props.volume
|
||||
}
|
||||
|
||||
set volume(val) {
|
||||
val = +val || 0.5
|
||||
if (val < 0) {
|
||||
val = 0
|
||||
}
|
||||
if (val > 1) {
|
||||
val = 1
|
||||
}
|
||||
this.props.volume = val
|
||||
}
|
||||
|
||||
get mode() {
|
||||
return this.props.mode
|
||||
}
|
||||
|
||||
set mode(val) {
|
||||
this.props.mode = val
|
||||
}
|
||||
|
||||
get time() {
|
||||
return this.props.time
|
||||
}
|
||||
|
||||
get stat() {
|
||||
return this.props.stat
|
||||
}
|
||||
|
||||
async play(id) {
|
||||
var url, gain
|
||||
|
||||
if (id === undefined) {
|
||||
if (this.track) {
|
||||
if (this.stat === 'playing') {
|
||||
this.props.time = this.track.context.currentTime
|
||||
this.__AUDIO__.pause()
|
||||
this.props.stat = 'paused'
|
||||
} else if (this.stat === 'paused') {
|
||||
this.__AUDIO__.play()
|
||||
this.props.stat = 'playing'
|
||||
}
|
||||
}
|
||||
} else {
|
||||
url = this.__LIST__[id]
|
||||
if (!url || this.props.curr === url) {
|
||||
return
|
||||
}
|
||||
|
||||
gain = this.__AC__.createGain()
|
||||
|
||||
if (this.track) {
|
||||
this.stop()
|
||||
}
|
||||
|
||||
gain.gain.value = this.volume
|
||||
|
||||
this.track = await this._getTrack(url)
|
||||
this.track.connect(gain).connect(this.__AC__.destination)
|
||||
|
||||
this.__AUDIO__.play()
|
||||
this.props.stat = 'playing'
|
||||
}
|
||||
}
|
||||
|
||||
seek(time) {
|
||||
this.props.time = time
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.track) {
|
||||
this.__AUDIO__.pause()
|
||||
this.track = null
|
||||
this.props.time = 0
|
||||
this.props.stat = 'stoped'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
'.gif': 'image/gif',
|
||||
'.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')
|
||||
|
||||
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', () => {
|
||||
// 注册协议
|
||||
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 buff = fs.cat(path.resolve(__dirname, file))
|
||||
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
|
||||
session.defaultSession.setUserAgent(
|
||||
'KugouMusic/2.9.5 (Mac OS X Version 10.15.7 (Build 19H2))'
|
||||
|
|
Reference in New Issue