import { selectClaimsByUri, selectIsFetchingClaimListMine, selectMyClaims, selectClaimsById, makeSelectContentTypeForUri, makeSelectClaimForUri, } from 'redux/selectors/claims'; import { createSelector } from 'reselect'; import { buildURI } from 'lbryURI'; import Lbry from 'lbry'; import { PAGE_SIZE } from 'constants/claim'; export const selectState = state => state.fileInfo || {}; export const selectFileInfosByOutpoint = createSelector( selectState, state => state.byOutpoint || {} ); export const selectIsFetchingFileList = createSelector( selectState, state => state.isFetchingFileList ); export const selectIsFetchingFileListDownloadedOrPublished = createSelector( selectIsFetchingFileList, selectIsFetchingClaimListMine, (isFetchingFileList, isFetchingClaimListMine) => isFetchingFileList || isFetchingClaimListMine ); export const makeSelectFileInfoForUri = uri => createSelector( selectClaimsByUri, selectFileInfosByOutpoint, (claims, byOutpoint) => { const claim = claims[uri]; const outpoint = claim ? `${claim.txid}:${claim.nout}` : undefined; return outpoint ? byOutpoint[outpoint] : undefined; } ); export const selectDownloadingByOutpoint = createSelector( selectState, state => state.downloadingByOutpoint || {} ); export const makeSelectDownloadingForUri = uri => createSelector( selectDownloadingByOutpoint, makeSelectFileInfoForUri(uri), (byOutpoint, fileInfo) => { if (!fileInfo) return false; return byOutpoint[fileInfo.outpoint]; } ); export const selectUrisLoading = createSelector( selectState, state => state.fetching || {} ); export const makeSelectLoadingForUri = uri => createSelector( selectUrisLoading, makeSelectClaimForUri(uri), (fetchingByOutpoint, claim) => { if (!claim) { return false; } const { txid, nout } = claim; const outpoint = `${txid}:${nout}`; const isFetching = fetchingByOutpoint[outpoint]; return isFetching; } ); export const selectFileInfosDownloaded = createSelector( selectFileInfosByOutpoint, selectMyClaims, (byOutpoint, myClaims) => Object.values(byOutpoint).filter(fileInfo => { const myClaimIds = myClaims.map(claim => claim.claim_id); return ( fileInfo && myClaimIds.indexOf(fileInfo.claim_id) === -1 && (fileInfo.completed || fileInfo.written_bytes > 0 || fileInfo.blobs_completed > 0) ); }) ); // export const selectFileInfoForUri = (state, props) => { // const claims = selectClaimsByUri(state), // claim = claims[props.uri], // fileInfos = selectAllFileInfos(state), // outpoint = claim ? `${claim.txid}:${claim.nout}` : undefined; // return outpoint && fileInfos ? fileInfos[outpoint] : undefined; // }; export const selectDownloadingFileInfos = createSelector( selectDownloadingByOutpoint, selectFileInfosByOutpoint, (downloadingByOutpoint, fileInfosByOutpoint) => { const outpoints = Object.keys(downloadingByOutpoint); const fileInfos = []; outpoints.forEach(outpoint => { const fileInfo = fileInfosByOutpoint[outpoint]; if (fileInfo) fileInfos.push(fileInfo); }); return fileInfos; } ); export const selectTotalDownloadProgress = createSelector( selectDownloadingFileInfos, fileInfos => { const progress = []; fileInfos.forEach(fileInfo => { progress.push((fileInfo.written_bytes / fileInfo.total_bytes) * 100); }); const totalProgress = progress.reduce((a, b) => a + b, 0); if (fileInfos.length > 0) return totalProgress / fileInfos.length / 100.0; return -1; } ); export const selectFileListPublishedSort = createSelector( selectState, state => state.fileListPublishedSort ); export const selectFileListDownloadedSort = createSelector( selectState, state => state.fileListDownloadedSort ); export const selectDownloadedUris = createSelector( selectFileInfosDownloaded, // We should use permament_url but it doesn't exist in file_list info => info.slice().map(claim => `lbry://${claim.claim_name}#${claim.claim_id}`) ); export const makeSelectMediaTypeForUri = uri => createSelector( makeSelectFileInfoForUri(uri), makeSelectContentTypeForUri(uri), (fileInfo, contentType) => { if (!fileInfo && !contentType) { return undefined; } const fileName = fileInfo && fileInfo.file_name; return Lbry.getMediaType(contentType, fileName); } ); export const makeSelectUriIsStreamable = uri => createSelector( makeSelectMediaTypeForUri(uri), mediaType => { const isStreamable = ['audio', 'video', 'image'].indexOf(mediaType) !== -1; return isStreamable; } ); export const makeSelectDownloadPathForUri = uri => createSelector( makeSelectFileInfoForUri(uri), fileInfo => { return fileInfo && fileInfo.download_path; } ); export const makeSelectFilePartlyDownloaded = uri => createSelector( makeSelectFileInfoForUri(uri), fileInfo => { if (!fileInfo) { return false; } return fileInfo.written_bytes > 0 || fileInfo.blobs_completed > 0; } ); export const makeSelectFileNameForUri = uri => createSelector( makeSelectFileInfoForUri(uri), fileInfo => { return fileInfo && fileInfo.file_name; } ); export const selectDownloadUrlsCount = createSelector( selectDownloadedUris, uris => uris.length ); function filterFileInfos(fileInfos, query) { if (query) { const queryMatchRegExp = new RegExp(query, 'i'); return fileInfos.filter(fileInfo => { const { metadata } = fileInfo; return ( (metadata.title && metadata.title.match(queryMatchRegExp)) || (fileInfo.channel_name && fileInfo.channel_name.match(queryMatchRegExp)) || (fileInfo.claim_name && fileInfo.claim_name.match(queryMatchRegExp)) ); }); } return fileInfos; } export const makeSelectSearchDownloadUrlsForPage = (query, page = 1) => createSelector( selectFileInfosDownloaded, fileInfos => { const matchingFileInfos = filterFileInfos(fileInfos, query); const start = (Number(page) - 1) * Number(PAGE_SIZE); const end = Number(page) * Number(PAGE_SIZE); return matchingFileInfos && matchingFileInfos.length ? matchingFileInfos.slice(start, end).map(fileInfo => buildURI({ streamName: fileInfo.claim_name, channelName: fileInfo.channel_name, channelClaimId: fileInfo.channel_claim_id, }) ) : []; } ); export const makeSelectSearchDownloadUrlsCount = query => createSelector( selectFileInfosDownloaded, fileInfos => { return fileInfos && fileInfos.length ? filterFileInfos(fileInfos, query).length : 0; } );