Show file type icons with the option to disable

This commit is contained in:
joshuaboud 2021-12-16 14:47:07 -04:00
parent 344c2d1468
commit da920db018
No known key found for this signature in database
GPG Key ID: 17EFB59E2A8BF50E
8 changed files with 193 additions and 100 deletions

View File

@ -30,7 +30,7 @@ With no command line use needed, you can:
### EL8 ### EL8
1. `# dnf install https://github.com/45Drives/cockpit-navigator/releases/download/v0.5.8/cockpit-navigator-0.5.8-1.el8.noarch.rpm` 1. `# dnf install https://github.com/45Drives/cockpit-navigator/releases/download/v0.5.8/cockpit-navigator-0.5.8-1.el8.noarch.rpm`
## From Source ## From Source
1. Ensure dependencies are installed: `cockpit`, `python3`, `rsync`, `zip`. 1. Ensure dependencies are installed: `cockpit`, `python3`, `python3-magic`, `zip`.
1. `$ git clone https://github.com/45Drives/cockpit-navigator.git` 1. `$ git clone https://github.com/45Drives/cockpit-navigator.git`
1. `$ cd cockpit-navigator` 1. `$ cd cockpit-navigator`
1. `$ git checkout <version>` (v0.5.8 is latest) 1. `$ git checkout <version>` (v0.5.8 is latest)

View File

@ -25,13 +25,13 @@
"deb": [ "deb": [
"cockpit", "cockpit",
"python3", "python3",
"rsync", "python3-magic",
"zip" "zip"
], ],
"el": [ "el": [
"cockpit", "cockpit",
"python3", "python3",
"rsync", "python3-magic",
"zip" "zip"
] ]
}, },
@ -57,11 +57,12 @@
"version": "0.5.9", "version": "0.5.9",
"buildVersion": "1", "buildVersion": "1",
"ignore": [], "ignore": [],
"date": "2021-12-15T17:43:15.251513", "date": "2021-12-16T14:46:53.434779",
"packager": "Joshua Boudreau <jboudreau@45drives.com>", "packager": "Joshua Boudreau <jboudreau@45drives.com>",
"changes": [ "changes": [
"Disallow downloading files of size 0 to avoid Cockpit bug where fsread never returns.", "Disallow downloading files of size 0 to avoid Cockpit bug where fsread never returns.",
"Overhaul copying/moving to use built in python functions instead of rsync, making moving large files within the same fs instant." "Overhaul copying/moving to use built in python functions instead of rsync, making moving large files within the same fs instant.",
"Add descriptive icons using mimetype with the option to disable."
] ]
} }
} }

View File

@ -62,16 +62,21 @@ export class NavDir extends NavEntry {
/** /**
* *
* @param {NavWindow} nav_window_ref * @param {NavWindow} nav_window_ref
* @param {Boolean} show_mimetype_icons
* @returns {Promise<NavEntry[]>} * @returns {Promise<NavEntry[]>}
*/ */
get_children(nav_window_ref) { get_children(nav_window_ref, show_mimetype_icons = false) {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
var children = []; var children = [];
var argv = ["/usr/share/cockpit/navigator/scripts/ls.py3", this.path_str()];
if (!show_mimetype_icons)
argv.push("--no-mimetype");
var proc = cockpit.spawn( var proc = cockpit.spawn(
["/usr/share/cockpit/navigator/scripts/ls.py3", this.path_str()], argv,
{err:"out", superuser: "try"} {err:"out", superuser: "try"}
); );
proc.fail((e, data) => { proc.fail((e, data) => {
console.log(argv);
reject(data); reject(data);
}); });
var data; var data;
@ -100,7 +105,7 @@ export class NavDir extends NavEntry {
children.push(new NavFileLink(path, stat, nav_window_ref, entry["link-target"])); children.push(new NavFileLink(path, stat, nav_window_ref, entry["link-target"]));
break; break;
default: default:
children.push(new NavFile(path, stat, nav_window_ref)); children.push(new NavFile(path, stat, nav_window_ref, entry["mimetype"]));
break; break;
} }
}); });

View File

