Skip to content

Commit

Permalink
Improve player performance
Browse files Browse the repository at this point in the history
  • Loading branch information
undyingwraith committed Oct 21, 2024
1 parent 13ed0ce commit ef50f9c
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 609 deletions.
11 changes: 8 additions & 3 deletions packages/core/src/util/createRemoteIpfs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IFileInfo, IIpfsService } from 'ipmc-interfaces';
import { create } from 'kubo-rpc-client';
import { concat } from 'uint8arrays';

export async function createRemoteIpfs(url?: string): Promise<IIpfsService> {
const node = create({ url });
Expand All @@ -22,9 +23,6 @@ export async function createRemoteIpfs(url?: string): Promise<IIpfsService> {
stop() {
return Promise.resolve();
},
toUrl(cid: string) {
return `http://127.0.0.1:${port}/ipfs/${cid}`;
},
id() {
return id;
},
Expand Down Expand Up @@ -55,5 +53,12 @@ export async function createRemoteIpfs(url?: string): Promise<IIpfsService> {
async rmPin(cid) {
await node.pin.rm(cid);
},
async fetch(cid: string, path?: string) {
const parts = [];
for await (const buf of node.cat(path ? path.startsWith('/') ? cid + path : cid + '/' + path : cid)) {
parts.push(buf);
}
return concat(parts);
}
};
}
2 changes: 0 additions & 2 deletions packages/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
"blockstore-fs": "^1.1.10",
"datastore-level": "^10.1.8",
"electron-updater": "6.2.1",
"express": "^4.19.2",
"helia": "^4.2.2",
"ipmc-core": "workspace:^",
"ipmc-interfaces": "workspace:^",
Expand All @@ -59,7 +58,6 @@
"@helia/ipns": "^7.2.2",
"@preact/signals-react-transform": "^0.3.1",
"@rollup/plugin-commonjs": "^25.0.7",
"@types/express": "^4",
"@types/node": "^20.12.12",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
Expand Down
87 changes: 18 additions & 69 deletions packages/desktop/src/preload/index.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,38 @@
import { gossipsub } from '@chainsafe/libp2p-gossipsub';
import { noise, pureJsCrypto } from '@chainsafe/libp2p-noise';
import { yamux } from '@chainsafe/libp2p-yamux';
import { mplex } from '@libp2p/mplex';
import { bitswap, trustlessGateway } from '@helia/block-brokers';
import { ipns } from '@helia/ipns';
import { unixfs } from '@helia/unixfs';
import { autoNAT } from '@libp2p/autonat';
import { bootstrap } from '@libp2p/bootstrap';
import { circuitRelayServer, circuitRelayTransport } from '@libp2p/circuit-relay-v2';
import { dcutr } from '@libp2p/dcutr';
import { identify, identifyPush } from '@libp2p/identify';
import { kadDHT, removePrivateAddressesMapper } from '@libp2p/kad-dht';
import { keychain } from '@libp2p/keychain';
import { mdns } from '@libp2p/mdns';
import { mplex } from '@libp2p/mplex';
import { peerIdFromString } from '@libp2p/peer-id';
import { ping } from '@libp2p/ping';
import { preSharedKey } from '@libp2p/pnet';
import { pubsubPeerDiscovery } from '@libp2p/pubsub-peer-discovery';
import { tcp } from '@libp2p/tcp';
import { uPnPNAT } from '@libp2p/upnp-nat';
import { webSockets } from '@libp2p/websockets';
import { kadDHT, removePrivateAddressesMapper } from '@libp2p/kad-dht';
import { webTransport } from '@libp2p/webtransport';
import { FsBlockstore } from 'blockstore-fs';
import { LevelDatastore } from 'datastore-level';
import { contextBridge } from 'electron';
import express from 'express';
import fs from 'fs';
import { createHelia } from 'helia';
import { IncomingMessage, Server, ServerResponse } from 'http';
import { Defaults } from 'ipmc-core';
import { IConfigurationService, IFileInfo, IInternalProfile, IIpfsService, INodeService, IProfile } from 'ipmc-interfaces';
import { CID } from 'multiformats';
import { gossipsub } from '@chainsafe/libp2p-gossipsub';
import { pubsubPeerDiscovery } from '@libp2p/pubsub-peer-discovery';
import { mdns } from '@libp2p/mdns';
import { identify, identifyPush } from '@libp2p/identify';
import { keychain } from '@libp2p/keychain';
import { ping } from '@libp2p/ping';
import { dcutr } from '@libp2p/dcutr';
import { autoNAT } from '@libp2p/autonat';
import { ipnsSelector } from 'ipns/selector';
import { ipnsValidator } from 'ipns/validator';
import { uPnPNAT } from '@libp2p/upnp-nat';
import { webTransport } from '@libp2p/webtransport';
import * as libp2pInfo from 'libp2p/version';
import { CID } from 'multiformats';
import { concat } from 'uint8arrays';
import { Defaults } from 'ipmc-core';

function getProfileFolder(name: string): string {
return `./profiles/${name}`;
Expand Down Expand Up @@ -133,53 +131,6 @@ const nodeService: INodeService = {

const fs = unixfs(helia);

const app = express();
app.get('/:cid', async (request, response) => {
const CHUNK_SIZE = (10 ** 6) * 3;
const rangeHeader = request.headers.range;
const offsetString = rangeHeader != undefined ? rangeHeader.substring(rangeHeader.indexOf('=') + 1, rangeHeader.indexOf('-')) : undefined;
const offset = offsetString !== undefined && offsetString !== '' ? parseInt(offsetString) : 0;

const cid = CID.parse(request.params.cid);

const controller = new AbortController();
request.once('close', () => controller.abort('Request cancelled'));
const stat = await fs.stat(cid, { signal: controller.signal });
const fileSize = parseInt(stat.fileSize.toString());
const end = rangeHeader === undefined ? fileSize - 1 : Math.min(offset + CHUNK_SIZE, fileSize - 1);
const length = end - offset;

const headers = {
'Accept-Ranges': 'bytes',
'Access-Control-Allow-Headers': 'Range',
'Access-Control-Expose-Headers': [
'Content-Length',
'Content-Range',
],
'Content-Length': length,
'Content-Range': `bytes ${offset}-${end}/${fileSize}`,
};

response.writeHead(length + 1 < fileSize ? 206 : 200, headers);

for await (const buf of fs.cat(cid, {
offset,
length,
signal: controller.signal,
})) {
try {
await new Promise<void>((resolve, reject) => response.write(buf, (ex) => ex != undefined ? reject(ex) : resolve()));
} catch (ex) {
console.error(ex);
}
}
response.end();
});
const gatewayPort = 8090;
const server = await new Promise<Server<typeof IncomingMessage, typeof ServerResponse>>((resolve) => {
const server = app.listen(gatewayPort, '127.0.0.1', () => resolve(server));
});

const service = ({
async ls(cid: string) {
const files: IFileInfo[] = [];
Expand All @@ -193,15 +144,11 @@ const nodeService: INodeService = {
return files;
},
async stop() {
server.close();
await helia.stop();
await blockstore.close();
await datastore.close();
nodes.delete(profile.id);
},
toUrl(cid: string) {
return `http://127.0.0.1:${gatewayPort}/${cid}`;
},
peers() {
return Promise.resolve([...new Set(helia.libp2p.getConnections().map(p => p.remoteAddr.toString()))]);
},
Expand Down Expand Up @@ -229,9 +176,11 @@ const nodeService: INodeService = {
console.log(`Pin progress ${cid}: ${block.toString()}`);
}
},
async fetch(cid: string) {
async fetch(cid: string, path?: string) {
const data: Uint8Array[] = [];
for await (const buf of fs.cat(CID.parse(cid))) {
for await (const buf of fs.cat(CID.parse(cid), {
path
})) {
data.push(buf);
}
return concat(data);
Expand Down
8 changes: 3 additions & 5 deletions packages/interfaces/src/Services/IIpfsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,8 @@ export interface IIpfsService {
ls(cid: string): Promise<IFileInfo[]>;

/**
* Gets a url of the file.
* @param cid cid of the file to get the url to.
* Lists all connected peers.
*/
toUrl(cid: string): string;

peers(): Promise<string[]>;

/**
Expand All @@ -54,6 +51,7 @@ export interface IIpfsService {
/**
* Fetches a block.
* @param cid cid of the block to fetch.
* @param path (optional) path inside the cid.
*/
fetch(cid: string): Promise<Uint8Array>;
fetch(cid: string, path?: string): Promise<Uint8Array>;
}
8 changes: 4 additions & 4 deletions packages/ui/src/components/atoms/FileInfoDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { Box, Stack, Typography } from '@mui/material';
import { IFileInfo, IIpfsService, IIpfsServiceSymbol, isPosterFeature } from 'ipmc-interfaces';
import { IFileInfo, isPosterFeature } from 'ipmc-interfaces';
import React from 'react';
import { useFileUrl } from '../../hooks';
import { useTitle } from '../../hooks/useTitle';
import { useService } from '../../context/AppContext';

export function FileInfoDisplay(props: { file: IFileInfo; }) {
const { file } = props;
const ipfs = useService<IIpfsService>(IIpfsServiceSymbol);
const name = useTitle(file);
const title = (
<Typography variant={'h3'}>{name}</Typography>
);
const posterUrl = useFileUrl(isPosterFeature(file) && file.posters.length > 0 ? file.posters[0]?.cid : undefined);

return isPosterFeature(file) && file.posters.length > 0 ? (
<Stack direction={'row'} spacing={1}>
<img src={ipfs.toUrl(file.posters[0].cid)} style={{ height: 250, flexGrow: 0 }} />
<img src={posterUrl.value} style={{ height: 250, flexGrow: 0 }} />
{title}
</Stack>
) : (
Expand Down
23 changes: 7 additions & 16 deletions packages/ui/src/components/organisms/VideoPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,20 @@ import { IIpfsService, IIpfsServiceSymbol, IVideoFile } from 'ipmc-interfaces';
import React from 'react';
import { useService } from '../../context';
import { useHotkey } from '../../hooks';
//@ts-ignore
import shaka from 'shaka-player';

function createShakaIpfsPlugin(ipfs: IIpfsService): shaka.extern.SchemePlugin {
return async (uri: string, request: shaka.extern.Request, requestType: shaka.net.NetworkingEngine.RequestType, progressUpdated: shaka.extern.ProgressUpdated, headersReceived: shaka.extern.HeadersReceived, config: shaka.extern.SchemePluginConfig) => {
const path = uri.substring(uri.indexOf('://') + 3);
const paths = path.split('/');
let cid = paths.shift()!;
const fullPath = uri.substring(uri.indexOf('://') + 3);
const paths = fullPath.split('/');
const cid = paths.shift()!;
const path = paths.join('/');

while (paths.length > 0) {
const filename = paths.shift();
const files = await ipfs.ls(cid);

const file = files.find(f => f.name === filename);
if (!file) {
throw new Error('File Not Found');
}
cid = file.cid;
}

console.log(uri, path, cid, request, requestType, config);
console.log(uri, fullPath, path, cid, request, requestType, config);
headersReceived({});

const data = await ipfs.fetch(cid);
const data = await ipfs.fetch(cid, path);

return {
uri: uri,
Expand Down
9 changes: 4 additions & 5 deletions packages/webui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,6 @@ export function App() {
await blockstore.close();
await datastore.close();
},
toUrl(cid: string) {
return `TODO ${cid}`;
},
peers() {
return Promise.resolve(helia.libp2p.getConnections().map(p => p.remoteAddr.toString()));
},
Expand All @@ -175,9 +172,11 @@ export function App() {
console.log(`Pin progress ${cid}: ${block.toString()}`);
}
},
async fetch(cid: string) {
async fetch(cid: string, path?: string) {
const data: Uint8Array[] = [];
for await (const buf of fs.cat(CID.parse(cid))) {
for await (const buf of fs.cat(CID.parse(cid), {
path
})) {
data.push(buf);
}
return concat(data);
Expand Down
Loading

0 comments on commit ef50f9c

Please sign in to comment.