diff --git a/app/src/api.js b/app/src/api.js
index 508471e9..5481b7a0 100644
--- a/app/src/api.js
+++ b/app/src/api.js
@@ -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();
diff --git a/app/src/components/FileBrowser.js b/app/src/components/FileBrowser.js
index 5dd64d83..0886fd7c 100644
--- a/app/src/components/FileBrowser.js
+++ b/app/src/components/FileBrowser.js
@@ -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
onSelect(folder['path'])}/>
- onFolderClick(path)} className='file-path' colSpan={2}>
+ onFolderClick(path)} className='file-path' colSpan={2} disabled={is_empty}>
{depthIndentation(pathWithNoTrailingSlash)}
{is_empty ? : }
{name}
;
- 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 =
-
-
-
- ;
- } else if (mimetype.startsWith('image/')) {
- modalBody =
-
-
-
- ;
- }
-
- if (modalBody) {
- // Create modal that will instantly display the file.
- setModal( setModal(null)}
- >
- {modalBody}
-
- window.open(url)}>Open
- setModal(null)}>Close
-
- )
- } 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}
diff --git a/app/src/components/FilePreview.js b/app/src/components/FilePreview.js
new file mode 100644
index 00000000..7dee4b93
--- /dev/null
+++ b/app/src/components/FilePreview.js
@@ -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
+
+
+
+
+}
+
+function getImageModal(path) {
+ const url = getMediaPathURL(path);
+ return
+
+
+
+
+}
+
+function getEpubModal(path) {
+ const downloadURL = getDownloadPathURL(path);
+ const viewerUrl = `/epub.html?url=${downloadURL}`;
+ return
+
+
+
+
+}
+
+function getVideoModal(path) {
+ const url = getMediaPathURL(path);
+ return
+
+
+}
+
+export function useFilePreview() {
+ const [path, setPath] = React.useState(null);
+ const [modal, setModal] = React.useState(null);
+
+ function setModalContent(content, url, downloadURL) {
+ setModal( setModal(null)}
+ >
+ {content}
+
+ {downloadURL &&
+ window.open(downloadURL)} floated='left'>Download}
+ window.open(url)}>Open
+ setPath(null)}>Close
+
+ );
+ }
+
+ 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};
+}
diff --git a/wrolpi/files/lib.py b/wrolpi/files/lib.py
index ce35d4c7..349f0342 100644
--- a/wrolpi/files/lib.py
+++ b/wrolpi/files/lib.py
@@ -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