Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a9ef41962 | |||
| 69fbb15bca | |||
| f9a82c121e | |||
| 245b76e04e | |||
| fa4d8debd0 | |||
| 4e9a902a1c | |||
| bd15a45090 | |||
| 0a20b70478 |
20
DEVELOP.md
20
DEVELOP.md
@@ -52,3 +52,23 @@ would be stored in the following key/value pairs in the btree db:
|
|||||||
* 00aa11bb22/playlist/00000: a.mp3
|
* 00aa11bb22/playlist/00000: a.mp3
|
||||||
* 00aa11bb22/playlist/00001: b.mp3
|
* 00aa11bb22/playlist/00001: b.mp3
|
||||||
* 00aa11bb22/playlistpos: 00000
|
* 00aa11bb22/playlistpos: 00000
|
||||||
|
|
||||||
|
## Notes for UI development with chromium
|
||||||
|
|
||||||
|
Features for the web interface are best prototyped in a browser directly. By using the built-in developmer tools and
|
||||||
|
and their "override" feature, the web contents are replaced by a locally stored copy, which can be used to directly
|
||||||
|
test the modifications without going all the way through the build and flash process.
|
||||||
|
|
||||||
|
However, modern browsers may restrict or even completely forbid the execution of dynamic content like JavaScript, if
|
||||||
|
the content is stored on the local machine and/or the content is accessed using http. In such a case, chromium issues
|
||||||
|
an error message similar to the following one:
|
||||||
|
|
||||||
|
> Access to fetch at 'http://192.168.4.1/api/v1/audiofiles' from origin 'http://192.168.4.1' has been blocked by CORS
|
||||||
|
> policy: The request client is not a secure context and the resource is in more-private address space `local`.
|
||||||
|
|
||||||
|
To mitigate this, chromium offers two flags that need modification:
|
||||||
|
- 'chrome://flags/#local-network-access-check' must be `Disabled`
|
||||||
|
- 'chrome://flags/#unsafely-treat-insecure-origin-as-secure' must be `Enabled`
|
||||||
|
|
||||||
|
Note that these settings leave the browser susceptible to security issues and should be returned to
|
||||||
|
their default values as soon as possible.
|
||||||
|
|||||||
@@ -147,6 +147,7 @@
|
|||||||
<button onclick="showScreen('menu')">🏠 Main Menu</button>
|
<button onclick="showScreen('menu')">🏠 Main Menu</button>
|
||||||
<button onclick="showScreen('config')">⚙️ Config Editor</button>
|
<button onclick="showScreen('config')">⚙️ Config Editor</button>
|
||||||
<button onclick="showScreen('playlist')">🖹 Playlist Editor</button>
|
<button onclick="showScreen('playlist')">🖹 Playlist Editor</button>
|
||||||
|
<button onclick="showScreen('filebrowser', 'filesystem')">📂 Filesystem</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- MAIN MENU -->
|
<!-- MAIN MENU -->
|
||||||
@@ -242,10 +243,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- PLAYLIST EDITOR SCREEN 3: file browser -->
|
<!-- PLAYLIST EDITOR SCREEN 3: file browser -->
|
||||||
<div id="screen-playlist_filebrowser" class="screen">
|
<div id="screen-filebrowser" class="screen">
|
||||||
<h2>Playlist Editor</h2>
|
<h2 id="playlist-filebrowser-title">Playlist Editor</h2>
|
||||||
<div id="playlist-filebrowser-container">
|
<div id="playlist-filebrowser-container">
|
||||||
<div class="scroll-container">
|
<div class="scroll-container" style="margin-bottom: 10px">
|
||||||
<div class="tree" id="playlist-filebrowser-tree">
|
<div class="tree" id="playlist-filebrowser-tree">
|
||||||
<ul><li>Loading...</li></ul>
|
<ul><li>Loading...</li></ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -654,7 +655,7 @@
|
|||||||
document.getElementById('playlist-edit-removetrack').addEventListener("click", (e) => deleteSelectedTracks());
|
document.getElementById('playlist-edit-removetrack').addEventListener("click", (e) => deleteSelectedTracks());
|
||||||
document.getElementById('playlist-edit-back').addEventListener("click", (e) => showScreen('playlist'));
|
document.getElementById('playlist-edit-back').addEventListener("click", (e) => showScreen('playlist'));
|
||||||
document.getElementById('playlist-edit-addtrack').addEventListener("click", (e) => {
|
document.getElementById('playlist-edit-addtrack').addEventListener("click", (e) => {
|
||||||
showScreen("playlist_filebrowser");
|
showScreen("filebrowser", "playlist");
|
||||||
});
|
});
|
||||||
document.getElementById('playlist-edit-save').addEventListener("click", (e) => save());
|
document.getElementById('playlist-edit-save').addEventListener("click", (e) => save());
|
||||||
}
|
}
|
||||||
@@ -786,7 +787,9 @@
|
|||||||
/* ----------------------------------------
|
/* ----------------------------------------
|
||||||
PLAYLIST EDITOR LOGIC - ADD FILES SCREEN
|
PLAYLIST EDITOR LOGIC - ADD FILES SCREEN
|
||||||
------------------------------------------- */
|
------------------------------------------- */
|
||||||
Screens.playlist_filebrowser = (() => {
|
Screens.filebrowser = (() => {
|
||||||
|
let isFilesystemMode = false;
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
document.getElementById('playlist-filebrowser-cancel').addEventListener("click", (e) => {
|
document.getElementById('playlist-filebrowser-cancel').addEventListener("click", (e) => {
|
||||||
showScreen("playlist_edit", {});
|
showScreen("playlist_edit", {});
|
||||||
@@ -805,13 +808,32 @@
|
|||||||
});
|
});
|
||||||
tree.init();
|
tree.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onShow(intent) {
|
async function onShow(intent) {
|
||||||
|
console.log(intent)
|
||||||
document.getElementById('playlist-filebrowser-addtrack').disabled = true;
|
document.getElementById('playlist-filebrowser-addtrack').disabled = true;
|
||||||
|
|
||||||
|
const title = document.getElementById('playlist-filebrowser-title');
|
||||||
|
const cancelButton = document.getElementById('playlist-filebrowser-cancel');
|
||||||
|
const addTracksButton = document.getElementById('playlist-filebrowser-addtrack');
|
||||||
|
|
||||||
if (intent !== 'refresh') {
|
if (intent !== 'refresh') {
|
||||||
|
isFilesystemMode = (intent === 'filesystem');
|
||||||
|
|
||||||
document.getElementById('playlist-filebrowser-upload-progress').value = 0;
|
document.getElementById('playlist-filebrowser-upload-progress').value = 0;
|
||||||
document.getElementById("playlist-filebrowser-upload-files").value = "";
|
document.getElementById("playlist-filebrowser-upload-files").value = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isFilesystemMode) {
|
||||||
|
title.innerText = "Filesystem";
|
||||||
|
cancelButton.style.display = 'none';
|
||||||
|
addTracksButton.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
title.innerText = "Playlist Editor";
|
||||||
|
cancelButton.style.display = ''
|
||||||
|
addTracksButton.style.display = ''
|
||||||
|
}
|
||||||
|
|
||||||
tree = document.getElementById("playlist-filebrowser-tree");
|
tree = document.getElementById("playlist-filebrowser-tree");
|
||||||
tree.innerHTML = "Loading...";
|
tree.innerHTML = "Loading...";
|
||||||
fetch('/api/v1/audiofiles')
|
fetch('/api/v1/audiofiles')
|
||||||
@@ -938,7 +960,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
xhr.open("POST", `/api/v1/audiofiles?type=file&location=${location}`);
|
xhr.open("POST", `/api/v1/audiofiles?type=file&location=${encodeURIComponent(location)}`);
|
||||||
xhr.overrideMimeType("audio/mpeg");
|
xhr.overrideMimeType("audio/mpeg");
|
||||||
xhr.send(files[0]);
|
xhr.send(files[0]);
|
||||||
}
|
}
|
||||||
@@ -956,7 +978,7 @@
|
|||||||
const location = selectedNodes.length === 1
|
const location = selectedNodes.length === 1
|
||||||
? selectedNodes[0].getAttribute('data-path') + '/' + name.value
|
? selectedNodes[0].getAttribute('data-path') + '/' + name.value
|
||||||
: '/' + name.value;
|
: '/' + name.value;
|
||||||
const saveRes = await fetch(`/api/v1/audiofiles?type=directory&location=${location}`,
|
const saveRes = await fetch(`/api/v1/audiofiles?type=directory&location=${encodeURIComponent(location)}`,
|
||||||
{method: 'POST'});
|
{method: 'POST'});
|
||||||
// Reload file list from device
|
// Reload file list from device
|
||||||
onShow('refresh');
|
onShow('refresh');
|
||||||
@@ -973,7 +995,7 @@
|
|||||||
items.sort();
|
items.sort();
|
||||||
items.reverse();
|
items.reverse();
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
const saveRes = await fetch(`/api/v1/audiofiles?location=${item}`,
|
const saveRes = await fetch(`/api/v1/audiofiles?location=${encodeURIComponent(item)}`,
|
||||||
{method: 'DELETE'});
|
{method: 'DELETE'});
|
||||||
if (!saveRes.ok) {
|
if (!saveRes.ok) {
|
||||||
alert(`Failed to delete item ${item}: ${await saveRes.text()}`);
|
alert(`Failed to delete item ${item}: ${await saveRes.text()}`);
|
||||||
|
|||||||
@@ -235,16 +235,19 @@ async def audiofile_upload(request):
|
|||||||
if type_ == 'directory':
|
if type_ == 'directory':
|
||||||
if length != 0:
|
if length != 0:
|
||||||
return 'directory request may not have content', 400
|
return 'directory request may not have content', 400
|
||||||
os.mkdir(path)
|
|
||||||
return '', 204
|
|
||||||
with open(path, 'wb') as newfile:
|
|
||||||
try:
|
try:
|
||||||
|
os.mkdir(path)
|
||||||
|
except OSError as ex:
|
||||||
|
return f'error creating directory: {ex}', 500
|
||||||
|
return '', 204
|
||||||
|
try:
|
||||||
|
with open(path, 'wb') as newfile:
|
||||||
if length > Request.max_body_length:
|
if length > Request.max_body_length:
|
||||||
bytes_copied = await stream_to_file(request.stream, newfile, length)
|
bytes_copied = await stream_to_file(request.stream, newfile, length)
|
||||||
else:
|
else:
|
||||||
bytes_copied = newfile.write(request.body)
|
bytes_copied = newfile.write(request.body)
|
||||||
except OSError as ex:
|
except OSError as ex:
|
||||||
return f'error writing data to file: {ex}', 500
|
return f'error writing data to file: {ex}', 500
|
||||||
if bytes_copied == length:
|
if bytes_copied == length:
|
||||||
return '', 204
|
return '', 204
|
||||||
else:
|
else:
|
||||||
|
|||||||
Reference in New Issue
Block a user