|
|
@@ -1,19 +1,28 @@ |
|
|
|
import { h, Component } from 'preact';
|
|
|
|
import WBTable from 'wb-table';
|
|
|
|
import WBBreadcrumbs from 'wb-breadcrumbs';
|
|
|
|
import { WBManifestReader } from 'wb-collection-manifest';
|
|
|
|
//import WBManifestReader from 'wb-manifest-reader';
|
|
|
|
// import { WBManifestReader } from 'wb-collection-manifest';
|
|
|
|
// import WBManifestReader from 'wb-manifest-reader';
|
|
|
|
import WBPagination from 'wb-pagination';
|
|
|
|
import makeArvadosRequest from 'make-arvados-request';
|
|
|
|
import wbDownloadFile from 'wb-download-file';
|
|
|
|
|
|
|
|
function unescapeName(name) {
|
|
|
|
return name.replace(/(\\\\|\\[0-9]{3})/g,
|
|
|
|
(_, $1) => ($1 === '\\\\' ? '\\' : String.fromCharCode(parseInt($1.substr(1), 8))));
|
|
|
|
}
|
|
|
|
|
|
|
|
class WBCollectionContent extends Component {
|
|
|
|
constructor(...args) {
|
|
|
|
super(...args);
|
|
|
|
this.state.rows = [];
|
|
|
|
this.state.manifestReader = null;
|
|
|
|
this.state.manifestWorker = new Worker('/js/wb-manifest-worker.js');
|
|
|
|
this.state.manifestWorker.onerror = (e => console.log(e));
|
|
|
|
this.state.loaded = 0;
|
|
|
|
this.state.total = 0;
|
|
|
|
this.state.mode = 'manifestDownload';
|
|
|
|
this.state.parsedStreams = 0;
|
|
|
|
this.state.totalStreams = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
getUrl(params) {
|
|
|
@@ -26,7 +35,8 @@ class WBCollectionContent extends Component { |
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
let { arvHost, arvToken } = this.props.app.state;
|
|
|
|
let { uuid } = this.props;
|
|
|
|
let { uuid, collectionPath } = this.props;
|
|
|
|
let { manifestWorker } = this.state;
|
|
|
|
|
|
|
|
let select = [ 'manifest_text' ];
|
|
|
|
let prom = makeArvadosRequest(arvHost, arvToken,
|
|
|
@@ -36,25 +46,90 @@ class WBCollectionContent extends Component { |
|
|
|
this.setState({ 'loaded': e.loaded, 'total': e.total });
|
|
|
|
} });
|
|
|
|
prom = prom.then(xhr => {
|
|
|
|
this.state.manifestReader = new WBManifestReader(xhr.response.manifest_text);
|
|
|
|
this.prepareRows();
|
|
|
|
const streams = xhr.response.manifest_text.split('\n');
|
|
|
|
const paths = streams.filter(s => s).map(s => {
|
|
|
|
const n = s.indexOf(' ');
|
|
|
|
return unescapeName(s.substr(0, n));
|
|
|
|
});
|
|
|
|
|
|
|
|
let prom_1 = new Promise(accept => accept());
|
|
|
|
|
|
|
|
prom_1 = prom_1.then(() => {
|
|
|
|
const prom_2 = new Promise(accept => {
|
|
|
|
manifestWorker.onmessage = () => accept();
|
|
|
|
manifestWorker.postMessage([ 'precreatePaths', paths ]);
|
|
|
|
this.setState({
|
|
|
|
'totalStreams': streams.length,
|
|
|
|
'parsedStreams': 0,
|
|
|
|
'mode': 'manifestParse'
|
|
|
|
})
|
|
|
|
})
|
|
|
|
return prom_2;
|
|
|
|
});
|
|
|
|
|
|
|
|
for (let i = 0; i < streams.length; i++) {
|
|
|
|
|
|
|
|
prom_1 = prom_1.then(() => {
|
|
|
|
const prom_2 = new Promise(accept => {
|
|
|
|
manifestWorker.onmessage = () => accept();
|
|
|
|
manifestWorker.postMessage([ 'parseStream', streams[i] ]);
|
|
|
|
});
|
|
|
|
|
|
|
|
return prom_2;
|
|
|
|
});
|
|
|
|
|
|
|
|
prom_1 = prom_1.then(() => {
|
|
|
|
const prom_2 = new Promise(accept => {
|
|
|
|
manifestWorker.onmessage = (e) => accept(e);
|
|
|
|
manifestWorker.postMessage([ 'listDirectory', '.' + this.props.collectionPath, true ]);
|
|
|
|
if (i % 1000 === 0)
|
|
|
|
console.log(i + '/' + streams.length);
|
|
|
|
});
|
|
|
|
return prom_2;
|
|
|
|
});
|
|
|
|
|
|
|
|
prom_1 = prom_1.then(e => {
|
|
|
|
this.prepareRows(e.data[1]);
|
|
|
|
this.setState({ 'parsedStreams': (i + 1) });
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
prom_1 = prom_1.then(() => this.setState({
|
|
|
|
'mode': 'browsingReady'
|
|
|
|
}));
|
|
|
|
|
|
|
|
return prom_1;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
componentWillReceiveProps(nextProps) {
|
|
|
|
const { manifestWorker, mode } = this.state;
|
|
|
|
const { collectionPath } = nextProps;
|
|
|
|
if (mode === 'browsingReady') {
|
|
|
|
this.state.mode = 'waitForListing';
|
|
|
|
let prom = new Promise(accept => {
|
|
|
|
manifestWorker.onmessage = (e) => accept(e);
|
|
|
|
manifestWorker.postMessage([ 'listDirectory', '.' + collectionPath ]);
|
|
|
|
});
|
|
|
|
|
|
|
|
prom = prom.then(e => {
|
|
|
|
this.state.mode = 'browsingReady';
|
|
|
|
this.prepareRows(e.data[1]);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
this.props = nextProps;
|
|
|
|
this.prepareRows();
|
|
|
|
// this.prepareRows();
|
|
|
|
}
|
|
|
|
|
|
|
|
prepareRows() {
|
|
|
|
let { manifestReader } = this.state;
|
|
|
|
prepareRows(listing) {
|
|
|
|
let { manifestReader, mode } = this.state;
|
|
|
|
let { collectionPath, page, itemsPerPage } = this.props;
|
|
|
|
let { arvHost, arvToken } = this.props.app.state;
|
|
|
|
|
|
|
|
//path = path.split('/');
|
|
|
|
//path = [ '.' ].concat(path);
|
|
|
|
|
|
|
|
let listing = manifestReader.listDirectory('.' + collectionPath)
|
|
|
|
//let listing = manifestReader.listDirectory('.' + collectionPath)
|
|
|
|
const numPages = Math.ceil(listing.length / itemsPerPage);
|
|
|
|
listing = listing.slice(page * itemsPerPage,
|
|
|
|
page * itemsPerPage + itemsPerPage);
|
|
|
@@ -71,52 +146,73 @@ class WBCollectionContent extends Component { |
|
|
|
item[1],
|
|
|
|
'File',
|
|
|
|
filesize(item[2]),
|
|
|
|
(<div>
|
|
|
|
<button class="btn btn-outline-primary mx-1" title="Download"
|
|
|
|
onclick={ () => {
|
|
|
|
let prom = wbDownloadFile(arvHost, arvToken, manifestReader,
|
|
|
|
'.' + collectionPath + '/' + item[1]);
|
|
|
|
prom = prom.then(blocks => {
|
|
|
|
const blob = new Blob(blocks);
|
|
|
|
const a = document.createElement('a');
|
|
|
|
a.name = item[1];
|
|
|
|
a.href = window.URL.createObjectURL(blob);
|
|
|
|
a.click();
|
|
|
|
});
|
|
|
|
} }><i class="fas fa-download"></i></button>
|
|
|
|
|
|
|
|
<button class="btn btn-outline-primary mx-1" title="View"
|
|
|
|
onclick={ () => {
|
|
|
|
let prom = wbDownloadFile(arvHost, arvToken, manifestReader,
|
|
|
|
'.' + collectionPath + '/' + item[1]);
|
|
|
|
prom = prom.then(blocks => {
|
|
|
|
const blob = new Blob(blocks);
|
|
|
|
window.open(window.URL.createObjectURL(blob));
|
|
|
|
});
|
|
|
|
} }><i class="far fa-eye"></i></button>
|
|
|
|
</div>)
|
|
|
|
( (mode === 'browsingReady') ? (
|
|
|
|
<div>
|
|
|
|
<button class="btn btn-outline-primary mx-1" title="Download"
|
|
|
|
onclick={ () => {
|
|
|
|
let prom = wbDownloadFile(arvHost, arvToken, manifestReader,
|
|
|
|
'.' + collectionPath + '/' + item[1]);
|
|
|
|
prom = prom.then(blocks => {
|
|
|
|
const blob = new Blob(blocks);
|
|
|
|
const a = document.createElement('a');
|
|
|
|
a.name = item[1];
|
|
|
|
a.href = window.URL.createObjectURL(blob);
|
|
|
|
a.click();
|
|
|
|
});
|
|
|
|
} }><i class="fas fa-download"></i></button>
|
|
|
|
|
|
|
|
<button class="btn btn-outline-primary mx-1" title="View"
|
|
|
|
onclick={ () => {
|
|
|
|
let prom = wbDownloadFile(arvHost, arvToken, manifestReader,
|
|
|
|
'.' + collectionPath + '/' + item[1]);
|
|
|
|
prom = prom.then(blocks => {
|
|
|
|
const blob = new Blob(blocks);
|
|
|
|
window.open(window.URL.createObjectURL(blob));
|
|
|
|
});
|
|
|
|
} }><i class="far fa-eye"></i></button>
|
|
|
|
</div>
|
|
|
|
) : null)
|
|
|
|
]
|
|
|
|
))
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
render({ collectionPath, page }, { manifestReader, rows, numPages, loaded, total }) {
|
|
|
|
render({ collectionPath, page }, { manifestReader, rows,
|
|
|
|
numPages, loaded, total, mode, parsedStreams, totalStreams }) {
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<WBBreadcrumbs items={ ('.' + collectionPath).split('/') } />
|
|
|
|
|
|
|
|
{ manifestReader ? (
|
|
|
|
<div>
|
|
|
|
<WBTable columns={ [ 'Name', 'Type', 'Size', 'Actions' ] }
|
|
|
|
rows={ rows } />
|
|
|
|
|
|
|
|
<WBPagination activePage={ page } numPages={ numPages }
|
|
|
|
getPageUrl={ page => this.getUrl({ 'page': page }) } />
|
|
|
|
</div>
|
|
|
|
) : (
|
|
|
|
<div>Downloading manifest: { filesize(loaded) }</div>
|
|
|
|
) }
|
|
|
|
|
|
|
|
{ (mode === 'manifestDownload') ?
|
|
|
|
(
|
|
|
|
<div class="container-fluid">
|
|
|
|
<div>Downloading manifest: { filesize(loaded) }</div>
|
|
|
|
<div class="progress">
|
|
|
|
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar"
|
|
|
|
aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"></div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
) : (
|
|
|
|
<div>
|
|
|
|
{ mode === 'manifestParse' ? (
|
|
|
|
<div class="container-fluid mb-2">
|
|
|
|
<div>Parsing manifest: { parsedStreams }/{ totalStreams }</div>
|
|
|
|
<div class="progress">
|
|
|
|
<div class="progress-bar progress-bar-striped progress-bar-animated bg-success" role="progressbar"
|
|
|
|
aria-valuenow={ totalStreams } aria-valuemin="0" aria-valuemax={ parsedStreams } style={ 'width: ' + Math.round(parsedStreams * 100 / totalStreams) + '%' }></div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
) : null }
|
|
|
|
|
|
|
|
<WBTable columns={ [ 'Name', 'Type', 'Size', 'Actions' ] }
|
|
|
|
rows={ rows } />
|
|
|
|
|
|
|
|
<WBPagination activePage={ page } numPages={ numPages }
|
|
|
|
getPageUrl={ page => this.getUrl({ 'page': page }) } />
|
|
|
|
</div>
|
|
|
|
) }
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|