Downloads
This commit is contained in:
@@ -167,6 +167,10 @@ MimeTypes::MimeTypes()
|
|||||||
AddMimeType("MPG", "video/mp2p");
|
AddMimeType("MPG", "video/mp2p");
|
||||||
AddMimeType("MPEG", "video/mp2p");
|
AddMimeType("MPEG", "video/mp2p");
|
||||||
|
|
||||||
|
// custom.
|
||||||
|
AddMimeType("NAM", "application/x-nam+json");
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const MimeTypes&MimeTypes::instance()
|
const MimeTypes&MimeTypes::instance()
|
||||||
|
|||||||
@@ -2879,3 +2879,8 @@ void PiPedalModel::OnAlsaDriverTerminatedAbnormally() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PiPedalModel::IsInUploadsDirectory(const std::string &path)
|
||||||
|
{
|
||||||
|
return !storage.IsInUploadsDirectory(path);
|
||||||
|
}
|
||||||
@@ -456,6 +456,8 @@ namespace pipedal
|
|||||||
std::string RenameFilePropertyFile(const std::string &oldRelativePath, const std::string &newRelativePath, const UiFileProperty &uiFileProperty);
|
std::string RenameFilePropertyFile(const std::string &oldRelativePath, const std::string &newRelativePath, const UiFileProperty &uiFileProperty);
|
||||||
FilePropertyDirectoryTree::ptr GetFilePropertydirectoryTree(const UiFileProperty &uiFileProperty,const std::string&selectedPath);
|
FilePropertyDirectoryTree::ptr GetFilePropertydirectoryTree(const UiFileProperty &uiFileProperty,const std::string&selectedPath);
|
||||||
|
|
||||||
|
bool IsInUploadsDirectory(const std::string &path);
|
||||||
|
|
||||||
std::string UploadUserFile(const std::string &directory, int64_t instanceId, const std::string &patchProperty, const std::string &filename, std::istream &inputStream, size_t streamLength);
|
std::string UploadUserFile(const std::string &directory, int64_t instanceId, const std::string &patchProperty, const std::string &filename, std::istream &inputStream, size_t streamLength);
|
||||||
uint64_t CreateNewPreset();
|
uint64_t CreateNewPreset();
|
||||||
|
|
||||||
|
|||||||
+88
-14
@@ -153,7 +153,7 @@ namespace pipedal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <class Clock, class Duration>
|
template <class Clock, class Duration>
|
||||||
RingBufferStatus readWait_until(size_t size,const std::chrono::time_point<Clock, Duration> &time_point)
|
RingBufferStatus readWait_until(size_t size, const std::chrono::time_point<Clock, Duration> &time_point)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@@ -161,7 +161,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
std::unique_lock lock(mutex);
|
std::unique_lock lock(mutex);
|
||||||
size_t available = readSpace_();
|
size_t available = readSpace_();
|
||||||
if (available >= size)
|
if (available >= size)
|
||||||
{
|
{
|
||||||
return RingBufferStatus::Ready;
|
return RingBufferStatus::Ready;
|
||||||
}
|
}
|
||||||
@@ -215,12 +215,69 @@ namespace pipedal
|
|||||||
return (size_t)size;
|
return (size_t)size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
size_t readSpace()
|
size_t readSpace()
|
||||||
{
|
{
|
||||||
std::unique_lock lock(mutex);
|
std::unique_lock lock(mutex);
|
||||||
return readSpace_();
|
return readSpace_();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool write_packet(size_t bytes, void *data)
|
||||||
|
{
|
||||||
|
if (MULTI_WRITER)
|
||||||
|
{
|
||||||
|
std::lock_guard writeLock{writeMutex};
|
||||||
|
if (writeSpace() < bytes + sizeof(bytes))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t index = this->writePosition;
|
||||||
|
for (size_t i = 0; i < sizeof(bytes); ++i)
|
||||||
|
{
|
||||||
|
buffer[(index++() & ringBufferMask] = ((uint8_t*)(&bytes))[i];
|
||||||
|
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < bytes; ++i)
|
||||||
|
{
|
||||||
|
buffer[(index++) & ringBufferMask] = data[i];
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
this->writePosition = (index) & ringBufferMask;
|
||||||
|
}
|
||||||
|
if (SEMAPHORE_READER)
|
||||||
|
{
|
||||||
|
cvRead.notify_all();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (writeSpace() < bytes + sizeof(bytes))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t index = this->writePosition;
|
||||||
|
for (size_t i = 0; i < sizeof(bytes); ++i)
|
||||||
|
{
|
||||||
|
buffer[(index++() & ringBufferMask] = ((uint8_t*)(&bytes))[i];
|
||||||
|
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < bytes; ++i)
|
||||||
|
{
|
||||||
|
buffer[(index++) & ringBufferMask] = data[i];
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
this->writePosition = (index) & ringBufferMask;
|
||||||
|
}
|
||||||
|
if (SEMAPHORE_READER)
|
||||||
|
{
|
||||||
|
cvRead.notify_all();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool write(size_t bytes, uint8_t *data)
|
bool write(size_t bytes, uint8_t *data)
|
||||||
{
|
{
|
||||||
if (MULTI_WRITER)
|
if (MULTI_WRITER)
|
||||||
@@ -273,7 +330,7 @@ namespace pipedal
|
|||||||
if (MULTI_WRITER)
|
if (MULTI_WRITER)
|
||||||
{
|
{
|
||||||
std::lock_guard guard(writeMutex);
|
std::lock_guard guard(writeMutex);
|
||||||
if (writeSpace() <= sizeof(bytes) + bytes +bytes2)
|
if (writeSpace() <= sizeof(bytes) + bytes + bytes2)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -340,6 +397,23 @@ namespace pipedal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t read_packet(size_t maxSize, void*data) {
|
||||||
|
size_t packet_size;
|
||||||
|
if (!read(sizeof(packet_size), (uint8_t*)&packet_size))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("RingBuffer::read_packet: failed to read packet size.");
|
||||||
|
}
|
||||||
|
if (packet_size > maxSize)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("RingBuffer::read_packet: packet size too large.");
|
||||||
|
}
|
||||||
|
if (!read(packet_size, (uint8_t*)data))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("RingBuffer::read_packet: failed to read packet data.");
|
||||||
|
}
|
||||||
|
return packet_size;
|
||||||
|
}
|
||||||
|
|
||||||
bool read(size_t bytes, uint8_t *data)
|
bool read(size_t bytes, uint8_t *data)
|
||||||
{
|
{
|
||||||
if (readSpace() < bytes)
|
if (readSpace() < bytes)
|
||||||
@@ -366,18 +440,20 @@ namespace pipedal
|
|||||||
|
|
||||||
delete[] buffer;
|
delete[] buffer;
|
||||||
}
|
}
|
||||||
bool isReadReady() {
|
bool isReadReady()
|
||||||
|
{
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
if (isReadReady_()) return true;
|
if (isReadReady_())
|
||||||
|
return true;
|
||||||
return !this->is_open;
|
return !this->is_open;
|
||||||
}
|
}
|
||||||
bool isReadReady(size_t size) {
|
bool isReadReady(size_t size)
|
||||||
|
{
|
||||||
size_t available = readSpace();
|
size_t available = readSpace();
|
||||||
return available >= size;
|
return available >= size;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
size_t readSpace_()
|
size_t readSpace_()
|
||||||
{
|
{
|
||||||
int64_t size = writePosition - readPosition;
|
int64_t size = writePosition - readPosition;
|
||||||
@@ -389,7 +465,7 @@ namespace pipedal
|
|||||||
uint32_t peekSize()
|
uint32_t peekSize()
|
||||||
{
|
{
|
||||||
volatile uint32_t result;
|
volatile uint32_t result;
|
||||||
uint8_t *p = (uint8_t*)&result;
|
uint8_t *p = (uint8_t *)&result;
|
||||||
size_t ix = this->readPosition;
|
size_t ix = this->readPosition;
|
||||||
for (size_t i = 0; i < sizeof(result); ++i)
|
for (size_t i = 0; i < sizeof(result); ++i)
|
||||||
{
|
{
|
||||||
@@ -400,14 +476,12 @@ namespace pipedal
|
|||||||
bool isReadReady_()
|
bool isReadReady_()
|
||||||
{
|
{
|
||||||
size_t available = readSpace_();
|
size_t available = readSpace_();
|
||||||
if (available < sizeof(uint32_t)) return false;
|
if (available < sizeof(uint32_t))
|
||||||
|
return false;
|
||||||
// peak to get the size!
|
// peak to get the size!
|
||||||
uint32_t packetSize = peekSize();
|
uint32_t packetSize = peekSize();
|
||||||
return packetSize+sizeof(uint32_t) <= available;
|
return packetSize + sizeof(uint32_t) <= available;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -327,6 +327,7 @@ namespace pipedal
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void write(RingBufferCommand command, const T &value, size_t dataLength, uint8_t *variableData)
|
void write(RingBufferCommand command, const T &value, size_t dataLength, uint8_t *variableData)
|
||||||
{
|
{
|
||||||
|
|||||||
+46
-1
@@ -34,6 +34,7 @@
|
|||||||
#include "PresetBundle.hpp"
|
#include "PresetBundle.hpp"
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
#include "HotspotManager.hpp"
|
#include "HotspotManager.hpp"
|
||||||
|
#include "MimeTypes.hpp"
|
||||||
|
|
||||||
#define OLD_PRESET_EXTENSION ".piPreset"
|
#define OLD_PRESET_EXTENSION ".piPreset"
|
||||||
#define PRESET_EXTENSION ".piPreset"
|
#define PRESET_EXTENSION ".piPreset"
|
||||||
@@ -51,6 +52,22 @@ using namespace pipedal;
|
|||||||
using namespace boost::system;
|
using namespace boost::system;
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static bool HasDotDot(const std::filesystem::path &path) {
|
||||||
|
for (auto &part : path)
|
||||||
|
{
|
||||||
|
if (part == "..") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (part == ".")
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
class UserUploadResponse
|
class UserUploadResponse
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -63,6 +80,13 @@ JSON_MAP_REFERENCE(UserUploadResponse, errorMessage)
|
|||||||
JSON_MAP_REFERENCE(UserUploadResponse, path)
|
JSON_MAP_REFERENCE(UserUploadResponse, path)
|
||||||
JSON_MAP_END()
|
JSON_MAP_END()
|
||||||
|
|
||||||
|
static std::string GetMimeType(const std::filesystem::path&path) {
|
||||||
|
std::string extension = path.extension();
|
||||||
|
const MimeTypes&mimeTypes = MimeTypes::instance();
|
||||||
|
auto result = mimeTypes.MimeTypeFromExtension(extension);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
static bool IsZipFile(const std::filesystem::path &path)
|
static bool IsZipFile(const std::filesystem::path &path)
|
||||||
{
|
{
|
||||||
std::ifstream f(path);
|
std::ifstream f(path);
|
||||||
@@ -119,6 +143,10 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::string segment = request_uri.segment(1);
|
std::string segment = request_uri.segment(1);
|
||||||
|
if (segment == "downloadMediaFile")
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (segment == "uploadPluginPresets")
|
if (segment == "uploadPluginPresets")
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@@ -154,7 +182,7 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetContentDispositionHeader(const std::string &name, const std::string &extension)
|
static std::string GetContentDispositionHeader(const std::string &name, const std::string &extension)
|
||||||
{
|
{
|
||||||
std::string fileName = name.substr(0, 64) + extension;
|
std::string fileName = name.substr(0, 64) + extension;
|
||||||
std::stringstream s;
|
std::stringstream s;
|
||||||
@@ -423,6 +451,23 @@ public:
|
|||||||
{
|
{
|
||||||
std::string segment = request_uri.segment(1);
|
std::string segment = request_uri.segment(1);
|
||||||
|
|
||||||
|
if (segment == "downloadMediaFile") {
|
||||||
|
fs::path path = request_uri.query("path");
|
||||||
|
|
||||||
|
if (!fs::exists(path) || !this->model->IsInUploadsDirectory(path) || HasDotDot(path))
|
||||||
|
{
|
||||||
|
throw PiPedalException("File not found.");
|
||||||
|
}
|
||||||
|
auto mimeType = GetMimeType(path);
|
||||||
|
if (mimeType.empty()) {
|
||||||
|
throw PiPedalException("Can't download files of this type.");
|
||||||
|
}
|
||||||
|
res.set(HttpField::content_type, mimeType);
|
||||||
|
res.set(HttpField::cache_control, "no-cache");
|
||||||
|
std::string disposition = GetContentDispositionHeader(path.stem().string(), path.extension().string());
|
||||||
|
res.set(HttpField::content_disposition, disposition);
|
||||||
|
res.setBodyFile(path,false);
|
||||||
|
}
|
||||||
if (segment == "uploadPluginPresets")
|
if (segment == "uploadPluginPresets")
|
||||||
{
|
{
|
||||||
PluginPresets presets;
|
PluginPresets presets;
|
||||||
|
|||||||
Generated
+1
-1
@@ -29,7 +29,7 @@
|
|||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
"eslint": "^9.19.0",
|
"eslint": "^9.19.0",
|
||||||
"eslint-plugin-react-hooks": "^5.0.0",
|
"eslint-plugin-react-hooks": "^5.0.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.18",
|
"eslint-plugin-react-refresh": "^0.4.19",
|
||||||
"globals": "^15.14.0",
|
"globals": "^15.14.0",
|
||||||
"typescript": "~5.7.2",
|
"typescript": "~5.7.2",
|
||||||
"typescript-eslint": "^8.22.0",
|
"typescript-eslint": "^8.22.0",
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@
|
|||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
"eslint": "^9.19.0",
|
"eslint": "^9.19.0",
|
||||||
"eslint-plugin-react-hooks": "^5.0.0",
|
"eslint-plugin-react-hooks": "^5.0.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.18",
|
"eslint-plugin-react-refresh": "^0.4.19",
|
||||||
"globals": "^15.14.0",
|
"globals": "^15.14.0",
|
||||||
"typescript": "~5.7.2",
|
"typescript": "~5.7.2",
|
||||||
"typescript-eslint": "^8.22.0",
|
"typescript-eslint": "^8.22.0",
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {createStyles} from './WithStyles';
|
import { createStyles } from './WithStyles';
|
||||||
|
|
||||||
|
|
||||||
import { Theme } from '@mui/material/styles';
|
import { Theme } from '@mui/material/styles';
|
||||||
@@ -33,6 +33,7 @@ import { PiPedalModel, PiPedalModelFactory, FileEntry, BreadcrumbEntry, FileRequ
|
|||||||
import { isDarkMode } from './DarkMode';
|
import { isDarkMode } from './DarkMode';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import FileUploadIcon from '@mui/icons-material/FileUpload';
|
import FileUploadIcon from '@mui/icons-material/FileUpload';
|
||||||
|
import FileDownloadIcon from '@mui/icons-material/FileDownload';
|
||||||
import AudioFileIcon from '@mui/icons-material/AudioFile';
|
import AudioFileIcon from '@mui/icons-material/AudioFile';
|
||||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||||
import FolderIcon from '@mui/icons-material/Folder';
|
import FolderIcon from '@mui/icons-material/Folder';
|
||||||
@@ -108,9 +109,11 @@ export interface FilePropertyDialogState {
|
|||||||
selectedFileIsDirectory: boolean;
|
selectedFileIsDirectory: boolean;
|
||||||
selectedFileProtected: boolean;
|
selectedFileProtected: boolean;
|
||||||
hasSelection: boolean;
|
hasSelection: boolean;
|
||||||
|
hasFileSelection: boolean;
|
||||||
canDelete: boolean;
|
canDelete: boolean;
|
||||||
fileResult: FileRequestResult;
|
fileResult: FileRequestResult;
|
||||||
navDirectory: string;
|
navDirectory: string;
|
||||||
|
windowWidth: number;
|
||||||
uploadDirectory: string;
|
uploadDirectory: string;
|
||||||
isProtectedDirectory: boolean;
|
isProtectedDirectory: boolean;
|
||||||
columns: number;
|
columns: number;
|
||||||
@@ -199,10 +202,12 @@ export default withStyles(
|
|||||||
fullScreen: this.getFullScreen(),
|
fullScreen: this.getFullScreen(),
|
||||||
selectedFile: selectedFile,
|
selectedFile: selectedFile,
|
||||||
selectedFileProtected: true,
|
selectedFileProtected: true,
|
||||||
|
windowWidth: this.windowSize.width,
|
||||||
selectedFileIsDirectory: false,
|
selectedFileIsDirectory: false,
|
||||||
navDirectory: this.getNavDirectoryFromFile(selectedFile, props.fileProperty),
|
navDirectory: this.getNavDirectoryFromFile(selectedFile, props.fileProperty),
|
||||||
uploadDirectory: "",
|
uploadDirectory: "",
|
||||||
hasSelection: false,
|
hasSelection: false,
|
||||||
|
hasFileSelection: false,
|
||||||
canDelete: false,
|
canDelete: false,
|
||||||
columns: 0,
|
columns: 0,
|
||||||
columnWidth: 1,
|
columnWidth: 1,
|
||||||
@@ -259,12 +264,13 @@ export default withStyles(
|
|||||||
// }
|
// }
|
||||||
filesResult.files.splice(0, 0, { pathname: "", displayName: "<none>", isDirectory: false, isProtected: true });
|
filesResult.files.splice(0, 0, { pathname: "", displayName: "<none>", isDirectory: false, isProtected: true });
|
||||||
|
|
||||||
let fileEntry = this.getFileEntry(filesResult.files, this.state.selectedFile);
|
let fileEntry = this.getFileEntry(filesResult.files, this.state.selectedFile);
|
||||||
this.setState({
|
this.setState({
|
||||||
isProtectedDirectory: filesResult.isProtected,
|
isProtectedDirectory: filesResult.isProtected,
|
||||||
fileResult: filesResult,
|
fileResult: filesResult,
|
||||||
hasSelection: !!fileEntry,
|
hasSelection: !!fileEntry,
|
||||||
selectedFileProtected: fileEntry ? fileEntry.isProtected: true,
|
hasFileSelection: !!fileEntry && (!fileEntry.isDirectory) && (!fileEntry.isProtected),
|
||||||
|
selectedFileProtected: fileEntry ? fileEntry.isProtected : true,
|
||||||
navDirectory: navPath,
|
navDirectory: navPath,
|
||||||
uploadDirectory: filesResult.currentDirectory
|
uploadDirectory: filesResult.currentDirectory
|
||||||
});
|
});
|
||||||
@@ -297,7 +303,8 @@ export default withStyles(
|
|||||||
onWindowSizeChanged(width: number, height: number): void {
|
onWindowSizeChanged(width: number, height: number): void {
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
fullScreen: this.getFullScreen()
|
fullScreen: this.getFullScreen(),
|
||||||
|
windowWidth: width
|
||||||
})
|
})
|
||||||
if (this.lastDivRef !== null) {
|
if (this.lastDivRef !== null) {
|
||||||
this.onMeasureRef(this.lastDivRef);
|
this.onMeasureRef(this.lastDivRef);
|
||||||
@@ -377,7 +384,8 @@ export default withStyles(
|
|||||||
selectedFile: fileEntry.pathname,
|
selectedFile: fileEntry.pathname,
|
||||||
selectedFileIsDirectory: fileEntry.isDirectory,
|
selectedFileIsDirectory: fileEntry.isDirectory,
|
||||||
selectedFileProtected: fileEntry.isProtected,
|
selectedFileProtected: fileEntry.isProtected,
|
||||||
hasSelection: this.isFileInList(this.state.fileResult.files, fileEntry.pathname)
|
hasSelection: this.isFileInList(this.state.fileResult.files, fileEntry.pathname),
|
||||||
|
hasFileSelection: !fileEntry.isDirectory && !fileEntry.isDirectory && !fileEntry.isProtected
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
onDoubleClickValue(selectedFile: string) {
|
onDoubleClickValue(selectedFile: string) {
|
||||||
@@ -392,14 +400,21 @@ export default withStyles(
|
|||||||
this.setState({ menuAnchorEl: null });
|
this.setState({ menuAnchorEl: null });
|
||||||
}
|
}
|
||||||
handleDelete() {
|
handleDelete() {
|
||||||
if (this.state.selectedFileProtected) {
|
if (this.state.selectedFileProtected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({ openConfirmDeleteDialog: true });
|
this.setState({ openConfirmDeleteDialog: true });
|
||||||
}
|
}
|
||||||
|
handleDownloadFile() {
|
||||||
|
if (this.state.selectedFileProtected || !this.state.hasFileSelection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let file = this.state.selectedFile;
|
||||||
|
this.model.downloadAudioFile(file);
|
||||||
|
}
|
||||||
handleConfirmDelete() {
|
handleConfirmDelete() {
|
||||||
if (this.state.selectedFileProtected) {
|
if (this.state.selectedFileProtected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({ openConfirmDeleteDialog: false });
|
this.setState({ openConfirmDeleteDialog: false });
|
||||||
if (this.state.hasSelection) {
|
if (this.state.hasSelection) {
|
||||||
@@ -424,20 +439,21 @@ export default withStyles(
|
|||||||
this.model.deleteUserFile(this.state.selectedFile)
|
this.model.deleteUserFile(this.state.selectedFile)
|
||||||
.then(
|
.then(
|
||||||
() => {
|
() => {
|
||||||
if (newSelection)
|
if (newSelection) {
|
||||||
{
|
this.setState({
|
||||||
this.setState({
|
|
||||||
selectedFile: newSelection.pathname,
|
selectedFile: newSelection.pathname,
|
||||||
selectedFileIsDirectory: newSelection.isDirectory,
|
selectedFileIsDirectory: newSelection.isDirectory,
|
||||||
selectedFileProtected: newSelection.isProtected,
|
selectedFileProtected: newSelection.isProtected,
|
||||||
hasSelection: true
|
hasSelection: true,
|
||||||
|
hasFileSelection: !newSelection.isDirectory && !newSelection.isProtected
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedFile: "",
|
selectedFile: "",
|
||||||
selectedFileIsDirectory: false,
|
selectedFileIsDirectory: false,
|
||||||
selectedFileProtected: true,
|
selectedFileProtected: true,
|
||||||
hasSelection: false
|
hasSelection: false,
|
||||||
|
hasFileSelection: false
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -580,8 +596,7 @@ export default withStyles(
|
|||||||
}
|
}
|
||||||
let protectedDirectory = this.state.fileResult.isProtected;
|
let protectedDirectory = this.state.fileResult.isProtected;
|
||||||
let protectedItem = true;
|
let protectedItem = true;
|
||||||
if (this.state.hasSelection)
|
if (this.state.hasSelection) {
|
||||||
{
|
|
||||||
protectedItem = this.state.selectedFileProtected;
|
protectedItem = this.state.selectedFileProtected;
|
||||||
}
|
}
|
||||||
let canMoveOrRename = this.hasSelectedFileOrFolder() && !protectedItem;
|
let canMoveOrRename = this.hasSelectedFileOrFolder() && !protectedItem;
|
||||||
@@ -719,31 +734,79 @@ export default withStyles(
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
<Divider />
|
<Divider />
|
||||||
<DialogActions style={{ justifyContent: "stretch" }}>
|
<DialogActions style={{ justifyContent: "stretch" }}>
|
||||||
<div style={{ display: "flex", width: "100%", alignItems: "center", flexFlow: "row nowrap" }}>
|
{this.state.windowWidth > 500 ? (
|
||||||
<IconButton style={{ visibility: (this.state.hasSelection ? "visible" : "hidden") }} aria-label="delete" component="label" color="primary"
|
<div style={{ display: "flex", width: "100%", alignItems: "center", flexFlow: "row nowrap" }}>
|
||||||
disabled={!this.state.hasSelection || this.state.selectedFile === "" || protectedItem}
|
<IconButton style={{ visibility: (this.state.hasSelection ? "visible" : "hidden") }} aria-label="delete" component="label" color="primary"
|
||||||
onClick={() => this.handleDelete()} >
|
disabled={!this.state.hasSelection || this.state.selectedFile === "" || protectedItem}
|
||||||
<OldDeleteIcon fontSize='small' />
|
onClick={() => this.handleDelete()} >
|
||||||
</IconButton>
|
<OldDeleteIcon fontSize='small' />
|
||||||
|
</IconButton>
|
||||||
|
|
||||||
<Button style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileUploadIcon />}
|
<Button style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileUploadIcon />}
|
||||||
onClick={() => { this.setState({ openUploadFileDialog: true }) }} disabled={protectedDirectory}
|
onClick={() => { this.setState({ openUploadFileDialog: true }) }} disabled={protectedDirectory}
|
||||||
>
|
>
|
||||||
<div>Upload</div>
|
<div>Upload</div>
|
||||||
</Button>
|
</Button>
|
||||||
<div style={{ flex: "1 1 auto" }}> </div>
|
|
||||||
|
|
||||||
<Button variant="dialogSecondary" onClick={() => { this.props.onCancel(); }} aria-label="cancel">
|
<Button style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileDownloadIcon />}
|
||||||
Cancel
|
onClick={() => { this.handleDownloadFile(); }} disabled={protectedDirectory || !this.state.hasFileSelection}
|
||||||
</Button>
|
>
|
||||||
<Button variant="dialogPrimary" style={{ flex: "0 0 auto" }}
|
<div>Download</div>
|
||||||
onClick={() => { this.openSelectedFile(); }}
|
</Button>
|
||||||
|
|
||||||
disabled={(!this.state.hasSelection) && this.state.selectedFile !== ""} aria-label="select"
|
<div style={{ flex: "1 1 auto" }}> </div>
|
||||||
>
|
|
||||||
{okButtonText}
|
<Button variant="dialogSecondary" onClick={() => { this.props.onCancel(); }} aria-label="cancel">
|
||||||
</Button>
|
Cancel
|
||||||
</div>
|
</Button>
|
||||||
|
<Button variant="dialogPrimary" style={{ flex: "0 0 auto" }}
|
||||||
|
onClick={() => { this.openSelectedFile(); }}
|
||||||
|
|
||||||
|
disabled={(!this.state.hasSelection) && this.state.selectedFile !== ""} aria-label="select"
|
||||||
|
>
|
||||||
|
{okButtonText}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div style={{width: "100%"}}>
|
||||||
|
<div style={{ display: "flex", width: "100%", alignItems: "center", flexFlow: "row nowrap" }}>
|
||||||
|
<IconButton style={{ visibility: (this.state.hasSelection ? "visible" : "hidden") }} aria-label="delete" component="label" color="primary"
|
||||||
|
disabled={!this.state.hasSelection || this.state.selectedFile === "" || protectedItem}
|
||||||
|
onClick={() => this.handleDelete()} >
|
||||||
|
<OldDeleteIcon fontSize='small' />
|
||||||
|
</IconButton>
|
||||||
|
|
||||||
|
<Button style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileUploadIcon />}
|
||||||
|
onClick={() => { this.setState({ openUploadFileDialog: true }) }} disabled={protectedDirectory}
|
||||||
|
>
|
||||||
|
<div>Upload</div>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileDownloadIcon />}
|
||||||
|
onClick={() => { this.handleDownloadFile(); }} disabled={protectedDirectory || !this.state.hasFileSelection}
|
||||||
|
|
||||||
|
>
|
||||||
|
<div>Download</div>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<div style={{ flex: "1 1 auto" }}> </div>
|
||||||
|
</div>
|
||||||
|
<div style={{ display: "flex", width: "100%", alignItems: "center", flexFlow: "row nowrap" }}>
|
||||||
|
<div style={{ flex: "1 1 auto" }}> </div>
|
||||||
|
|
||||||
|
<Button variant="dialogSecondary" onClick={() => { this.props.onCancel(); }} aria-label="cancel">
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button variant="dialogPrimary" style={{ flex: "0 0 auto" }}
|
||||||
|
onClick={() => { this.openSelectedFile(); }}
|
||||||
|
|
||||||
|
disabled={(!this.state.hasSelection) && this.state.selectedFile !== ""} aria-label="select"
|
||||||
|
>
|
||||||
|
{okButtonText}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
<UploadFileDialog
|
<UploadFileDialog
|
||||||
open={this.state.openUploadFileDialog}
|
open={this.state.openUploadFileDialog}
|
||||||
@@ -897,10 +960,10 @@ export default withStyles(
|
|||||||
private onExecuteNewFolder(newName: string) {
|
private onExecuteNewFolder(newName: string) {
|
||||||
this.model.createNewSampleDirectory(pathConcat(this.state.navDirectory, newName), this.props.fileProperty)
|
this.model.createNewSampleDirectory(pathConcat(this.state.navDirectory, newName), this.props.fileProperty)
|
||||||
.then((newPath) => {
|
.then((newPath) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedFile: newPath,
|
selectedFile: newPath,
|
||||||
selectedFileIsDirectory:
|
selectedFileIsDirectory:
|
||||||
true,selectedFileProtected: false
|
true, selectedFileProtected: false
|
||||||
});
|
});
|
||||||
this.requestFiles(this.state.navDirectory);
|
this.requestFiles(this.state.navDirectory);
|
||||||
this.requestScroll = true
|
this.requestScroll = true
|
||||||
|
|||||||
@@ -2388,6 +2388,16 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
this.webSocket?.send("cancelListenForMidiEvent", listenHandle._handle);
|
this.webSocket?.send("cancelListenForMidiEvent", listenHandle._handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downloadAudioFile(filePath: string) {
|
||||||
|
let downloadUrl = this.varServerUrl + "downloadMediaFile?path=" + encodeURIComponent(filePath);
|
||||||
|
|
||||||
|
// download with no flashing temporary tab.
|
||||||
|
let link = window.document.createElement("A") as HTMLLinkElement;
|
||||||
|
link.href = downloadUrl;
|
||||||
|
link.setAttribute("download", "");
|
||||||
|
link.click();
|
||||||
|
}
|
||||||
|
|
||||||
download(targetType: string, instanceId: number | string): void {
|
download(targetType: string, instanceId: number | string): void {
|
||||||
if (instanceId === -1) return;
|
if (instanceId === -1) return;
|
||||||
let url = this.varServerUrl + targetType + "?id=" + instanceId;
|
let url = this.varServerUrl + targetType + "?id=" + instanceId;
|
||||||
|
|||||||
@@ -790,7 +790,7 @@ const PluginControlView =
|
|||||||
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
this.model.showAlert("Unable to complete the operation. Audio is not running." + error);
|
this.model.showAlert("Unable to complete the operation. " + error);
|
||||||
});
|
});
|
||||||
this.setState({ showFileDialog: false });
|
this.setState({ showFileDialog: false });
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user