Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add getFile(). Separate closing UDP sockets from device connections. #18

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
lib
docs
node_modules
24 changes: 24 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"version": "0.2.0",
"configurations": [
{
// Run `yarn build` then run cli.js.
"type": "pwa-node",
"request": "launch",
"name": "Run CLI",
"skipFiles": [
"<node_internals>/**"
],
"internalConsoleOptions": "openOnSessionStart",
// This is so signale shows up in the VSC terminal
"console": "integratedTerminal",
"preLaunchTask": "yarn build",
"program": "${workspaceFolder}/lib/cli.js",
"resolveSourceMapLocations": [
"${workspaceFolder}/**",
"!**/node_modules/**"
],
"outFiles": ["${workspaceFolder}/lib/*.js"]
}
]
}
10 changes: 10 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "yarn build",
"type": "shell",
"command": "yarn build"
}
]
}
28 changes: 18 additions & 10 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import signale from 'signale';
import {MixstatusProcessor} from 'src/mixstatus';
import {bringOnline} from 'src/network';

import fs from 'fs';

Sentry.init({
dsn: 'https://36570041fd5a4c05af76456e60a1233a@o126623.ingest.sentry.io/5205486',
tracesSampleRate: 1,
Expand All @@ -16,9 +18,9 @@ async function cli() {
const network = await bringOnline();
signale.success('Network online, preparing to connect');

network.deviceManager.on('connected', d =>
network.deviceManager.on('connected', d => {
signale.star('New device: %s [id: %s]', d.name, d.id)
);
});

signale.await('Autoconfiguring network.. waiting for devices');
await network.autoconfigFromPeers();
Expand Down Expand Up @@ -48,8 +50,6 @@ async function cli() {

lastTid.set(state.deviceId, trackId);

console.log(trackId);

const track = await network.db.getMetadata({
deviceId: trackDeviceId,
trackSlot,
Expand All @@ -62,14 +62,22 @@ async function cli() {
return;
}

const art = await network.db.getArtwork({
deviceId: trackDeviceId,
trackSlot,
trackType,
track,
console.log(trackId, track.title);

// Download a file from ProDJ-Link.
const buf = await network.db.getFile({
deviceId: state.trackDeviceId,
trackSlot: state.trackSlot,
trackType: state.trackType,
track: track
});
if (buf) {
fs.writeFileSync(track.fileName, buf, 'binary');
console.log(`Copied ${track.fileName}`);
}

// Display the track that was emmited by the network.

console.log(trackId, track.title, art?.length);
});

await new Promise(r => setTimeout(r, 3000));
Expand Down
File renamed without changes.
94 changes: 94 additions & 0 deletions src/db/getFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import * as Sentry from '@sentry/node';
import {Span} from '@sentry/tracing';

import {Track} from 'src/entities';
import LocalDatabase from 'src/localdb';
import {fetchFile} from 'src/nfs';
import RemoteDatabase, {MenuTarget, Query} from 'src/remotedb';
import {Device, DeviceID, MediaSlot, TrackType} from 'src/types';

export type Options = {
/**
* The device to query the file off of
*/
deviceId: DeviceID;
/**
* The media slot the track is present in
*/
trackSlot: MediaSlot;
/**
* The type of track we are querying file for
*/
trackType: TrackType;
/**
* The track to get track for
*/
track: Track;
/**
* The Sentry transaction span
*/
span?: Span;
};

const CHUNK_SIZE = 8192; // Maximum allowed XDR read size

export async function viaRemote(remote: RemoteDatabase, device: Device, opts: Required<Options>) {
const {deviceId, trackSlot, trackType, track, span} = opts;

console.error('Getting a file from Rekordbox via ProDJ-Link is not yet supported.')
return null;

// const conn = await remote.get(deviceId);
// if (conn === null) {
// return null;
// }

// if (track.artwork === null) {
// return null;
// }

// const queryDescriptor = {
// trackSlot,
// trackType,
// menuTarget: MenuTarget.Main,
// };

// return conn.query({
// queryDescriptor,
// query: Query.GetArtwork,
// args: {artworkId: track.artwork.id},
// span,
// });
}

export async function viaLocal(
local: LocalDatabase,
device: Device,
opts: Required<Options>
) {
const {deviceId, trackSlot, track} = opts;

if (trackSlot !== MediaSlot.USB && trackSlot !== MediaSlot.SD) {
throw new Error('Expected USB or SD or RB slot for remote database query');
}

const conn = await local.get(deviceId, trackSlot);
if (conn === null) {
return null;
}

try {
return fetchFile({
device,
slot: trackSlot,
path: track.filePath,
onProgress: (progress) => {
console.log(progress.read, progress.total);
},
chunkSize: CHUNK_SIZE
});
} catch (error) {
Sentry.captureException(error);
return null;
}
}
51 changes: 46 additions & 5 deletions src/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import {
} from 'src/types';
import {getSlotName, getTrackTypeName} from 'src/utils';

import * as GetArtwork from './getArtwork';
import * as GetFile from './getFile';
import * as GetArtworkThumbnail from './getArtworkThumbnail';
import * as GetMetadata from './getMetadata';
import * as GetPlaylist from './getPlaylist';
import * as GetWaveforms from './getWaveforms';
Expand Down Expand Up @@ -125,10 +126,50 @@ class Database {
return track;
}

/**
* Retrives the file off a specific device slot.
*/
async getFile(opts: GetArtworkThumbnail.Options) {
const {deviceId, trackType, trackSlot, span} = opts;

const tx = span
? span.startChild({op: 'dbGetFile'})
: Sentry.startTransaction({name: 'dbGetFile'});

tx.setTag('deviceId', deviceId.toString());
tx.setTag('trackType', getTrackTypeName(trackType));
tx.setTag('trackSlot', getSlotName(trackSlot));

const callOpts = {...opts, span: tx};

const device = await this.#deviceManager.getDeviceEnsured(deviceId);
if (device === null) {
return null;
}

const strategy = this.#getTrackLookupStrategy(device, trackType);
let artwork: Buffer | null = null;

if (strategy === LookupStrategy.Remote) {
artwork = await GetFile.viaRemote(this.#remoteDatabase, device, callOpts);
}

if (strategy === LookupStrategy.Local) {
artwork = await GetFile.viaLocal(this.#localDatabase, device, callOpts);
}

if (strategy === LookupStrategy.NoneAvailable) {
tx.setStatus(SpanStatus.Unavailable);
}

tx.finish();

return artwork;
}
/**
* Retrives the artwork for a track on a specific device slot.
*/
async getArtwork(opts: GetArtwork.Options) {
async getArtworkThumbnail(opts: GetArtworkThumbnail.Options) {
const {deviceId, trackType, trackSlot, span} = opts;

const tx = span
Expand All @@ -150,11 +191,11 @@ class Database {
let artwork: Buffer | null = null;

if (strategy === LookupStrategy.Remote) {
artwork = await GetArtwork.viaRemote(this.#remoteDatabase, callOpts);
artwork = await GetArtworkThumbnail.viaRemote(this.#remoteDatabase, callOpts);
}

if (strategy === LookupStrategy.Local) {
artwork = await GetArtwork.viaLocal(this.#localDatabase, device, callOpts);
artwork = await GetArtworkThumbnail.viaLocal(this.#localDatabase, device, callOpts);
}

if (strategy === LookupStrategy.NoneAvailable) {
Expand All @@ -169,7 +210,7 @@ class Database {
/**
* Retrives the waveforms for a track on a specific device slot.
*/
async getWaveforms(opts: GetArtwork.Options) {
async getWaveforms(opts: GetArtworkThumbnail.Options) {
const {deviceId, trackType, trackSlot, span} = opts;

const tx = span
Expand Down
7 changes: 7 additions & 0 deletions src/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,13 @@ export class ProlinkNetwork {
this.localdb?.disconnectForDevice(device);
}

return this.close;
}

/**
* Close UDP sockets.
*/
close() {
return Promise.all([
udpClose(this.#announceSocket),
udpClose(this.#statusSocket),
Expand Down
4 changes: 3 additions & 1 deletion src/nfs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ type FetchFileOptions = {
path: string;
onProgress?: Parameters<typeof fetchFileCall>[2];
span?: Span;
chunkSize?: number;
};

const badRoothandleError = (slot: MediaSlot, deviceId: DeviceID) =>
Expand All @@ -160,6 +161,7 @@ export async function fetchFile({
path,
onProgress,
span,
chunkSize
}: FetchFileOptions) {
const tx = span
? span.startChild({op: 'fetchFile'})
Expand Down Expand Up @@ -190,7 +192,7 @@ export async function fetchFile({
fileInfo = await lookupPath(nfsClient, rootHandle, path, tx);
}

const file = await fetchFileCall(nfsClient, fileInfo, onProgress, tx);
const file = await fetchFileCall(nfsClient, fileInfo, onProgress, tx, chunkSize);

tx.setData('path', path);
tx.setData('slot', getSlotName(slot));
Expand Down
8 changes: 6 additions & 2 deletions src/nfs/programs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,12 @@ export async function fetchFile(
conn: RpcProgram,
file: FileInfo,
onProgress?: (progress: FetchProgress) => void,
span?: Span
span?: Span,
readSize?: number
) {
readSize = readSize || READ_SIZE;
if (readSize > 8192)
throw new Error(`Maximum read size for NFS is 8192. You specified: ${readSize}`);
const {handle, name, size} = file;
const data = Buffer.alloc(size);

Expand All @@ -208,7 +212,7 @@ export async function fetchFile(
const readArgs = new nfs.ReadArgs({
handle,
offset: bytesRead,
count: READ_SIZE,
count: readSize,
totalCount: 0,
});

Expand Down