import { Player } from 'lavalink' ; import QueueStore , { ExtendedRedis } from './QueueStore' ; import { EventEmitter } from 'events' ; export interface NP { position : number ; track : string ; } export default class Queue extends EventEmitter { public readonly keys : { next : string ; pos : string ; prev : string ; } ; constructor ( public readonly store , public readonly guildID ) { super ( ) ; this . keys = { next : `playlists.${ this . guildID }.next` , pos : `playlists.${ this . guildID }.pos` , prev : `playlists.${ this . guildID }.prev` , } ; this . on ( 'event' , async ( d ) => { if ( ! [ "TrackEndEvent" , "TrackStartEvent" , "WebSocketClosedEvent" ] . includes ( d . type ) || ( d . type === "TrackEndEvent" && ! [ 'REPLACED' , 'STOPPED' ] . includes ( d . reason ) ) ) { let count = d . type === 'TrackEndEvent' ? undefined : 1 ; try { await this . _next ( { count , previous : d } ) ; } catch ( e ) { this . store . client . emit ( 'error' , e ) ; } } } ) ; this . on ( 'playerUpdate' , async ( d ) => { try { await this . _redis . set ( this . keys . pos , d . state . position ) ; } catch ( e ) { this . store . client . emit ( 'error' , e ) ; } } ) ; } public get player ( ) : Player { return this . store . client . players . get ( this . guildID ) ; } public async start ( ) { const np = await this . current ( ) ; if ( ! np ) return this . _next ( ) ; await this . player . play ( np . track , { start : np . position } ) ; return true ; } public add ( ... tracks ) { if ( ! tracks . length ) return Promise . resolve ( 0 ) ; return this . _redis . lpush ( this . keys . next , ... tracks ) ; } public unshift ( ... tracks ) { if ( ! tracks . length ) return Promise . resolve ( 0 ) ; return this . _redis . rpush ( this . keys . next , ... tracks ) ; } public remove ( track ) { return this . _redis . lrem ( this . keys . next , 1 , track ) ; } public next ( count = 1 ) { return this . _next ( { count } ) ; } public length ( ) { return this . _redis . llen ( this . keys . next ) ; } public async sort ( predicate ? ) { const tracks = await this . tracks ( ) ; tracks . sort ( predicate ) ; return this . _redis . loverride ( this . keys . next , ... tracks ) ; } public async move ( from , to ) { const list = await this . _redis . lmove ( this . keys . next , - from - 1 , - to - 1 ) ; return list . reverse ( ) ; } public shuffle ( ) { return this . _redis . lshuffle ( this . keys . next , Date . now ( ) ) ; } public splice ( start , deleteCount ? , ... tracks ) { return this . _redis . lrevsplice ( this . keys . next , start , deleteCount , ... tracks ) ; } public trim ( start , end ) { return this . _redis . ltrim ( this . keys . next , start , end ) ; } public async stop ( ) { await this . player . stop ( ) ; } public async clear ( ) { return this . _redis . del ( this . keys . next , this . keys . prev , this . keys . pos ) ; } public async current ( ) { const [ track , position ] = await Promise . all ( [ this . _redis . lindex ( this . keys . prev , 0 ) , this . _redis . get ( this . keys . pos ) , ] ) ; if ( track ) { return { track , position : parseInt ( position ) || 0 , } ; } return null ; } public async tracks ( start = 0 , end = - 1 ) { if ( end === Infinity ) end = - 1 ; const tracks = await this . _redis . lrange ( this . keys . next , start , end ) ; return tracks . reverse ( ) ; } protected async _next ( { count , previous } = { } ) { await this . _redis . set ( this . keys . pos , 0 ) ; if ( ! previous ) previous = await this . current ( ) ; if ( count === undefined && previous ) { const length = await this . length ( ) ; count = this . store . client . advanceBy ( this , { previous : previous . track , remaining : length } ) ; } if ( count === 0 ) return this . start ( ) ; const skipped = await this . _redis . multirpoplpush ( this . keys . next , this . keys . prev , count || 1 ) ; if ( skipped . length ) return this . start ( ) ; await this . clear ( ) ; return false ; } protected get _redis ( ) : ExtendedRedis { return this . store . redis ; } }