@ -29,10 +29,61 @@ export class NavFile extends NavEntry {
* @param {object} stat * @param {object} stat
* @param {NavWindow} nav_window_ref * @param {NavWindow} nav_window_ref
*/ */
constructor(path, stat, nav_window_ref) { constructor(path, stat, nav_window_ref, mimetype) {
super(path, stat, nav_window_ref); super(path, stat, nav_window_ref);
this.nav_type = "file"; this.nav_type = "file";
if (mimetype) {
if (/^image\//.test(mimetype)) {
this.dom_element.nav_item_icon.classList.add("fas", "fa-file-image");
} else if (/^video\//.test(mimetype)) {
this.dom_element.nav_item_icon.classList.add("fas", "fa-file-video");
} else if (/^audio\//.test(mimetype)) {
this.dom_element.nav_item_icon.classList.add("fas", "fa-file-audio");
} else {
switch (mimetype) {
case "text/plain":
case "application/rtf":
this.dom_element.nav_item_icon.classList.add("fas", "fa-file-alt");
break;
case "text/csv":
this.dom_element.nav_item_icon.classList.add("fas", "fa-file-csv");
break;
case "application/msword":
case "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
case "application/vnd.oasis.opendocument.text":
this.dom_element.nav_item_icon.classList.add("fas", "fa-file-word");
break;
case "application/vnd.ms-powerpoint":
case "application/vnd.openxmlformats-officedocument.presentationml.presentation":
case "application/vnd.oasis.opendocument.presentation":
this.dom_element.nav_item_icon.classList.add("fas", "fa-file-powerpoint");
break;
case "application/vnd.ms-excel":
case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
case "application/vnd.oasis.opendocument.spreadsheet":
this.dom_element.nav_item_icon.classList.add("fas", "fa-file-excel");
break;
case "application/pdf":
this.dom_element.nav_item_icon.classList.add("fas", "fa-file-pdf");
break;
case "application/x-freearc":
case "application/x-bzip":
case "application/x-bzip2":
case "application/gzip":
case "application/vnd.rar":
case "application/x-tar":
case "application/zip":
case "application/x-7z-compressed":
this.dom_element.nav_item_icon.classList.add("fas", "fa-file-archive");
break;
default:
this.dom_element.nav_item_icon.classList.add("fas", "fa-file"); this.dom_element.nav_item_icon.classList.add("fas", "fa-file");
break;
}
}
} else {
this.dom_element.nav_item_icon.classList.add("fas", "fa-file");
}
this.double_click = false; this.double_click = false;
} }

View File

@ -28,6 +28,9 @@ import { format_bytes, format_permissions } from "../functions.js";
export class NavWindow { export class NavWindow {
constructor() { constructor() {
this.item_display = "grid"; this.item_display = "grid";
var show_mimetype_icons_str = localStorage.getItem('show-mimetype-icons') ?? 'true';
this.show_mimetype_icons = show_mimetype_icons_str === 'true';
document.getElementById("nav-show-mimetype-icons").checked = this.show_mimetype_icons;
this.path_stack = (localStorage.getItem('navigator-path') ?? '/').split('/'); this.path_stack = (localStorage.getItem('navigator-path') ?? '/').split('/');
this.path_stack = this.path_stack.map((_, index) => new NavDir([...this.path_stack.slice(0, index + 1)].filter(part => part != ''), this)); this.path_stack = this.path_stack.map((_, index) => new NavDir([...this.path_stack.slice(0, index + 1)].filter(part => part != ''), this));
@ -126,7 +129,7 @@ export class NavWindow {
this.show_hidden = document.getElementById("nav-show-hidden").checked; this.show_hidden = document.getElementById("nav-show-hidden").checked;
this.start_load(); this.start_load();
try { try {
var files = await this.pwd().get_children(this); var files = await this.pwd().get_children(this, this.show_mimetype_icons);
} catch(e) { } catch(e) {
await this.modal_prompt.alert(e); await this.modal_prompt.alert(e);
this.up(); this.up();
@ -914,6 +917,13 @@ export class NavWindow {
localStorage.setItem("item-display", this.item_display); localStorage.setItem("item-display", this.item_display);
} }
async set_show_mimetype_icons(e) {
console.log(e);
this.show_mimetype_icons = e.target.checked;
localStorage.setItem("show-mimetype-icons", e.target.checked.toString());
await this.refresh();
}
search_filter(event) { search_filter(event) {
var search_name = event.target.value; var search_name = event.target.value;
let search_func; let search_func;

View File

@ -181,6 +181,17 @@
<i class="fas fa-list" id="nav-item-display-icon"></i> <i class="fas fa-list" id="nav-item-display-icon"></i>
</div> </div>
<div class="horizontal-spacer"></div> <div class="horizontal-spacer"></div>
<div class="nav-toggle">
<div class="nav-btn-group">
<i class="fas fa-icons" id="nav-show-mimetype-icons-icon"></i>
<div class="horizontal-spacer-sm"></div>
<label class="switch" title="Show File Type Icons (Disable for faster load)">
<input type="checkbox" id="nav-show-mimetype-icons">
<span class="slider round"></span>
</label>
</div>
</div>
<div class="horizontal-spacer"></div>
<div class="nav-toggle"> <div class="nav-toggle">
<div class="nav-btn-group"> <div class="nav-btn-group">
<i class="fas fa-low-vision" id="nav-show-hidden-icon"></i> <i class="fas fa-low-vision" id="nav-show-hidden-icon"></i>

View File

@ -117,6 +117,7 @@ function set_up_buttons() {
document.getElementById("pwd").addEventListener("focus", nav_window.nav_bar_update_choices.bind(nav_window), false); document.getElementById("pwd").addEventListener("focus", nav_window.nav_bar_update_choices.bind(nav_window), false);
document.getElementById("pwd").addEventListener("keydown", nav_window.nav_bar_event_handler.bind(nav_window)); document.getElementById("pwd").addEventListener("keydown", nav_window.nav_bar_event_handler.bind(nav_window));
document.getElementById("toggle-theme").addEventListener("change", switch_theme, false); document.getElementById("toggle-theme").addEventListener("change", switch_theme, false);
document.getElementById("nav-show-mimetype-icons").addEventListener("change", nav_window.set_show_mimetype_icons.bind(nav_window));
document.getElementById("nav-show-hidden").addEventListener("change", nav_window.toggle_show_hidden.bind(nav_window)); document.getElementById("nav-show-hidden").addEventListener("change", nav_window.toggle_show_hidden.bind(nav_window));
document.getElementById("nav-item-display-btn").addEventListener("click", nav_window.switch_item_display.bind(nav_window)); document.getElementById("nav-item-display-btn").addEventListener("click", nav_window.switch_item_display.bind(nav_window));
for (let option of ["name", "owner", "group", "size", "modified", "created"]) { for (let option of ["name", "owner", "group", "size", "modified", "created"]) {

View File

@ -21,10 +21,12 @@ import os
from stat import S_ISDIR, S_ISLNK, filemode from stat import S_ISDIR, S_ISLNK, filemode
import json import json
import sys import sys
import magic
from optparse import OptionParser
from pwd import getpwuid from pwd import getpwuid
from grp import getgrgid from grp import getgrgid
def get_stat(full_path, filename = '/'): def get_stat(full_path, filename = '/', no_mimetype=False):
try: try:
stats = os.lstat(full_path) stats = os.lstat(full_path)
except OSError: except OSError:
@ -64,6 +66,13 @@ def get_stat(full_path, filename = '/'):
group = getgrgid(stats.st_gid).gr_name group = getgrgid(stats.st_gid).gr_name
except: except:
pass pass
mimetype = None
if not no_mimetype:
try:
if not isdir:
mimetype = magic.detect_from_filename(full_path)[0]
except:
pass
response = { response = {
"inaccessible": False, "inaccessible": False,
"filename": filename, "filename": filename,
@ -80,25 +89,30 @@ def get_stat(full_path, filename = '/'):
"atime": stats.st_atime, "atime": stats.st_atime,
"mtime": stats.st_mtime, "mtime": stats.st_mtime,
"ctime": stats.st_ctime "ctime": stats.st_ctime
} },
"mimetype": mimetype
} }
return response return response
def main(): def main():
if(len(sys.argv) < 2): parser = OptionParser()
parser.add_option("-m", "--no-mimetype", dest="no_mimetype", default=False, action="store_true")
(options, args) = parser.parse_args()
if(len(args) != 1):
print("Not enough args: ", args)
sys.exit(1) sys.exit(1)
try: try:
nodes = os.listdir(sys.argv[1]) nodes = os.listdir(args[0])
except Exception as e: except Exception as e:
print(e) print(e)
sys.exit(1) sys.exit(1)
response = { response = {
".": get_stat(sys.argv[1]), ".": get_stat(args[0], no_mimetype=options.no_mimetype),
"children": [] "children": []
} }
for node in nodes: for node in nodes:
full_path = sys.argv[1] + "/" + node full_path = args[0] + "/" + node
response["children"].append(get_stat(full_path, node)) response["children"].append(get_stat(full_path, node, no_mimetype=options.no_mimetype))
print(json.dumps(response)) print(json.dumps(response))