mirror of
https://github.com/45Drives/cockpit-navigator.git
synced 2025-07-29 08:34:50 +02:00
commit
f4d6f3e29f
10
README.md
10
README.md
@ -19,17 +19,17 @@ With no command line use needed, you can:
|
||||
# Installation
|
||||
## From Github Release
|
||||
### Ubuntu
|
||||
1. `$ wget https://github.com/45Drives/cockpit-navigator/releases/download/v0.2.3/cockpit-navigator_0.2.3-1focal_all.deb`
|
||||
1. `# apt install ./cockpit-navigator_0.2.3-1focal_all.deb`
|
||||
1. `$ wget https://github.com/45Drives/cockpit-navigator/releases/download/v0.3.0/cockpit-navigator_0.3.0-1focal_all.deb`
|
||||
1. `# apt install ./cockpit-navigator_0.3.0-1focal_all.deb`
|
||||
### EL7
|
||||
1. `# yum install https://github.com/45Drives/cockpit-navigator/releases/download/v0.2.3/cockpit-navigator-0.2.3-1.el7.noarch.rpm`
|
||||
1. `# yum install https://github.com/45Drives/cockpit-navigator/releases/download/v0.3.0/cockpit-navigator-0.3.0-1.el7.noarch.rpm`
|
||||
### EL8
|
||||
1. `# dnf install https://github.com/45Drives/cockpit-navigator/releases/download/v0.2.3/cockpit-navigator-0.2.3-1.el8.noarch.rpm`
|
||||
1. `# dnf install https://github.com/45Drives/cockpit-navigator/releases/download/v0.3.0/cockpit-navigator-0.3.0-1.el8.noarch.rpm`
|
||||
## From Source
|
||||
1. Ensure dependencies are installed: `cockpit`, `python3`, `rsync`.
|
||||
1. `$ git clone https://github.com/45Drives/cockpit-navigator.git`
|
||||
1. `$ cd cockpit-navigator`
|
||||
1. `$ git checkout <version>` (v0.2.3 is latest)
|
||||
1. `$ git checkout <version>` (v0.3.0 is latest)
|
||||
1. `# make install`
|
||||
## From 45Drives Repositories
|
||||
### Ubuntu
|
||||
|
8
debian/changelog
vendored
8
debian/changelog
vendored
@ -1,3 +1,11 @@
|
||||
cockpit-navigator (0.3.0-1focal) focal; urgency=medium
|
||||
|
||||
* Add drag and drop uploading of files.
|
||||
* Add event listeners for ctrl+a to select all, ctrl+x to cut,
|
||||
ctrl+c to copy, ctrl+v to paste, and delete to remove a file.
|
||||
|
||||
-- Josh Boudreau <jboudreau@45drives.com> Thu, 03 Jun 2021 16:33:00 -0300
|
||||
|
||||
cockpit-navigator (0.2.3-1focal) focal; urgency=medium
|
||||
|
||||
* Fix closing contextmenu in el7.
|
||||
|
@ -1,5 +1,5 @@
|
||||
Name: cockpit-navigator
|
||||
Version: 0.2.3
|
||||
Version: 0.3.0
|
||||
Release: 1%{?dist}
|
||||
Summary: A File System Browser for Cockpit.
|
||||
License: GPL-3.0+
|
||||
@ -32,6 +32,10 @@ rm -rf %{buildroot}
|
||||
/usr/share/cockpit/navigator/*
|
||||
|
||||
%changelog
|
||||
* Thu Jun 03 2021 Josh Boudreau <jboudreau@45drives.com> 0.3.0-1
|
||||
- Add drag and drop uploading of files.
|
||||
- Add event listeners for ctrl+a to select all, ctrl+x to cut,
|
||||
ctrl+c to copy, ctrl+v to paste, and delete to remove a file.
|
||||
* Wed Jun 02 2021 Josh Boudreau <jboudreau@45drives.com> 0.2.3-1
|
||||
- Fix closing contextmenu in el7.
|
||||
- Hide rename in right click menu with multiple selected entries.
|
||||
|
@ -207,6 +207,7 @@ input[type="text"] {
|
||||
align-items: flex-start;
|
||||
align-content: flex-start;
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
@ -486,3 +487,45 @@ input:checked + .slider:before {
|
||||
.nav-context-menu-item:hover {
|
||||
background-color: var(--border);
|
||||
}
|
||||
|
||||
.drag-enter {
|
||||
border: 1px dashed var(--border);
|
||||
}
|
||||
|
||||
.nav-notifications {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
flex-flow: column-reverse nowrap;
|
||||
align-items: stretch;
|
||||
max-height: 50%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.nav-notification {
|
||||
margin: 5px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: stretch;
|
||||
z-index: 10;
|
||||
flex-grow: 0;
|
||||
padding: 5px;
|
||||
background-color: var(--container);
|
||||
border-radius: var(--nav-border-radius);
|
||||
color: var(--font);
|
||||
}
|
||||
|
||||
.nav-notification-header {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.nav-notification-header > progress {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,10 @@
|
||||
</div>
|
||||
<div class="vertical-spacer"></div>
|
||||
<div class="flex-row inner-container">
|
||||
<div class="contents-view" id="nav-contents-view"></div>
|
||||
<div class="contents-view" id="nav-contents-view">
|
||||
<div class="nav-notifications" id="nav-notifications">
|
||||
</div>
|
||||
</div>
|
||||
<div class="edit-file-contents nav-hidden" id="nav-edit-contents-view">
|
||||
<div class="editor-header" id="nav-edit-contents-header"></div>
|
||||
<div class="vertical-spacer"></div>
|
||||
@ -150,7 +153,7 @@
|
||||
<span id="nav-num-dirs">-</span> Directories, <span id="nav-num-files">-</span> Files (<span id="nav-num-bytes">-</span>)
|
||||
</div>
|
||||
<div class="flex-grow"></div>
|
||||
<a href="https://45drives.com" target="_blank" title="Visit 45Drives.com">
|
||||
<a href="https://www.45drives.com/?utm_source=Houston&utm_medium=UI&utm_campaign=OS-Link" target="_blank" title="Visit 45Drives.com">
|
||||
<img src="branding/logo-light.svg" id="logo-45d"><span class="logo-45">45</span><span class="logo-drives">Drives</span>
|
||||
</a>
|
||||
<div class="flex-grow"></div>
|
||||
|
@ -164,6 +164,8 @@ class NavEntry {
|
||||
this.dom_element.addEventListener("click", this);
|
||||
this.dom_element.addEventListener("contextmenu", this);
|
||||
this.is_hidden_file = this.filename().startsWith('.');
|
||||
if (this.is_hidden_file)
|
||||
icon.style.opacity = 0.5;
|
||||
this.dom_element.title = this.filename();
|
||||
}
|
||||
|
||||
@ -757,22 +759,15 @@ class NavContextMenu {
|
||||
}
|
||||
|
||||
cut() {
|
||||
this.nav_window_ref.clip_board = [...this.nav_window_ref.selected_entries];
|
||||
this.nav_window_ref.copy_or_move = "move";
|
||||
this.nav_window_ref.paste_cwd = this.nav_window_ref.pwd().path_str();
|
||||
this.menu_options["paste"].hidden = false;
|
||||
this.nav_window_ref.cut();
|
||||
}
|
||||
|
||||
copy() {
|
||||
this.nav_window_ref.clip_board = [...this.nav_window_ref.selected_entries];
|
||||
this.nav_window_ref.copy_or_move = "copy";
|
||||
this.nav_window_ref.paste_cwd = this.nav_window_ref.pwd().path_str();
|
||||
this.menu_options["paste"].hidden = false;
|
||||
this.nav_window_ref.copy();
|
||||
}
|
||||
|
||||
paste() {
|
||||
this.nav_window_ref.paste_clipboard();
|
||||
this.hide_paste();
|
||||
this.nav_window_ref.paste();
|
||||
}
|
||||
|
||||
rename() {
|
||||
@ -809,18 +804,18 @@ class NavContextMenu {
|
||||
this.nav_window_ref.set_selected(target, false, false);
|
||||
}
|
||||
if (target === this.nav_window_ref.pwd()) {
|
||||
this.menu_options["copy"].hidden = true;
|
||||
this.menu_options["cut"].hidden = true;
|
||||
this.menu_options["delete"].hidden = true;
|
||||
this.menu_options["copy"].style.display = "none";
|
||||
this.menu_options["cut"].style.display = "none";
|
||||
this.menu_options["delete"].style.display = "none";
|
||||
} else {
|
||||
this.menu_options["copy"].hidden = false;
|
||||
this.menu_options["cut"].hidden = false;
|
||||
this.menu_options["delete"].hidden = false;
|
||||
this.menu_options["copy"].style.display = "block";
|
||||
this.menu_options["cut"].style.display = "block";
|
||||
this.menu_options["delete"].style.display = "block";
|
||||
}
|
||||
if (this.nav_window_ref.selected_entries.size > 1) {
|
||||
this.menu_options["rename"].hidden = true;
|
||||
this.menu_options["rename"].style.display = "none";
|
||||
} else {
|
||||
this.menu_options["rename"].hidden = false;
|
||||
this.menu_options["rename"].style.display = "block";
|
||||
}
|
||||
this.target = target;
|
||||
this.dom_element.style.display = "inline";
|
||||
@ -833,7 +828,189 @@ class NavContextMenu {
|
||||
}
|
||||
|
||||
hide_paste() {
|
||||
this.menu_options["paste"].hidden = true;
|
||||
this.menu_options["paste"].style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
class FileUpload {
|
||||
/**
|
||||
*
|
||||
* @param {File|Blob} file
|
||||
* @param {Number} chunk_size
|
||||
* @param {NavWindow} nav_window_ref
|
||||
*/
|
||||
constructor(file, chunk_size, nav_window_ref) {
|
||||
this.chunk_size = chunk_size;
|
||||
this.filename = file.name;
|
||||
this.nav_window_ref = nav_window_ref;
|
||||
this.path = nav_window_ref.pwd().path_str() + "/" + file.name;
|
||||
this.reader = new FileReader();
|
||||
this.chunks = this.slice_file(file);
|
||||
this.chunk_index = 0;
|
||||
}
|
||||
|
||||
check_if_exists() {
|
||||
return new Promise((resolve, reject) => {
|
||||
var proc = cockpit.spawn(["/usr/share/cockpit/navigator/scripts/fail-if-exists.py", this.path], {superuser: "try"});
|
||||
proc.done((data) => {resolve(false)});
|
||||
proc.fail((e, data) => {resolve(true)});
|
||||
});
|
||||
}
|
||||
|
||||
make_html_element() {
|
||||
var notification = document.createElement("div");
|
||||
notification.classList.add("nav-notification");
|
||||
var header = document.createElement("div");
|
||||
header.classList.add("nav-notification-header");
|
||||
notification.appendChild(header);
|
||||
header.innerText = "Uploading " + this.filename;
|
||||
var progress = document.createElement("progress");
|
||||
progress.max = this.num_chunks;
|
||||
notification.appendChild(progress);
|
||||
this.progress = progress;
|
||||
this.html_elements = [progress, header, notification];
|
||||
document.getElementById("nav-notifications").appendChild(notification);
|
||||
}
|
||||
|
||||
remove_html_element() {
|
||||
for (let elem of this.html_elements) {
|
||||
elem.parentElement.removeChild(elem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {File|Blob} file
|
||||
* @returns {Array}
|
||||
*/
|
||||
slice_file(file) {
|
||||
var offset = 0;
|
||||
var chunks = [];
|
||||
this.num_chunks = Math.ceil(file.size / this.chunk_size);
|
||||
for (let i = 0; i < this.num_chunks; i++) {
|
||||
var next_offset = Math.min(this.chunk_size * (i + 1), file.size);
|
||||
chunks.push(file.slice(offset, next_offset));
|
||||
offset = next_offset;
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
|
||||
async upload() {
|
||||
if (await this.check_if_exists()) {
|
||||
window.alert(this.filename + ": File exists.");
|
||||
return;
|
||||
}
|
||||
this.make_html_element();
|
||||
this.proc = cockpit.spawn(["/usr/share/cockpit/navigator/scripts/write-chunks.py", this.path], {err: "out", superuser: "try"});
|
||||
this.proc.fail((e, data) => {
|
||||
this.reader.onload = () => {}
|
||||
this.done();
|
||||
window.alert(data);
|
||||
})
|
||||
this.proc.done((data) => {
|
||||
|
||||
})
|
||||
this.reader.onload = (function(uploader_ref) {
|
||||
return async function(evt) {
|
||||
uploader_ref.write_to_file(evt, uploader_ref.chunk_index * uploader_ref.chunk_size);
|
||||
uploader_ref.chunk_index++;
|
||||
uploader_ref.progress.value = uploader_ref.chunk_index;
|
||||
if (uploader_ref.chunk_index < uploader_ref.num_chunks)
|
||||
uploader_ref.reader.readAsArrayBuffer(uploader_ref.chunks[uploader_ref.chunk_index]);
|
||||
else {
|
||||
uploader_ref.done();
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
this.reader.readAsArrayBuffer(this.chunks[0]);
|
||||
}
|
||||
|
||||
arrayBufferToBase64(buffer) {
|
||||
let binary = '';
|
||||
let bytes = new Uint8Array(buffer);
|
||||
let len = bytes.byteLength;
|
||||
for (let i = 0; i < len; i++) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
return window.btoa(binary);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Event} evt
|
||||
* @param {Number} offset
|
||||
*/
|
||||
write_to_file(evt, offset) {
|
||||
var chunk_b64 = this.arrayBufferToBase64(evt.target.result);
|
||||
const seek = this.chunk_index * this.chunk_size;
|
||||
var obj = {
|
||||
seek: seek,
|
||||
chunk: chunk_b64
|
||||
};
|
||||
this.proc.input(JSON.stringify(obj) + "\n", true);
|
||||
}
|
||||
|
||||
done() {
|
||||
this.proc.input(); // close stdin
|
||||
this.nav_window_ref.refresh();
|
||||
this.remove_html_element();
|
||||
}
|
||||
}
|
||||
|
||||
class NavDragDrop {
|
||||
/**
|
||||
*
|
||||
* @param {HTMLDivElement} drop_area
|
||||
* @param {NavWindow} nav_window_ref
|
||||
*/
|
||||
constructor(drop_area, nav_window_ref) {
|
||||
drop_area.addEventListener("dragenter", this);
|
||||
drop_area.addEventListener("dragover", this);
|
||||
drop_area.addEventListener("dragleave", this);
|
||||
drop_area.addEventListener("drop", this);
|
||||
this.drop_area = drop_area;
|
||||
this.nav_window_ref = nav_window_ref;
|
||||
}
|
||||
|
||||
handleEvent(e) {
|
||||
e.preventDefault();
|
||||
switch(e.type){
|
||||
case "dragenter":
|
||||
this.drop_area.classList.add("drag-enter");
|
||||
break;
|
||||
case "dragover":
|
||||
break;
|
||||
case "dragleave":
|
||||
this.drop_area.classList.remove("drag-enter");
|
||||
break;
|
||||
case "drop":
|
||||
if (e.dataTransfer.items) {
|
||||
for (let item of e.dataTransfer.items) {
|
||||
if (item.kind === 'file') {
|
||||
var file = item.getAsFile();
|
||||
if (file.type === "") {
|
||||
window.alert(file.name + ": Cannot upload folders.");
|
||||
continue;
|
||||
}
|
||||
var uploader = new FileUpload(file, 4096, this.nav_window_ref);
|
||||
uploader.upload();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let file of ev.dataTransfer.files) {
|
||||
if (file.type === "")
|
||||
continue;
|
||||
var uploader = new FileUpload(file, 4096, this.nav_window_ref);
|
||||
uploader.upload();
|
||||
}
|
||||
}
|
||||
this.drop_area.classList.remove("drag-enter");
|
||||
break;
|
||||
default:
|
||||
this.drop_area.classList.remove("drag-enter");
|
||||
break;
|
||||
}
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
@ -848,11 +1025,14 @@ class NavWindow {
|
||||
this.window = document.getElementById("nav-contents-view");
|
||||
this.window.addEventListener("click", this);
|
||||
this.window.addEventListener("contextmenu", this);
|
||||
window.addEventListener("keydown", this);
|
||||
this.last_selected_index = -1;
|
||||
|
||||
this.context_menu = new NavContextMenu("nav-context-menu", this);
|
||||
|
||||
this.clip_board = [];
|
||||
|
||||
this.uploader = new NavDragDrop(this.window, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -869,6 +1049,22 @@ class NavWindow {
|
||||
this.context_menu.show(e, this.pwd());
|
||||
e.preventDefault();
|
||||
break;
|
||||
case "keydown":
|
||||
if (e.keyCode === 46) {
|
||||
this.delete_selected();
|
||||
} else if (e.keyCode === 65 && e.ctrlKey) {
|
||||
this.select_all();
|
||||
e.preventDefault();
|
||||
} else if (e.keyCode === 67 && e.ctrlKey) {
|
||||
this.copy();
|
||||
} else if (e.keyCode === 86 && e.ctrlKey) {
|
||||
this.paste();
|
||||
} else if (e.keyCode === 88 && e.ctrlKey) {
|
||||
this.cut();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -878,7 +1074,7 @@ class NavWindow {
|
||||
var num_dirs = 0;
|
||||
var num_files = 0;
|
||||
var bytes_sum = 0;
|
||||
var show_hidden = document.getElementById("nav-show-hidden").checked;
|
||||
this.show_hidden = document.getElementById("nav-show-hidden").checked;
|
||||
this.start_load();
|
||||
var files = await this.pwd().get_children(this);
|
||||
while (this.entries.length) {
|
||||
@ -900,7 +1096,7 @@ class NavWindow {
|
||||
num_files++;
|
||||
bytes_sum += file.stat["size"];
|
||||
}
|
||||
if(!file.is_hidden_file || show_hidden)
|
||||
if(!file.is_hidden_file || this.show_hidden)
|
||||
file.show();
|
||||
this.entries.push(file);
|
||||
file.context_menu_ref = this.context_menu;
|
||||
@ -1224,6 +1420,25 @@ class NavWindow {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
cut() {
|
||||
this.clip_board = [...this.selected_entries];
|
||||
this.copy_or_move = "move";
|
||||
this.paste_cwd = this.pwd().path_str();
|
||||
this.context_menu.menu_options["paste"].style.display = "block";
|
||||
}
|
||||
|
||||
copy() {
|
||||
this.clip_board = [...this.selected_entries];
|
||||
this.copy_or_move = "copy";
|
||||
this.paste_cwd = this.pwd().path_str();
|
||||
this.context_menu.menu_options["paste"].style.display = "block";
|
||||
}
|
||||
|
||||
paste() {
|
||||
this.paste_clipboard();
|
||||
this.context_menu.hide_paste();
|
||||
}
|
||||
|
||||
async paste_clipboard() {
|
||||
this.start_load();
|
||||
this.context_menu.hide_paste();
|
||||
@ -1253,9 +1468,10 @@ class NavWindow {
|
||||
proc.fail((e, data) => {
|
||||
window.alert("Paste failed.");
|
||||
});
|
||||
await proc;
|
||||
this.stop_load();
|
||||
this.refresh();
|
||||
proc.always(() => {
|
||||
this.stop_load();
|
||||
this.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1305,7 +1521,7 @@ class NavWindow {
|
||||
}
|
||||
|
||||
start_load() {
|
||||
document.getElementById("nav-loader-container").hidden = false;
|
||||
document.getElementById("nav-loader-container").style.display = "block";
|
||||
var buttons = document.getElementsByTagName("button");
|
||||
for (let button of buttons) {
|
||||
button.disabled = true;
|
||||
@ -1313,7 +1529,7 @@ class NavWindow {
|
||||
}
|
||||
|
||||
stop_load() {
|
||||
document.getElementById("nav-loader-container").hidden = true;
|
||||
document.getElementById("nav-loader-container").style.display = "none";
|
||||
var buttons = document.getElementsByTagName("button");
|
||||
for (let button of buttons) {
|
||||
button.disabled = false;
|
||||
@ -1386,6 +1602,14 @@ class NavWindow {
|
||||
}
|
||||
document.getElementById("pwd").disabled = false;
|
||||
}
|
||||
|
||||
select_all() {
|
||||
for (let entry of this.entries) {
|
||||
if (!entry.is_hidden_file || this.show_hidden) {
|
||||
this.set_selected(entry, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let nav_window = new NavWindow();
|
||||
|
26
navigator/scripts/fail-if-exists.py
Executable file
26
navigator/scripts/fail-if-exists.py
Executable file
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Cockpit Navigator - A File System Browser for Cockpit.
|
||||
Copyright (C) 2021 Josh Boudreau <jboudreau@45drives.com>
|
||||
|
||||
This file is part of Cockpit Navigator.
|
||||
Cockpit Navigator is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
Cockpit Navigator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Cockpit Navigator. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
if os.path.exists(sys.argv[1]):
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit(0)
|
70
navigator/scripts/write-chunks.py
Executable file
70
navigator/scripts/write-chunks.py
Executable file
@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Cockpit Navigator - A File System Browser for Cockpit.
|
||||
Copyright (C) 2021 Josh Boudreau <jboudreau@45drives.com>
|
||||
|
||||
This file is part of Cockpit Navigator.
|
||||
Cockpit Navigator is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
Cockpit Navigator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Cockpit Navigator. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
"""
|
||||
Synopsis: `write-chunks.py <newline delimited JSON objects>`
|
||||
JSON objects are of form:
|
||||
obj = {
|
||||
seek: <byte offset>
|
||||
chunk: <base64 encoded data chunk>
|
||||
}
|
||||
"""
|
||||
|
||||
import base64
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
|
||||
def write_chunk(chunk, file):
|
||||
seek = chunk["seek"]
|
||||
data = base64.b64decode(chunk["chunk"])
|
||||
file.seek(seek)
|
||||
file.write(data)
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
print("Invalid number of arguments.")
|
||||
sys.exit(1)
|
||||
path = sys.argv[1]
|
||||
try:
|
||||
file = open(path, "xb")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
sys.exit(1)
|
||||
while True:
|
||||
try:
|
||||
json_in = input()
|
||||
except EOFError:
|
||||
break
|
||||
json_list = json_in.split("\n")
|
||||
for json_obj in json_list:
|
||||
try:
|
||||
obj_in = json.loads(json_obj)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
log = open("/var/log/navigator.log", "w")
|
||||
log.write(json_in)
|
||||
log.close()
|
||||
sys.exit(1)
|
||||
write_chunk(obj_in, file)
|
||||
file.close()
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
x
Reference in New Issue
Block a user