Creating FilePreview for FileBrowser.

This commit is contained in:
Roland 2023-02-15 17:40:57 -07:00
parent 69e673e0b8
commit a2e1cfb0eb
No known key found for this signature in database
GPG key ID: 9D6DBD3ACF11AB93
4 changed files with 130 additions and 56 deletions

View file

@ -520,6 +520,7 @@ export async function refreshDirectoryFiles(directory) {
}
export async function getFiles(directories) {
console.debug(`getFiles ${directories}`);
let body = {directories: directories || []};
let response = await apiPost(`${API_URI}/files`, body);
let {files} = await response.json();

View file

@ -20,6 +20,7 @@ import _ from 'lodash';
import {SortableTable} from "./SortableTable";
import {useBrowseFiles} from "../hooks/customHooks";
import {DirectoryRefreshButton, FilesRefreshButton} from "./Files";
import {useFilePreview} from "./FilePreview";
function depthIndentation(path) {
// Repeated spaces for every folder a path is in.
@ -36,13 +37,13 @@ function Folder({folder, onFolderClick, sortData, selectedPath, onFileClick, onS
<TableCell collapsing>
<Checkbox checked={selectedPath === folder['path']} onChange={() => onSelect(folder['path'])}/>
</TableCell>
<TableCell onClick={() => onFolderClick(path)} className='file-path' colSpan={2}>
<TableCell onClick={() => onFolderClick(path)} className='file-path' colSpan={2} disabled={is_empty}>
{depthIndentation(pathWithNoTrailingSlash)}
{is_empty ? <Icon name='folder outline'/> : <Icon name='folder'/>}
{name}
</TableCell>
</TableRow>;
if (children && !_.isEmpty(children)) {
if (children) {
// Folder has children, recursively display them.
children = sortData(children);
let childPaths = [];
@ -112,51 +113,7 @@ export function FileBrowser() {
key: 'path', text: 'Path', sortBy: i => i['path'].toLowerCase()
}, {key: 'size', text: 'Size', sortBy: 'size'},];
const [modal, setModal] = React.useState(null);
const onFileClick = (path) => {
const {mimetype} = path;
const url = `/media/${path['path']}`;
let modalBody;
if (mimetype === 'text/plain' || mimetype === 'text/html') {
modalBody = <ModalContent>
<div className='full-height'>
<iframe title='textModal' src={url}
style={{
height: '100%',
width: '100%',
border: 'none',
position: 'absolute',
top: 0,
}}/>
</div>
</ModalContent>;
} else if (mimetype.startsWith('image/')) {
modalBody = <ModalContent>
<a href={url}>
<Image src={url}/>
</a>
</ModalContent>;
}
if (modalBody) {
// Create modal that will instantly display the file.
setModal(<Modal closeIcon
size='fullscreen'
open={true}
onClose={() => setModal(null)}
>
{modalBody}
<ModalActions>
<SButton color='blue' onClick={() => window.open(url)}>Open</SButton>
<SButton onClick={() => setModal(null)}>Close</SButton>
</ModalActions>
</Modal>)
} else {
// No special handler for this file type, just open it.
setModal(null);
window.open(url);
}
}
const {modal, setPath} = useFilePreview();
const onSelect = (path) => {
if (path.endsWith('/') && selectedPath === path) {
@ -241,7 +198,7 @@ export function FileBrowser() {
key={i['key']}
path={i}
onFolderClick={onFolderClick}
onFileClick={onFileClick}
onFileClick={(i) => setPath(i)}
sortData={sortData}
selectedPath={selectedPath}
onSelect={onSelect}

View file

@ -0,0 +1,112 @@
import React, {useEffect} from "react";
import _ from "lodash";
import {Button as SButton, Image, Modal, ModalActions, ModalContent} from "semantic-ui-react";
function getMediaPathURL(path) {
return `/media/${encodeURIComponent(path['path'])}`;
}
function getDownloadPathURL(path) {
return `/download/${encodeURIComponent(path['path'])}`;
}
function getEpubViewerURL(path) {
return `/epub.html?url=${getDownloadPathURL(path)}`;
}
function getIframeModal(path) {
const url = getMediaPathURL(path);
return <ModalContent>
<div className='full-height'>
<iframe title='textModal' src={url}
style={{
height: '100%', width: '100%', border: 'none', position: 'absolute', top: 0,
}}/>
</div>
</ModalContent>
}
function getImageModal(path) {
const url = getMediaPathURL(path);
return <ModalContent>
<a href={url}>
<Image src={url}/>
</a>
</ModalContent>
}
function getEpubModal(path) {
const downloadURL = getDownloadPathURL(path);
const viewerUrl = `/epub.html?url=${downloadURL}`;
return <ModalContent>
<div className='full-height'>
<iframe title='textModal' src={viewerUrl}
style={{
height: '100%', width: '100%', border: 'none', position: 'absolute', top: 0,
}}/>
</div>
</ModalContent>
}
function getVideoModal(path) {
const url = getMediaPathURL(path);
return <ModalContent>
<video controls
autoPlay={true}
id="player"
playsInline={true}
style={{maxWidth: '100%'}}
>
<source src={url} type="video/mp4"/>
</video>
</ModalContent>
}
export function useFilePreview() {
const [path, setPath] = React.useState(null);
const [modal, setModal] = React.useState(null);
function setModalContent(content, url, downloadURL) {
setModal(<Modal closeIcon
size='fullscreen'
open={true}
onClose={() => setModal(null)}
>
{content}
<ModalActions>
{downloadURL &&
<SButton color='yellow' onClick={() => window.open(downloadURL)} floated='left'>Download</SButton>}
<SButton color='blue' onClick={() => window.open(url)}>Open</SButton>
<SButton onClick={() => setPath(null)}>Close</SButton>
</ModalActions>
</Modal>);
}
useEffect(() => {
setModal(null);
if (path && !_.isEmpty(path)) {
const {mimetype, size} = path;
console.debug(`useFilePreview path=${path['path']} mimetype=${mimetype}`);
const url = getMediaPathURL(path);
const downloadURL = getDownloadPathURL(path);
if (mimetype.startsWith('text/') && size >= 10000) {
// Large text files should be downloaded.
window.open(downloadURL);
} else if (mimetype.startsWith('text/') || mimetype.startsWith('application/json')) {
setModalContent(getIframeModal(path), url, downloadURL);
} else if (mimetype.startsWith('video/')) {
setModalContent(getVideoModal(path), url, downloadURL);
} else if (mimetype.startsWith('application/epub')) {
const viewerURL = getEpubViewerURL(path);
setModalContent(getEpubModal(path), viewerURL, downloadURL);
} else if (mimetype.startsWith('image/')) {
setModalContent(getImageModal(path), url);
} else {
// No special handler for this file type, just open it.
window.open(downloadURL);
}
}
}, [path]);
return {modal, setPath};
}

View file

@ -37,12 +37,13 @@ except ImportError:
logger = logger.getChild(__name__)
__all__ = ['list_directories_contents', 'delete_file', 'split_path_stem_and_suffix', 'refresh_files', 'search_files',
'get_mimetype',
'split_file_name_words']
'get_mimetype', 'split_file_name_words']
@cachetools.func.ttl_cache(10_000, 30.0)
def _get_file_dict(file: pathlib.Path) -> Dict:
def _get_file_dict(file: pathlib.Path,
directories_cache: str, # Used to cache by requested directories.
) -> Dict:
media_directory = get_media_directory()
return dict(
path=file.relative_to(media_directory),
@ -52,7 +53,9 @@ def _get_file_dict(file: pathlib.Path) -> Dict:
@cachetools.func.ttl_cache(10_000, 30.0)
def _get_directory_dict(directory: pathlib.Path) -> Dict:
def _get_directory_dict(directory: pathlib.Path,
directories_cache: str, # Used to cache by requested directories.
) -> Dict:
media_directory = get_media_directory()
return dict(
path=f'{directory.relative_to(media_directory)}/',
@ -61,16 +64,17 @@ def _get_directory_dict(directory: pathlib.Path) -> Dict:
def _get_recursive_directory_dict(directory: pathlib.Path, directories: List[pathlib.Path]) -> Dict:
d = _get_directory_dict(directory)
directories_cache = str(sorted(directories))
d = _get_directory_dict(directory, directories_cache)
if directory in directories:
children = dict()
for path in directory.iterdir():
if path.is_dir() and path in directories:
children[f'{path.name}/'] = _get_recursive_directory_dict(path, directories)
elif path.is_dir():
children[f'{path.name}/'] = _get_directory_dict(path)
children[f'{path.name}/'] = _get_directory_dict(path, directories_cache)
else:
children[path.name] = _get_file_dict(path)
children[path.name] = _get_file_dict(path, directories_cache)
d['children'] = children
return d
@ -101,7 +105,7 @@ def list_directories_contents(directories_: List[str]) -> Dict:
if path.is_dir():
paths[f'{path.name}/'] = _get_recursive_directory_dict(path, directories)
else:
paths[path.name] = _get_file_dict(path)
paths[path.name] = _get_file_dict(path, str(directories))
return paths