Compare commits
6 Commits
package-it
...
master
Author | SHA1 | Date | |
---|---|---|---|
2ca1f9f4ec | |||
d34cd6ec93 | |||
870aa07839 | |||
7f7ee5a8ce | |||
019db25363 | |||
85865f7c05 |
16
README.md
16
README.md
@ -13,8 +13,8 @@ dependencies. This package requires two underlying packages. Those are
|
|||||||
`python-magic`, and `pillow`. Here are the installation documentation for those
|
`python-magic`, and `pillow`. Here are the installation documentation for those
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
* https://github.com/threatstack/libmagic
|
* [github.com/threatstack/libmagic](https://github.com/threatstack/libmagic)
|
||||||
* https://pillow.readthedocs.io/en/5.3.x/installation.html#external-libraries
|
* [pillow.readthedocs.io](https://pillow.readthedocs.io/en/5.3.x/installation.html#external-libraries)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@ -28,6 +28,16 @@ Using [--user] or [a virtual environment] is recommended:
|
|||||||
$ pip install --user distribusi
|
$ pip install --user distribusi
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: check if the path of your local bin is added to your shell path (otherwise you cannot run distribusi from the shell directly).
|
||||||
|
|
||||||
|
To check where distribusi is installed:
|
||||||
|
|
||||||
|
$ find * | grep distribusi
|
||||||
|
|
||||||
|
Add local bin to the $PATH variable:
|
||||||
|
|
||||||
|
$ PATH=$PATH:/home/USERNAME/.local/bin/
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Get help with:
|
Get help with:
|
||||||
@ -81,6 +91,8 @@ $ pipenv run distribusi --help
|
|||||||
|
|
||||||
## Release It
|
## Release It
|
||||||
|
|
||||||
|
You'll need a [PyPi](https://pypi.org/) account and to be added as a maintainer.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ make publish
|
$ make publish
|
||||||
```
|
```
|
||||||
|
98
distribusi/README.md
Normal file
98
distribusi/README.md
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# Distribusi CMS
|
||||||
|
|
||||||
|
`distribusi` is a content management system for the web that produces static
|
||||||
|
index pages based on folders in the filesystem. It is inspired by the automatic
|
||||||
|
index functions featured in several web servers. It works by traversing the
|
||||||
|
file system and directory hierarchy to automatically list all the files in the
|
||||||
|
directory and providing them with html classes and tags for easy styling.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
While a Pip install will pull in Python dependencies, you might need system
|
||||||
|
dependencies. This package requires two underlying packages. Those are
|
||||||
|
`python-magic`, and `pillow`. Here are the installation documentation for those
|
||||||
|
packages:
|
||||||
|
|
||||||
|
* [github.com/threatstack/libmagic](https://github.com/threatstack/libmagic)
|
||||||
|
* [pillow.readthedocs.io](https://pillow.readthedocs.io/en/5.3.x/installation.html#external-libraries)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Using [--user] or [a virtual environment] is recommended:
|
||||||
|
|
||||||
|
[--user]: https://packaging.python.org/tutorials/installing-packages/#installing-to-the-user-site
|
||||||
|
[a virtual environment]: https://packaging.python.org/tutorials/installing-packages/#creating-virtual-environments
|
||||||
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ pip install --user distribusi
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: check if the path of your local bin is added to your shell path (otherwise you cannot run distribusi from the shell directly).
|
||||||
|
|
||||||
|
To check where distribusi is installed:
|
||||||
|
|
||||||
|
$ find * | grep distribusi
|
||||||
|
|
||||||
|
Add local bin to the $PATH variable:
|
||||||
|
|
||||||
|
$ PATH=$PATH:/home/USERNAME/.local/bin/
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Get help with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ distribusi --help
|
||||||
|
```
|
||||||
|
|
||||||
|
Make a distribusi of your home folder:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ distribusi -d ~/
|
||||||
|
```
|
||||||
|
|
||||||
|
You will find that you now have an `index.html` in every folder.
|
||||||
|
|
||||||
|
Create a quick gallery for the web:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ distribusi -d /path/to/my/photos -t
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates an `index.html` with `base64` encoded thumbnails.
|
||||||
|
|
||||||
|
Generate verbose output:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ distribusi -v
|
||||||
|
```
|
||||||
|
|
||||||
|
Make an index of the archive page:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ distribusi -d /var/www/archive/my_event -t -v
|
||||||
|
```
|
||||||
|
|
||||||
|
# ✌
|
||||||
|
|
||||||
|
## Change It
|
||||||
|
|
||||||
|
Install [Pipenv] and then run:
|
||||||
|
|
||||||
|
[Pipenv]: https://pipenv.readthedocs.io/en/latest/install/#installing-pipenv
|
||||||
|
|
||||||
|
```
|
||||||
|
$ pipenv install --dev
|
||||||
|
$ pipenv run pip install -e .
|
||||||
|
$ pipenv run distribusi --help
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Release It
|
||||||
|
|
||||||
|
You'll need a [PyPi](https://pypi.org/) account and to be added as a maintainer.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ make publish
|
||||||
|
```
|
@ -1,6 +1,6 @@
|
|||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
from distribusi.distribusi import distribusify
|
from distribusi import distribusify
|
||||||
|
|
||||||
|
|
||||||
def build_argparser():
|
def build_argparser():
|
||||||
@ -56,4 +56,8 @@ def cli_entrypoint():
|
|||||||
else:
|
else:
|
||||||
directory = '.'
|
directory = '.'
|
||||||
|
|
||||||
|
print('running distribusi ...')
|
||||||
distribusify(args, directory)
|
distribusify(args, directory)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
cli_entrypoint()
|
@ -3,30 +3,30 @@ import os
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import magic
|
import magic
|
||||||
from distribusi.page_template import html_footer, html_head
|
from page_template import html_footer, html_head, styles
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
CODE_TYPES = [
|
CODE_TYPES = [
|
||||||
'x-c',
|
'x-c',
|
||||||
'html'
|
'html'
|
||||||
]
|
]
|
||||||
|
|
||||||
FILE_TYPES = {
|
FILE_TYPES = {
|
||||||
'image': '<img class="image" src="{}">',
|
'image': '<img class="image" src="{}">',
|
||||||
'pdf': (
|
'pdf': (
|
||||||
'<object data="{}" class="pdf" type="application/pdf">'
|
'<object data="{}" class="pdf" type="application/pdf">'
|
||||||
'<embed src="{}" type="application/pdf" /></object>'
|
'<embed src="{}" type="application/pdf" /></object>'
|
||||||
),
|
),
|
||||||
'text': '<a href="{}" class="text">{}</a>',
|
'text': '<a href="{}" class="text">{}</a>',
|
||||||
'video': (
|
'video': (
|
||||||
'<video class="video" controls>'
|
'<video class="video" controls>'
|
||||||
'<source src="{}"></source></video>'
|
'<source src="{}"></source></video>'
|
||||||
),
|
),
|
||||||
'audio': (
|
'audio': (
|
||||||
'<audio controls class="audio">'
|
'<audio controls class="audio">'
|
||||||
'<source src="{}"></source></audio>'
|
'<source src="{}"></source></audio>'
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -34,94 +34,107 @@ MIME_TYPE = magic.Magic(mime=True)
|
|||||||
|
|
||||||
|
|
||||||
def thumbnail(image, name):
|
def thumbnail(image, name):
|
||||||
size = (450, 450)
|
size = (450, 450)
|
||||||
im = Image.open(image)
|
im = Image.open(image)
|
||||||
im.thumbnail(size)
|
im.thumbnail(size)
|
||||||
output = BytesIO()
|
output = BytesIO()
|
||||||
im.save(output, format='JPEG')
|
im.save(output, format='JPEG')
|
||||||
im_data = output.getvalue()
|
im_data = output.getvalue()
|
||||||
data_url = base64.b64encode(im_data).decode()
|
data_url = base64.b64encode(im_data).decode()
|
||||||
return (
|
return (
|
||||||
"<a href='{}'><img class='thumbnail' "
|
"<a href='{}'><img class='thumbnail' "
|
||||||
"src='data:image/jpg;base64,{}'></a>"
|
"src='data:image/jpg;base64,{}'></a>"
|
||||||
).format(name, data_url)
|
).format(name, data_url)
|
||||||
|
|
||||||
|
|
||||||
def div(mime, tag, *values):
|
def div(mime, tag, *values):
|
||||||
id_name = values[0].split('.')[0].replace(' ', '_')
|
id_name = values[0].split('.')[0].replace(' ', '_')
|
||||||
|
|
||||||
if 'image' in mime:
|
if 'image' in mime:
|
||||||
html = '<div id="{}">{}<br><span class="filename">{}</span></div>'
|
html = '<div class="{}">{}<span class="filename">{}</span></div>'
|
||||||
elif 'pdf' in mime:
|
elif 'pdf' in mime:
|
||||||
html = '<div id="{}">{}<br><class="filename">{}</span></div>'
|
html = '<div class="{}">{}<span class="filename">{}</span></div>'
|
||||||
else:
|
else:
|
||||||
html = '<div id="{}">{}</div>'
|
html = '<div class="{}">{}</div>'
|
||||||
|
|
||||||
return html.format(id_name, tag, values[0])
|
return html.format(id_name, tag, values[0])
|
||||||
|
|
||||||
|
|
||||||
def distribusify(args, directory): # noqa
|
def distribusify(args, directory): # noqa
|
||||||
for root, dirs, files in os.walk(directory):
|
for root, dirs, files in os.walk(directory):
|
||||||
html = []
|
html = []
|
||||||
|
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
print('Listing', root)
|
print('Listing', root)
|
||||||
|
|
||||||
for name in files:
|
for name in files:
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
print('Adding', name)
|
print('Adding', name)
|
||||||
if 'index.html' not in name:
|
if 'index.html' not in name:
|
||||||
full_path = os.path.join(root, name)
|
full_path = os.path.join(root, name)
|
||||||
mime = MIME_TYPE.from_file(full_path)
|
mime = MIME_TYPE.from_file(full_path)
|
||||||
mime, format = mime.split('/') # example: plain text
|
mime, format = mime.split('/') # example: plain text
|
||||||
|
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
print(mime, format)
|
print(mime, format)
|
||||||
|
|
||||||
if mime in FILE_TYPES:
|
if mime in FILE_TYPES:
|
||||||
# expansion for different kind of textfiles
|
# expansion for different kind of textfiles
|
||||||
if mime == 'text':
|
if mime == 'text':
|
||||||
if name.endswith('.html') or name.endswith('.txt'):
|
if name.endswith('.html') or name.endswith('.txt'):
|
||||||
# what types of text files to expand
|
# what types of text files to expand
|
||||||
a = open(full_path).read()
|
a = open(full_path).read()
|
||||||
elif format in CODE_TYPES:
|
elif format in CODE_TYPES:
|
||||||
# if the plain text is code,
|
# if the plain text is code,
|
||||||
# which types do we wrap in pre-tags?
|
# which types do we wrap in pre-tags?
|
||||||
a = "<pre>"+open(full_path).read()+"</pre>"
|
a = "<pre>"+open(full_path).read()+"</pre>"
|
||||||
else:
|
else:
|
||||||
a = FILE_TYPES[mime]
|
a = FILE_TYPES[mime]
|
||||||
|
|
||||||
if mime == 'image' and args.thumbnail:
|
if mime == 'image' and args.thumbnail:
|
||||||
a = thumbnail(full_path, name)
|
a = thumbnail(full_path, name)
|
||||||
else:
|
else:
|
||||||
a = FILE_TYPES[mime]
|
a = FILE_TYPES[mime]
|
||||||
|
|
||||||
if format in FILE_TYPES:
|
if format in FILE_TYPES:
|
||||||
a = FILE_TYPES[format]
|
a = FILE_TYPES[format]
|
||||||
|
|
||||||
if mime not in FILE_TYPES and format not in FILE_TYPES:
|
if mime not in FILE_TYPES and format not in FILE_TYPES:
|
||||||
# catch exceptions not defined in FILE_TYPES before
|
# catch exceptions not defined in FILE_TYPES before
|
||||||
a = "<a href='{}'>{}</a>"
|
a = "<a href='{}'>{}</a>"
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
message = 'mime-type not in list, adding as href: \n'
|
message = 'mime-type not in list, adding as href: \n'
|
||||||
print(message, mime, format, name)
|
print(message, mime, format, name)
|
||||||
|
|
||||||
a = a.replace('{}', name)
|
a = a.replace('{}', name)
|
||||||
html.append(div(mime, a, name))
|
html.append(div(mime, a, name))
|
||||||
|
|
||||||
if root != directory:
|
if root != directory:
|
||||||
html.append('<a href="../">../</a>')
|
html.append('<a class="back" href="../">../</a>')
|
||||||
|
position = '../' * root.count('/')
|
||||||
|
stylesheet = '<link type="text/css" rel="stylesheet" href="{}stylesheet.css" />'.format(position)
|
||||||
|
else:
|
||||||
|
stylesheet = '<link type="text/css" rel="stylesheet" href="stylesheet.css" />'
|
||||||
|
|
||||||
|
for name in dirs:
|
||||||
|
a = "<a href='{}' class='dir'>{}/</a>".replace('{}', name)
|
||||||
|
html.insert(0, div('dir', a, 'folder'))
|
||||||
|
|
||||||
for name in dirs:
|
with open(os.path.join(root, 'index.html'), 'w') as f:
|
||||||
a = "<a href='{}' class='dir'>{}/</a>".replace('{}', name)
|
if not args.no_template:
|
||||||
html.append(div('dir', a, 'folder'))
|
header = html_head(stylesheet)
|
||||||
|
f.write(header)
|
||||||
|
|
||||||
with open(os.path.join(root, 'index.html'), 'w') as f:
|
h1 = '<h1 class="foldername">{}</h1>'.format(''.join(root))
|
||||||
if not args.no_template:
|
print('path:', h1)
|
||||||
f.write(html_head)
|
html.insert(0, h1)
|
||||||
|
|
||||||
for line in html:
|
for line in html:
|
||||||
f.write(line+'\n')
|
f.write(line+'\n')
|
||||||
|
|
||||||
if not args.no_template:
|
if not args.no_template:
|
||||||
f.write(html_footer)
|
f.write(html_footer)
|
||||||
|
|
||||||
|
print('adding a stylesheet to {}'.format(directory))
|
||||||
|
with open(os.path.join(directory, 'stylesheet.css'), 'w') as s:
|
||||||
|
s.write(styles)
|
||||||
|
@ -1,21 +1,100 @@
|
|||||||
html_head = """
|
def html_head(stylesheet):
|
||||||
|
header = """
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<!-- Generated with distribusi https://git.vvvvvvaria.org/rra/distribusi -->
|
<!-- Generated with distribusi https://git.vvvvvvaria.org/rra/distribusi -->
|
||||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||||
<style>
|
{}
|
||||||
.image{max-width: 100%;}
|
</head>
|
||||||
.pdf {width:100%;}
|
<body>
|
||||||
div{width: 640px;float:left;padding:1em;}
|
""".format(stylesheet)
|
||||||
video {width:640px;}
|
return header
|
||||||
.dir::before{content:"📁";font-size:18px;}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
"""
|
|
||||||
|
|
||||||
html_footer = """
|
html_footer = """
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
styles = """
|
||||||
|
body{
|
||||||
|
position: absolute;
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
margin:3.5em 1em 1em 1em;
|
||||||
|
}
|
||||||
|
.stylesheet{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1.foldername{
|
||||||
|
margin: 0 0 1em 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a{
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div{
|
||||||
|
width: 640px;
|
||||||
|
float:left;
|
||||||
|
padding:1em;
|
||||||
|
}
|
||||||
|
div.folder, div.README{
|
||||||
|
float: none;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
}
|
||||||
|
a.dir::before{
|
||||||
|
content:"📁";
|
||||||
|
font-size:18px;
|
||||||
|
padding-right: 1em;
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
a.text::before{
|
||||||
|
content:"☛";
|
||||||
|
font-size:18px;
|
||||||
|
padding-right: 1em;
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
a.back::before{
|
||||||
|
content:"⮪";
|
||||||
|
font-size:18px;
|
||||||
|
padding-right: 1.25em;
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
a.back{
|
||||||
|
position: fixed;
|
||||||
|
width:100%;
|
||||||
|
background-color:white;
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
padding:1em 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image{
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
.pdf {
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
|
video {
|
||||||
|
width:640px;
|
||||||
|
}
|
||||||
|
.filename{
|
||||||
|
display:block;
|
||||||
|
font-size:small;
|
||||||
|
margin-top:0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 600px) {
|
||||||
|
body{
|
||||||
|
width:calc(100% - 2em);
|
||||||
|
}
|
||||||
|
div {
|
||||||
|
width:calc(100% - 2em);
|
||||||
|
}
|
||||||
|
div a img{
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
Loading…
Reference in New Issue
Block a user