import * as ip from 'ip-address' ; import { Socket } from 'dgram' ; import { NetworkInterfaceInfoIPv4 } from 'os' ; import { ANNOUNCE_INTERVAL , ANNOUNCE_PORT , PROLINK_HEADER , VIRTUAL_CDJ_FIRMWARE , VIRTUAL_CDJ_NAME , } from 'src/constants' ; import DeviceManager from 'src/devices' ; import { Device , DeviceID , DeviceType } from 'src/types' ; import { buildName } from 'src/utils' ; /**
 * Constructs a virtual CDJ Device.
 */ export const getVirtualCDJ = ( iface , id ) : Device => ( { id , name : VIRTUAL_CDJ_NAME , type : DeviceType . CDJ , ip : new ip . Address4 ( iface . address ) , macAddr : new Uint8Array ( iface . mac . split ( ':' ) . map ( s => parseInt ( s , 16 ) ) ) , } ) ; /**
 * Returns a mostly empty-state status packet. This is currently used to report
 * the virtual CDJs status, which *seems* to be required for the CDJ to send
 * metadata about some unanalyzed mp3 files.
 */ export function makeStatusPacket ( device ) { const b = new Uint8Array ( [ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x0a , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 , 0x03 , 0x00 , 0x00 , 0xf8 , 0x00 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 , 0x00 , 0x04 , 0x04 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x04 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x9c , 0xff , 0xfe , 0x00 , 0x10 , 0x00 , 0x00 , 0x7f , 0xff , 0xff , 0xff , 0x7f , 0xff , 0xff , 0xff , 0x00 , 0x10 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xff , 0xff , 0xff , 0xff , 0xff , 0x01 , 0xff , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x10 , 0x00 , 0x00 , 0x00 , 0x10 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x0f , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x05 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , ] ) ; b . set ( PROLINK_HEADER , 0x0b ) ; b . set ( Buffer . from ( device . name , 'ascii' ) , 0x0b ) ; b . set ( new Uint8Array ( [ device . id ] ) , 0x21 ) ; b . set ( new Uint8Array ( [ device . id ] ) , 0x24 ) ; b . set ( Buffer . from ( VIRTUAL_CDJ_FIRMWARE , 'ascii' ) , 0x7c ) ; return b ; } /**
 * constructs the announce packet that is sent on the prolink network to
 * announce a devices existence.
 */ export function makeAnnouncePacket ( deviceToAnnounce ) { const d = deviceToAnnounce ; const unknown1 = [ 0x01 , 0x02 ] ; const unknown2 = [ 0x01 , 0x00 , 0x00 , 0x00 ] ; const parts = [ ... PROLINK_HEADER , ... [ 0x06 , 0x00 ] , ... buildName ( d ) , ... unknown1 , ... [ 0x00 , 0x36 ] , ... [ d . id ] , ... [ d . type ] , ... d . macAddr , ... d . ip . toArray ( ) , ... unknown2 , ... [ d . type ] , ... [ 0x00 ] , ] ; return Uint8Array . from ( parts ) ; } /**
 * the announcer service is used to report our fake CDJ to the prolink network,
 * as if it was a real CDJ.
 */ export class Announcer { /**
     * The announce socket to use to make the announcments
     */ #announceSocket : Socket ; /**
     * The device manager service used to determine which devices to announce
     * ourselves to.
     */ #deviceManager : DeviceManager ; /**
     * The virtual CDJ device to announce
     */ #vcdj : Device ; /**
     * The interval handle used to stop announcing
     */ #intervalHandle ? : NodeJS . Timeout ; constructor ( vcdj , announceSocket , deviceManager ) { this . #vcdj = vcdj ; this . #announceSocket = announceSocket ; this . #deviceManager = deviceManager ; } start ( ) { const announcePacket = makeAnnouncePacket ( this . #vcdj ) ; const announceToDevice = ( device ) => this . #announceSocket . send ( announcePacket , ANNOUNCE_PORT , device . ip . address ) ; this . #intervalHandle = setInterval ( ( ) => [ ... this . #deviceManager . devices . values ( ) ] . forEach ( announceToDevice ) , ANNOUNCE_INTERVAL ) ; } stop ( ) { if ( this . #intervalHandle !== undefined ) { clearInterval ( this . #intervalHandle ) ; } } }