storage-viewer 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- storage_viewer-1.0.0/LICENSE +21 -0
- storage_viewer-1.0.0/PKG-INFO +138 -0
- storage_viewer-1.0.0/README.md +103 -0
- storage_viewer-1.0.0/SourceCode/__init__.py +13 -0
- storage_viewer-1.0.0/SourceCode/main.py +386 -0
- storage_viewer-1.0.0/pyproject.toml +25 -0
- storage_viewer-1.0.0/setup.cfg +4 -0
- storage_viewer-1.0.0/storage_viewer.egg-info/PKG-INFO +138 -0
- storage_viewer-1.0.0/storage_viewer.egg-info/SOURCES.txt +10 -0
- storage_viewer-1.0.0/storage_viewer.egg-info/dependency_links.txt +1 -0
- storage_viewer-1.0.0/storage_viewer.egg-info/entry_points.txt +2 -0
- storage_viewer-1.0.0/storage_viewer.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Lingaraj Sa
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: storage-viewer
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Advanced directory tree visualizer with filtering options
|
|
5
|
+
Author: Lingaraj Sa
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2024 Lingaraj Sa
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
29
|
+
Classifier: Programming Language :: Python :: 3
|
|
30
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
31
|
+
Requires-Python: >=3.7
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
License-File: LICENSE
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# Storage Viewer CLI
|
|
37
|
+
|
|
38
|
+

|
|
39
|
+

|
|
40
|
+

|
|
41
|
+
|
|
42
|
+
A powerful, feature-rich directory tree visualizer with advanced filtering options for the command line.
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- **Hierarchical tree display** — Clean, readable directory structures
|
|
47
|
+
- **Advanced filtering** — Filter by extension, folder, pattern, and size
|
|
48
|
+
- **Size analysis** — Show file sizes in human-readable formats
|
|
49
|
+
- **File type icons** — Visual indicators for different file types
|
|
50
|
+
- **Depth control** — Limit directory traversal depth
|
|
51
|
+
- **Export capability** — Save tree structures to files
|
|
52
|
+
- **Statistics** — Summaries of directories and files
|
|
53
|
+
- **Fast and lightweight** — No external dependencies
|
|
54
|
+
|
|
55
|
+
## Basic usage
|
|
56
|
+
|
|
57
|
+
View a directory tree:
|
|
58
|
+
|
|
59
|
+
```console
|
|
60
|
+
stree /path/to/directory
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Show file type icons:
|
|
64
|
+
|
|
65
|
+
```console
|
|
66
|
+
stree . --icons
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Show file sizes:
|
|
70
|
+
|
|
71
|
+
```console
|
|
72
|
+
stree . --show-size
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Limit traversal depth:
|
|
76
|
+
|
|
77
|
+
```console
|
|
78
|
+
stree . --max-depth 3
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Examples
|
|
82
|
+
|
|
83
|
+
Exclude development folders:
|
|
84
|
+
|
|
85
|
+
```console
|
|
86
|
+
stree . --exclude-folders __pycache__,node_modules,.git,venv --max-depth 4
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Find large files:
|
|
90
|
+
|
|
91
|
+
```console
|
|
92
|
+
stree /home --min-size 100M --show-size --sort-by size --reverse-sort
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Export the tree to a file:
|
|
96
|
+
|
|
97
|
+
```console
|
|
98
|
+
stree /path --export directory_structure.txt
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Show only code files:
|
|
102
|
+
|
|
103
|
+
```console
|
|
104
|
+
stree . --include-patterns "*.py,*.js,*.java,*.cpp"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Exclude temporary files:
|
|
108
|
+
|
|
109
|
+
```console
|
|
110
|
+
stree . --exclude-extensions tmp,bak,log --exclude-files "*.tmp,*.bak"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Options
|
|
114
|
+
|
|
115
|
+
```text
|
|
116
|
+
Usage: stree [OPTIONS] PATH
|
|
117
|
+
|
|
118
|
+
Display directory tree structure with advanced filtering.
|
|
119
|
+
|
|
120
|
+
Options:
|
|
121
|
+
--show-hidden Show hidden files and folders
|
|
122
|
+
--no-file Exclude all files
|
|
123
|
+
--no-folder Exclude all folders
|
|
124
|
+
--exclude-extensions Comma-separated file extensions to exclude
|
|
125
|
+
--exclude-folders Comma-separated folder names to exclude
|
|
126
|
+
--exclude-files Comma-separated file patterns to exclude
|
|
127
|
+
--include-patterns Comma-separated patterns to include
|
|
128
|
+
--max-depth Maximum depth to traverse
|
|
129
|
+
--min-size Minimum file size (e.g., 1M, 100K, 1G)
|
|
130
|
+
--max-size Maximum file size (e.g., 1M, 100K, 1G)
|
|
131
|
+
--show-size Show file sizes
|
|
132
|
+
--icons Show file type icons
|
|
133
|
+
--sort-by Sort method: name, size, or type
|
|
134
|
+
--reverse-sort Reverse sort order
|
|
135
|
+
--export Export tree to specified file
|
|
136
|
+
--version Show version
|
|
137
|
+
--help Show this help message
|
|
138
|
+
```
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Storage Viewer CLI
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
A powerful, feature-rich directory tree visualizer with advanced filtering options for the command line.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Hierarchical tree display** — Clean, readable directory structures
|
|
12
|
+
- **Advanced filtering** — Filter by extension, folder, pattern, and size
|
|
13
|
+
- **Size analysis** — Show file sizes in human-readable formats
|
|
14
|
+
- **File type icons** — Visual indicators for different file types
|
|
15
|
+
- **Depth control** — Limit directory traversal depth
|
|
16
|
+
- **Export capability** — Save tree structures to files
|
|
17
|
+
- **Statistics** — Summaries of directories and files
|
|
18
|
+
- **Fast and lightweight** — No external dependencies
|
|
19
|
+
|
|
20
|
+
## Basic usage
|
|
21
|
+
|
|
22
|
+
View a directory tree:
|
|
23
|
+
|
|
24
|
+
```console
|
|
25
|
+
stree /path/to/directory
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Show file type icons:
|
|
29
|
+
|
|
30
|
+
```console
|
|
31
|
+
stree . --icons
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Show file sizes:
|
|
35
|
+
|
|
36
|
+
```console
|
|
37
|
+
stree . --show-size
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Limit traversal depth:
|
|
41
|
+
|
|
42
|
+
```console
|
|
43
|
+
stree . --max-depth 3
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Examples
|
|
47
|
+
|
|
48
|
+
Exclude development folders:
|
|
49
|
+
|
|
50
|
+
```console
|
|
51
|
+
stree . --exclude-folders __pycache__,node_modules,.git,venv --max-depth 4
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Find large files:
|
|
55
|
+
|
|
56
|
+
```console
|
|
57
|
+
stree /home --min-size 100M --show-size --sort-by size --reverse-sort
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Export the tree to a file:
|
|
61
|
+
|
|
62
|
+
```console
|
|
63
|
+
stree /path --export directory_structure.txt
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Show only code files:
|
|
67
|
+
|
|
68
|
+
```console
|
|
69
|
+
stree . --include-patterns "*.py,*.js,*.java,*.cpp"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Exclude temporary files:
|
|
73
|
+
|
|
74
|
+
```console
|
|
75
|
+
stree . --exclude-extensions tmp,bak,log --exclude-files "*.tmp,*.bak"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Options
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
Usage: stree [OPTIONS] PATH
|
|
82
|
+
|
|
83
|
+
Display directory tree structure with advanced filtering.
|
|
84
|
+
|
|
85
|
+
Options:
|
|
86
|
+
--show-hidden Show hidden files and folders
|
|
87
|
+
--no-file Exclude all files
|
|
88
|
+
--no-folder Exclude all folders
|
|
89
|
+
--exclude-extensions Comma-separated file extensions to exclude
|
|
90
|
+
--exclude-folders Comma-separated folder names to exclude
|
|
91
|
+
--exclude-files Comma-separated file patterns to exclude
|
|
92
|
+
--include-patterns Comma-separated patterns to include
|
|
93
|
+
--max-depth Maximum depth to traverse
|
|
94
|
+
--min-size Minimum file size (e.g., 1M, 100K, 1G)
|
|
95
|
+
--max-size Maximum file size (e.g., 1M, 100K, 1G)
|
|
96
|
+
--show-size Show file sizes
|
|
97
|
+
--icons Show file type icons
|
|
98
|
+
--sort-by Sort method: name, size, or type
|
|
99
|
+
--reverse-sort Reverse sort order
|
|
100
|
+
--export Export tree to specified file
|
|
101
|
+
--version Show version
|
|
102
|
+
--help Show this help message
|
|
103
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Storage Viewer - Advanced directory tree visualizer with filtering
|
|
3
|
+
A powerful CLI tool for visualizing directory structures with advanced filtering option>
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
__version__ = "1.0.0"
|
|
7
|
+
__author__ = "Lingaraj Sa"
|
|
8
|
+
__email__ = "infofusiontechlab@gmail.com"
|
|
9
|
+
__description__ = "Advanced directory tree visualizer with filtering options"
|
|
10
|
+
__url__ = "https://github.com/fusion-tech-lab-mastet/storage-viewer"
|
|
11
|
+
__license__ = "MIT"
|
|
12
|
+
|
|
13
|
+
from .main import DirectoryTree, parse_size, main
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
# src/storage_viewer/storage_viewer.py
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import argparse
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import fnmatch
|
|
7
|
+
|
|
8
|
+
__version__ = "1.0.0"
|
|
9
|
+
|
|
10
|
+
class DirectoryTree:
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self.stats = {
|
|
13
|
+
'directories': 0,
|
|
14
|
+
'files': 0,
|
|
15
|
+
'total_size': 0,
|
|
16
|
+
'skipped_items': 0
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
def should_include_item(self, item_path, item_name, options):
|
|
20
|
+
"""Determine if an item should be included based on filters"""
|
|
21
|
+
# Skip hidden files unless explicitly shown
|
|
22
|
+
if not getattr(options, 'show_hidden', False) and item_name.startswith('.'):
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
# Check if it's a directory
|
|
26
|
+
is_directory = os.path.isdir(item_path)
|
|
27
|
+
|
|
28
|
+
# Skip files if --no-file is specified
|
|
29
|
+
if not is_directory and getattr(options, 'no_file', False):
|
|
30
|
+
self.stats['skipped_items'] += 1
|
|
31
|
+
return False
|
|
32
|
+
|
|
33
|
+
# Skip folders if --no-folder is specified
|
|
34
|
+
if is_directory and getattr(options, 'no_folder', False):
|
|
35
|
+
self.stats['skipped_items'] += 1
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
# Check folder name exclusion
|
|
39
|
+
if is_directory and getattr(options, 'exclude_folders', []):
|
|
40
|
+
for folder_pattern in options.exclude_folders:
|
|
41
|
+
if fnmatch.fnmatch(item_name.lower(), folder_pattern.lower()):
|
|
42
|
+
self.stats['skipped_items'] += 1
|
|
43
|
+
return False
|
|
44
|
+
|
|
45
|
+
# Check file extension exclusion
|
|
46
|
+
if not is_directory and getattr(options, 'exclude_extensions', []):
|
|
47
|
+
file_ext = Path(item_name).suffix.lower()
|
|
48
|
+
if file_ext and file_ext[1:] in [ext.lower() for ext in options.exclude_extensions]:
|
|
49
|
+
self.stats['skipped_items'] += 1
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
# Check file pattern exclusion
|
|
53
|
+
if not is_directory and getattr(options, 'exclude_files', []):
|
|
54
|
+
for file_pattern in options.exclude_files:
|
|
55
|
+
if fnmatch.fnmatch(item_name.lower(), file_pattern.lower()):
|
|
56
|
+
self.stats['skipped_items'] += 1
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
# Check include patterns
|
|
60
|
+
if getattr(options, 'include_patterns', []):
|
|
61
|
+
matched = False
|
|
62
|
+
for pattern in options.include_patterns:
|
|
63
|
+
if fnmatch.fnmatch(item_name.lower(), pattern.lower()):
|
|
64
|
+
matched = True
|
|
65
|
+
break
|
|
66
|
+
if not matched:
|
|
67
|
+
self.stats['skipped_items'] += 1
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
# Check max depth
|
|
71
|
+
if getattr(options, 'max_depth', None) and self.current_depth >= options.max_depth:
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
# Check size filters
|
|
75
|
+
if not is_directory and getattr(options, 'min_size', None):
|
|
76
|
+
try:
|
|
77
|
+
file_size = os.path.getsize(item_path)
|
|
78
|
+
if file_size < options.min_size:
|
|
79
|
+
self.stats['skipped_items'] += 1
|
|
80
|
+
return False
|
|
81
|
+
except (OSError, PermissionError):
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
if not is_directory and getattr(options, 'max_size', None):
|
|
85
|
+
try:
|
|
86
|
+
file_size = os.path.getsize(item_path)
|
|
87
|
+
if file_size > options.max_size:
|
|
88
|
+
self.stats['skipped_items'] += 1
|
|
89
|
+
return False
|
|
90
|
+
except (OSError, PermissionError):
|
|
91
|
+
return False
|
|
92
|
+
|
|
93
|
+
return True
|
|
94
|
+
|
|
95
|
+
def get_file_icon(self, item_name, is_directory):
|
|
96
|
+
"""Get appropriate icon for the item"""
|
|
97
|
+
if is_directory:
|
|
98
|
+
return "📁 "
|
|
99
|
+
|
|
100
|
+
file_ext = Path(item_name).suffix.lower()
|
|
101
|
+
icons = {
|
|
102
|
+
'.py': '🐍 ', '.js': '📜 ', '.html': '🌐 ', '.css': '🎨 ',
|
|
103
|
+
'.json': '📋 ', '.xml': '📄 ', '.txt': '📝 ', '.md': '📖 ',
|
|
104
|
+
'.pdf': '📕 ', '.doc': '📘 ', '.docx': '📘 ',
|
|
105
|
+
'.jpg': '🖼 ', '.jpeg': '🖼 ', '.png': '🖼 ', '.gif': '🖼 ',
|
|
106
|
+
'.mp3': '🎵 ', '.mp4': '🎬 ', '.avi': '🎬 ', '.mkv': '🎬 ',
|
|
107
|
+
'.zip': '📦 ', '.tar': '📦 ', '.gz': '📦 ', '.rar': '📦 ',
|
|
108
|
+
'.exe': '⚙️ ', '.app': '⚙️ ', '.deb': '⚙️ ', '.rpm': '⚙️ ',
|
|
109
|
+
}
|
|
110
|
+
return icons.get(file_ext, '📄 ')
|
|
111
|
+
|
|
112
|
+
def print_tree(self, start_path, prefix="", is_last=True, options=None, current_depth=0):
|
|
113
|
+
"""Recursively print the directory tree structure"""
|
|
114
|
+
self.current_depth = current_depth
|
|
115
|
+
|
|
116
|
+
item_name = os.path.basename(start_path)
|
|
117
|
+
is_directory = os.path.isdir(start_path)
|
|
118
|
+
|
|
119
|
+
# Get icon and format item name
|
|
120
|
+
icon = self.get_file_icon(item_name, is_directory) if getattr(options, 'icons', False) else ""
|
|
121
|
+
size_info = ""
|
|
122
|
+
|
|
123
|
+
if getattr(options, 'show_size', False) and not is_directory:
|
|
124
|
+
try:
|
|
125
|
+
file_size = os.path.getsize(start_path)
|
|
126
|
+
size_info = f" [{self.format_size(file_size)}]"
|
|
127
|
+
except (OSError, PermissionError):
|
|
128
|
+
size_info = " [N/A]"
|
|
129
|
+
|
|
130
|
+
# Print the current item
|
|
131
|
+
connector = "└── " if is_last else "├── "
|
|
132
|
+
print(prefix + connector + icon + item_name + size_info)
|
|
133
|
+
|
|
134
|
+
# Update statistics
|
|
135
|
+
if is_directory:
|
|
136
|
+
self.stats['directories'] += 1
|
|
137
|
+
else:
|
|
138
|
+
self.stats['files'] += 1
|
|
139
|
+
try:
|
|
140
|
+
self.stats['total_size'] += os.path.getsize(start_path)
|
|
141
|
+
except (OSError, PermissionError):
|
|
142
|
+
pass
|
|
143
|
+
|
|
144
|
+
# Process directory contents
|
|
145
|
+
if is_directory and (not getattr(options, 'max_depth', None) or current_depth < getattr(options, 'max_depth', 0)):
|
|
146
|
+
try:
|
|
147
|
+
items = []
|
|
148
|
+
for item in os.listdir(start_path):
|
|
149
|
+
item_path = os.path.join(start_path, item)
|
|
150
|
+
if self.should_include_item(item_path, item, options):
|
|
151
|
+
items.append((item, item_path))
|
|
152
|
+
|
|
153
|
+
# Sort based on options
|
|
154
|
+
sort_by = getattr(options, 'sort_by', 'name')
|
|
155
|
+
if sort_by == 'name':
|
|
156
|
+
items.sort(key=lambda x: x[0].lower())
|
|
157
|
+
elif sort_by == 'size':
|
|
158
|
+
items.sort(key=lambda x: os.path.getsize(x[1]) if os.path.isfile(x[1]) else 0)
|
|
159
|
+
elif sort_by == 'type':
|
|
160
|
+
items.sort(key=lambda x: (not os.path.isdir(x[1]), Path(x[0]).suffix.lower(), x[0].lower()))
|
|
161
|
+
else: # default: directories first
|
|
162
|
+
items.sort(key=lambda x: (not os.path.isdir(x[1]), x[0].lower()))
|
|
163
|
+
|
|
164
|
+
# Reverse if requested
|
|
165
|
+
if getattr(options, 'reverse_sort', False):
|
|
166
|
+
items.reverse()
|
|
167
|
+
|
|
168
|
+
# Calculate new prefix
|
|
169
|
+
new_prefix = prefix + (" " if is_last else "│ ")
|
|
170
|
+
|
|
171
|
+
# Process each item
|
|
172
|
+
for i, (item_name, item_path) in enumerate(items):
|
|
173
|
+
is_last_item = (i == len(items) - 1)
|
|
174
|
+
self.print_tree(item_path, new_prefix, is_last_item, options, current_depth + 1)
|
|
175
|
+
|
|
176
|
+
except PermissionError:
|
|
177
|
+
print(prefix + " └── 🔒 [Permission Denied]")
|
|
178
|
+
except Exception as e:
|
|
179
|
+
print(prefix + f" └── ❌ [Error: {str(e)}]")
|
|
180
|
+
|
|
181
|
+
def export_tree(self, start_path, output_file, options):
|
|
182
|
+
"""Export tree to a file"""
|
|
183
|
+
original_stdout = sys.stdout
|
|
184
|
+
with open(output_file, 'w', encoding='utf-8') as f:
|
|
185
|
+
sys.stdout = f
|
|
186
|
+
self.print_detailed_tree(start_path, options)
|
|
187
|
+
sys.stdout = original_stdout
|
|
188
|
+
print(f"Tree exported to: {output_file}")
|
|
189
|
+
|
|
190
|
+
def print_detailed_tree(self, start_path, options):
|
|
191
|
+
"""Print detailed directory tree with header information"""
|
|
192
|
+
print(f"Directory Tree: {start_path}")
|
|
193
|
+
print("=" * 60)
|
|
194
|
+
|
|
195
|
+
if getattr(options, 'show_size', False):
|
|
196
|
+
total_size = self.get_directory_size(start_path)
|
|
197
|
+
print(f"Total Size: {self.format_size(total_size)}")
|
|
198
|
+
|
|
199
|
+
print(f"Filters: {self.get_filters_summary(options)}")
|
|
200
|
+
print()
|
|
201
|
+
|
|
202
|
+
self.print_tree(start_path, options=options)
|
|
203
|
+
|
|
204
|
+
print()
|
|
205
|
+
print("=" * 60)
|
|
206
|
+
self.print_statistics()
|
|
207
|
+
|
|
208
|
+
def get_filters_summary(self, options):
|
|
209
|
+
"""Get a summary of applied filters"""
|
|
210
|
+
filters = []
|
|
211
|
+
if getattr(options, 'no_file', False):
|
|
212
|
+
filters.append("No Files")
|
|
213
|
+
if getattr(options, 'no_folder', False):
|
|
214
|
+
filters.append("No Folders")
|
|
215
|
+
if getattr(options, 'exclude_folders', []):
|
|
216
|
+
filters.append(f"Exclude folders: {', '.join(options.exclude_folders)}")
|
|
217
|
+
if getattr(options, 'exclude_extensions', []):
|
|
218
|
+
filters.append(f"Exclude extensions: {', '.join(options.exclude_extensions)}")
|
|
219
|
+
if getattr(options, 'exclude_files', []):
|
|
220
|
+
filters.append(f"Exclude files: {', '.join(options.exclude_files)}")
|
|
221
|
+
if getattr(options, 'include_patterns', []):
|
|
222
|
+
filters.append(f"Include patterns: {', '.join(options.include_patterns)}")
|
|
223
|
+
if getattr(options, 'max_depth', None):
|
|
224
|
+
filters.append(f"Max depth: {options.max_depth}")
|
|
225
|
+
if getattr(options, 'min_size', None):
|
|
226
|
+
filters.append(f"Min size: {self.format_size(options.min_size)}")
|
|
227
|
+
if getattr(options, 'max_size', None):
|
|
228
|
+
filters.append(f"Max size: {self.format_size(options.max_size)}")
|
|
229
|
+
|
|
230
|
+
return "; ".join(filters) if filters else "None"
|
|
231
|
+
|
|
232
|
+
def print_statistics(self):
|
|
233
|
+
"""Print summary statistics"""
|
|
234
|
+
print("Statistics:")
|
|
235
|
+
print(f" 📁 Directories: {self.stats['directories']}")
|
|
236
|
+
print(f" 📄 Files: {self.stats['files']}")
|
|
237
|
+
print(f" 💾 Total Size: {self.format_size(self.stats['total_size'])}")
|
|
238
|
+
print(f" 🚫 Skipped Items: {self.stats['skipped_items']}")
|
|
239
|
+
|
|
240
|
+
def get_directory_size(self, path):
|
|
241
|
+
"""Calculate total directory size"""
|
|
242
|
+
total_size = 0
|
|
243
|
+
try:
|
|
244
|
+
for dirpath, dirnames, filenames in os.walk(path):
|
|
245
|
+
for filename in filenames:
|
|
246
|
+
filepath = os.path.join(dirpath, filename)
|
|
247
|
+
try:
|
|
248
|
+
total_size += os.path.getsize(filepath)
|
|
249
|
+
except (OSError, PermissionError):
|
|
250
|
+
continue
|
|
251
|
+
except (OSError, PermissionError):
|
|
252
|
+
pass
|
|
253
|
+
return total_size
|
|
254
|
+
|
|
255
|
+
def format_size(self, size_bytes):
|
|
256
|
+
"""Convert bytes to human readable format"""
|
|
257
|
+
if size_bytes == 0:
|
|
258
|
+
return "0 B"
|
|
259
|
+
|
|
260
|
+
size_names = ["B", "KB", "MB", "GB", "TB"]
|
|
261
|
+
i = 0
|
|
262
|
+
while size_bytes >= 1024 and i < len(size_names) - 1:
|
|
263
|
+
size_bytes /= 1024.0
|
|
264
|
+
i += 1
|
|
265
|
+
return f"{size_bytes:.2f} {size_names[i]}"
|
|
266
|
+
|
|
267
|
+
def parse_size(size_str):
|
|
268
|
+
"""Parse size string like '10M', '1G', '500K' to bytes"""
|
|
269
|
+
if not size_str:
|
|
270
|
+
return None
|
|
271
|
+
|
|
272
|
+
size_str = size_str.upper().strip()
|
|
273
|
+
multipliers = {'B': 1, 'K': 1024, 'M': 1024**2, 'G': 1024**3, 'T': 1024**4}
|
|
274
|
+
|
|
275
|
+
if size_str[-1] in multipliers:
|
|
276
|
+
number = float(size_str[:-1])
|
|
277
|
+
multiplier = multipliers[size_str[-1]]
|
|
278
|
+
return int(number * multiplier)
|
|
279
|
+
else:
|
|
280
|
+
return int(size_str)
|
|
281
|
+
|
|
282
|
+
def main():
|
|
283
|
+
parser = argparse.ArgumentParser(
|
|
284
|
+
description="Display directory tree structure with advanced filtering",
|
|
285
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
286
|
+
epilog="""
|
|
287
|
+
Examples:
|
|
288
|
+
stree /path/to/directory
|
|
289
|
+
stree /path --no-file
|
|
290
|
+
stree /path --no-folder
|
|
291
|
+
stree /path --exclude-extensions py,txt,log
|
|
292
|
+
stree /path --exclude-folders node_modules,__pycache__
|
|
293
|
+
stree /path --exclude-files "*.tmp,*.bak"
|
|
294
|
+
stree /path --include-patterns "*.py,*.js"
|
|
295
|
+
stree /path --max-depth 3
|
|
296
|
+
stree /path --min-size 1M --max-size 10M
|
|
297
|
+
stree /path --sort-by size --reverse-sort
|
|
298
|
+
stree /path --show-hidden --show-size
|
|
299
|
+
stree /path --icons
|
|
300
|
+
stree /path --export tree.txt
|
|
301
|
+
"""
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
# Basic options
|
|
305
|
+
parser.add_argument("path", help="Path to the directory to analyze")
|
|
306
|
+
parser.add_argument("--show-hidden", action="store_true", help="Show hidden files and folders")
|
|
307
|
+
|
|
308
|
+
# Exclusion filters
|
|
309
|
+
parser.add_argument("--no-file", action="store_true", help="Exclude all files")
|
|
310
|
+
parser.add_argument("--no-folder", action="store_true", help="Exclude all folders")
|
|
311
|
+
parser.add_argument("--exclude-extensions", help="Comma-separated file extensions to exclude (e.g., py,txt,log)")
|
|
312
|
+
parser.add_argument("--exclude-folders", help="Comma-separated folder names to exclude (e.g., node_modules,__pycache__)")
|
|
313
|
+
parser.add_argument("--exclude-files", help="Comma-separated file patterns to exclude (e.g., *.tmp,*.bak)")
|
|
314
|
+
|
|
315
|
+
# Inclusion filters
|
|
316
|
+
parser.add_argument("--include-patterns", help="Comma-separated patterns to include (e.g., *.py,*.js)")
|
|
317
|
+
|
|
318
|
+
# Size and depth filters
|
|
319
|
+
parser.add_argument("--max-depth", type=int, help="Maximum depth to traverse")
|
|
320
|
+
parser.add_argument("--min-size", help="Minimum file size (e.g., 1M, 100K, 1G)")
|
|
321
|
+
parser.add_argument("--max-size", help="Maximum file size (e.g., 1M, 100K, 1G)")
|
|
322
|
+
|
|
323
|
+
# Display options
|
|
324
|
+
parser.add_argument("--show-size", action="store_true", help="Show file sizes")
|
|
325
|
+
parser.add_argument("--icons", action="store_true", help="Show file type icons")
|
|
326
|
+
parser.add_argument("--sort-by", choices=['name', 'size', 'type'], default='name', help="Sort method")
|
|
327
|
+
parser.add_argument("--reverse-sort", action="store_true", help="Reverse sort order")
|
|
328
|
+
parser.add_argument("--export", help="Export tree to specified file")
|
|
329
|
+
|
|
330
|
+
# Version
|
|
331
|
+
parser.add_argument("--version", action="version", version=f"storage-viewer {__version__}")
|
|
332
|
+
|
|
333
|
+
args = parser.parse_args()
|
|
334
|
+
|
|
335
|
+
# Validate path
|
|
336
|
+
if not os.path.exists(args.path):
|
|
337
|
+
print(f"Error: Path '{args.path}' does not exist")
|
|
338
|
+
sys.exit(1)
|
|
339
|
+
|
|
340
|
+
# Parse comma-separated lists
|
|
341
|
+
if args.exclude_extensions:
|
|
342
|
+
args.exclude_extensions = [ext.strip() for ext in args.exclude_extensions.split(',')]
|
|
343
|
+
else:
|
|
344
|
+
args.exclude_extensions = []
|
|
345
|
+
|
|
346
|
+
if args.exclude_folders:
|
|
347
|
+
args.exclude_folders = [folder.strip() for folder in args.exclude_folders.split(',')]
|
|
348
|
+
else:
|
|
349
|
+
args.exclude_folders = []
|
|
350
|
+
|
|
351
|
+
if args.exclude_files:
|
|
352
|
+
args.exclude_files = [pattern.strip() for pattern in args.exclude_files.split(',')]
|
|
353
|
+
else:
|
|
354
|
+
args.exclude_files = []
|
|
355
|
+
|
|
356
|
+
if args.include_patterns:
|
|
357
|
+
args.include_patterns = [pattern.strip() for pattern in args.include_patterns.split(',')]
|
|
358
|
+
else:
|
|
359
|
+
args.include_patterns = []
|
|
360
|
+
|
|
361
|
+
# Parse size filters
|
|
362
|
+
if args.min_size:
|
|
363
|
+
args.min_size = parse_size(args.min_size)
|
|
364
|
+
if args.max_size:
|
|
365
|
+
args.max_size = parse_size(args.max_size)
|
|
366
|
+
|
|
367
|
+
# Convert to absolute path
|
|
368
|
+
abs_path = os.path.abspath(args.path)
|
|
369
|
+
|
|
370
|
+
try:
|
|
371
|
+
tree = DirectoryTree()
|
|
372
|
+
|
|
373
|
+
if args.export:
|
|
374
|
+
tree.export_tree(abs_path, args.export, args)
|
|
375
|
+
else:
|
|
376
|
+
tree.print_detailed_tree(abs_path, args)
|
|
377
|
+
|
|
378
|
+
except KeyboardInterrupt:
|
|
379
|
+
print("\nOperation cancelled by user")
|
|
380
|
+
except Exception as e:
|
|
381
|
+
print(f"Error: {str(e)}")
|
|
382
|
+
import traceback
|
|
383
|
+
traceback.print_exc()
|
|
384
|
+
|
|
385
|
+
if __name__ == "__main__":
|
|
386
|
+
main()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "storage-viewer"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Advanced directory tree visualizer with filtering options"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.7"
|
|
11
|
+
license = { file = "LICENSE" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Lingaraj Sa" }
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[project.scripts]
|
|
22
|
+
stree = "SourceCode.main:main"
|
|
23
|
+
|
|
24
|
+
[tool.setuptools]
|
|
25
|
+
packages = ["SourceCode"]
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: storage-viewer
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Advanced directory tree visualizer with filtering options
|
|
5
|
+
Author: Lingaraj Sa
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2024 Lingaraj Sa
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
29
|
+
Classifier: Programming Language :: Python :: 3
|
|
30
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
31
|
+
Requires-Python: >=3.7
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
License-File: LICENSE
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# Storage Viewer CLI
|
|
37
|
+
|
|
38
|
+

|
|
39
|
+

|
|
40
|
+

|
|
41
|
+
|
|
42
|
+
A powerful, feature-rich directory tree visualizer with advanced filtering options for the command line.
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- **Hierarchical tree display** — Clean, readable directory structures
|
|
47
|
+
- **Advanced filtering** — Filter by extension, folder, pattern, and size
|
|
48
|
+
- **Size analysis** — Show file sizes in human-readable formats
|
|
49
|
+
- **File type icons** — Visual indicators for different file types
|
|
50
|
+
- **Depth control** — Limit directory traversal depth
|
|
51
|
+
- **Export capability** — Save tree structures to files
|
|
52
|
+
- **Statistics** — Summaries of directories and files
|
|
53
|
+
- **Fast and lightweight** — No external dependencies
|
|
54
|
+
|
|
55
|
+
## Basic usage
|
|
56
|
+
|
|
57
|
+
View a directory tree:
|
|
58
|
+
|
|
59
|
+
```console
|
|
60
|
+
stree /path/to/directory
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Show file type icons:
|
|
64
|
+
|
|
65
|
+
```console
|
|
66
|
+
stree . --icons
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Show file sizes:
|
|
70
|
+
|
|
71
|
+
```console
|
|
72
|
+
stree . --show-size
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Limit traversal depth:
|
|
76
|
+
|
|
77
|
+
```console
|
|
78
|
+
stree . --max-depth 3
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Examples
|
|
82
|
+
|
|
83
|
+
Exclude development folders:
|
|
84
|
+
|
|
85
|
+
```console
|
|
86
|
+
stree . --exclude-folders __pycache__,node_modules,.git,venv --max-depth 4
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Find large files:
|
|
90
|
+
|
|
91
|
+
```console
|
|
92
|
+
stree /home --min-size 100M --show-size --sort-by size --reverse-sort
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Export the tree to a file:
|
|
96
|
+
|
|
97
|
+
```console
|
|
98
|
+
stree /path --export directory_structure.txt
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Show only code files:
|
|
102
|
+
|
|
103
|
+
```console
|
|
104
|
+
stree . --include-patterns "*.py,*.js,*.java,*.cpp"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Exclude temporary files:
|
|
108
|
+
|
|
109
|
+
```console
|
|
110
|
+
stree . --exclude-extensions tmp,bak,log --exclude-files "*.tmp,*.bak"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Options
|
|
114
|
+
|
|
115
|
+
```text
|
|
116
|
+
Usage: stree [OPTIONS] PATH
|
|
117
|
+
|
|
118
|
+
Display directory tree structure with advanced filtering.
|
|
119
|
+
|
|
120
|
+
Options:
|
|
121
|
+
--show-hidden Show hidden files and folders
|
|
122
|
+
--no-file Exclude all files
|
|
123
|
+
--no-folder Exclude all folders
|
|
124
|
+
--exclude-extensions Comma-separated file extensions to exclude
|
|
125
|
+
--exclude-folders Comma-separated folder names to exclude
|
|
126
|
+
--exclude-files Comma-separated file patterns to exclude
|
|
127
|
+
--include-patterns Comma-separated patterns to include
|
|
128
|
+
--max-depth Maximum depth to traverse
|
|
129
|
+
--min-size Minimum file size (e.g., 1M, 100K, 1G)
|
|
130
|
+
--max-size Maximum file size (e.g., 1M, 100K, 1G)
|
|
131
|
+
--show-size Show file sizes
|
|
132
|
+
--icons Show file type icons
|
|
133
|
+
--sort-by Sort method: name, size, or type
|
|
134
|
+
--reverse-sort Reverse sort order
|
|
135
|
+
--export Export tree to specified file
|
|
136
|
+
--version Show version
|
|
137
|
+
--help Show this help message
|
|
138
|
+
```
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
SourceCode/__init__.py
|
|
5
|
+
SourceCode/main.py
|
|
6
|
+
storage_viewer.egg-info/PKG-INFO
|
|
7
|
+
storage_viewer.egg-info/SOURCES.txt
|
|
8
|
+
storage_viewer.egg-info/dependency_links.txt
|
|
9
|
+
storage_viewer.egg-info/entry_points.txt
|
|
10
|
+
storage_viewer.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SourceCode
|