Compare commits

...

58 Commits

Author SHA1 Message Date
decentral1se
4964790762
follow title in upstream README 2022-02-04 13:01:12 +01:00
decentral1se
9ef68194db
caps that 2022-02-04 13:00:52 +01:00
decentral1se
1418feeaa8
fix all my typos 2022-02-04 13:00:14 +01:00
decentral1se
06cf1f5bdd
more uses 2022-02-04 12:59:12 +01:00
63becedaf5
Ensure we have wheel installed 2021-07-13 10:11:28 +02:00
6b627f0f96
Bump to new version 2021-07-13 10:08:01 +02:00
7a2f8a5142
Add change log entry
See #5.
2021-07-13 10:06:51 +02:00
decentral1se
6dd9fb7b40 Merge pull request 'a bug fix for --no-filenames option to work! (for images)' (#5) from losttra8n/distribusi:losttra8n-patch-1 into master
Reviewed-on: #5
2021-07-13 10:06:00 +02:00
losttra8n
46e68fc966 a bug fix for --no-filenames option to work! (for images)
i was trying to '-nf' option, then image captions were not consistent! i.e. they were still there with '-nf' option. so investigated.
2021-07-07 12:56:55 +02:00
rra
545577978a Update 'README.md'
added section for uses in the wild
2021-05-04 15:14:59 +02:00
60275a475e
Add sketchy build script 2020-05-03 18:11:20 +02:00
e730cfec55
Boot out a 0.0.9 release 2020-04-22 21:45:36 +02:00
rra
f766f67cee Merge branch 'master' of dickreckard/distribusi into master 2019-10-30 10:42:40 +01:00
49a521ea14 thumbnails png fix
couldn't generate thumbnails for png pictures due to different mode (RGBA vs RGB)
2019-10-21 00:22:26 +02:00
69fa5bb7c5 thumbnails fix
wrong indentation meant it never made the base64 thumbnails
2019-10-20 23:53:59 +02:00
5253d37411
Fix typo 2019-09-29 13:49:36 +02:00
c0c069cead
Update version to match next release 2019-09-29 13:34:49 +02:00
rra
64f449eee8 Update 'README.md' 2019-09-28 18:24:22 +02:00
601d5e86e7
Ignore publishing files 2019-09-20 17:16:29 +02:00
fd370294be
Fix back buttons when using index.html navigation 2019-09-20 17:13:19 +02:00
cb9e19d1a2
Allow to append index.html files on menus
There is also this `html.insert(0...` change that I am not sure
about but it seems to be working for us now.
2019-09-20 17:08:28 +02:00
2a0834734f
Allow skipping hidden and sorting 2019-09-20 16:47:04 +02:00
63902c079a
Merge lists and announce version skip 2019-09-15 23:07:21 +02:00
rra
6a9fbe9856 files are listed alphabetically, fixes to css and html 2019-09-15 17:39:04 +02:00
rra
321405c66f update changelog 2019-09-15 15:13:11 +02:00
RRA
010020e08b added --force flag to force overwriting non-distribusi indexes, code formatting 2019-09-15 15:06:23 +02:00
RRA
e21a78324f distribusi now only removes indexes that are created by distribusi 2019-09-15 14:29:16 +02:00
RRA
d5335f3212 added possibility to exclude directories with --exclude-directory option 2019-09-15 13:58:00 +02:00
rra
3247286a7e added info about exiftool 2019-09-13 13:18:42 +02:00
RRA
8529d952cd clean up according to pep8 2019-09-12 19:18:56 +02:00
rra
6400b2aea2 update changelog 2019-09-12 19:01:55 +02:00
rra
89067b48f7 Merge branch 'master' of https://git.vvvvvvaria.org/varia/distribusi 2019-09-12 18:50:03 +02:00
rra
bc6fa0bad8 unknown files now have icon 2019-09-12 18:46:33 +02:00
rra
fc19d536df default directory is always the current one, dont print anything if not verbose 2019-09-12 18:46:11 +02:00
rra
28f8793613 images now also respect --nf 2019-09-12 18:45:29 +02:00
rra
52830ff7c4 - changed variable names for clarity
- added f;ag to remove indexes
- made '--no-filename more consistent'
- styled verbose output better.
2019-09-12 18:40:32 +02:00
55e8fe190e
Make new release 2019-09-07 01:16:47 +02:00
80e8a5e4fd
Re-add pillow (still needed) and use looser bounds 2019-09-07 01:12:06 +02:00
rra
b613eef36c speed up by not calling exiftools always, images now always have figcaptions, catch errors properly 2019-09-04 16:13:29 +02:00
rra
dbe0e59b90 clean up argparse 2019-09-04 16:11:28 +02:00
rra
1611144f72 move mimetype mappings to separate file 2019-09-04 16:11:02 +02:00
9b8be7c761
Show how to upgrade 2019-07-08 20:43:33 +02:00
cb37e6428b
Add changelog 2019-07-08 20:25:00 +02:00
dc92a81035
Match other headers 2019-07-08 20:22:50 +02:00
e1ef66e646
Simplify install guide, add PyPi badge and access note 2019-07-08 20:20:00 +02:00
df71f44550
Clean up imports and formatting 2019-07-08 20:02:41 +02:00
d5606e5325
Bump to the next version and pin python-magic 2019-07-08 20:02:24 +02:00
8829a84b65
Add Black and Pip files config 2019-07-08 20:01:36 +02:00
6212f25536
Don't use pipenv for releases 2019-07-08 20:01:25 +02:00
b31e211498
Ignore new Pip related files 2019-07-08 20:01:15 +02:00
02b0e00892
Remove Pipenv stuff 2019-07-08 20:01:08 +02:00
lowrussia
415f8a5ac8 fixed style path issue 2019-07-08 17:49:12 +02:00
lowrussia
afaa58d710 added arguments for including exif metadata, including external css, not including filenames of pictures 2019-07-08 16:06:53 +02:00
lowrussia
7687aa96a2 added arguments for including exif metadata, including external css, not including filenames of pictures 2019-07-08 15:24:22 +02:00
mb
870aa07839 added a few lines on how to add distribusi to the $PATH variable 2019-06-11 12:55:03 +02:00
7f7ee5a8ce
Fix grammar. 2018-12-01 00:16:43 +01:00
019db25363
Add note about package maintainers. 2018-12-01 00:16:09 +01:00
85865f7c05
Add links instead. 2018-12-01 00:16:05 +01:00
13 changed files with 416 additions and 345 deletions

4
.gitignore vendored
View File

@ -5,3 +5,7 @@ __pycache__/
build/
dist
index.html
pip-wheel-metadata/
*.pdf
*.css
*.pyz

51
CHANGELOG.md Normal file
View File

@ -0,0 +1,51 @@
# Changelog
The changelog was only added at version 0.0.4.
## 0.0.10
- Fix `-nf` functionality ([#5](https://git.vvvvvvaria.org/varia/distribusi/pulls/5)) (thanks @losttra8n!)
## 0.0.9
- Fix thumbnail generation (thanks @dickreckard!)
- Adjust formatting for usage output
## 0.0.8
- Allow to ignore hidden directories with `--no-hidden`
- Files and directories are now sorted during distribusification.
- Allow to append `index.html` to the menu items with `--menu-with-index`
## 0.0.7
Let's think a bit about safety and robustness:
- distribusi only overwrites (or removes) indexes that have been created by distribusi itself
- override the above behaviour with `--force`
- `--exclude` now allows you to exclude folder names from being listed, this behaviour is not influenced by `--force`
And also some refactoring and niceties:
- Distribusi only prints when called with `--verbose`
- Restyled `--verbose` output
- Generated indexes can be removed with `--remove-indexes`
- .html and .txt files are now expanded and included as snippets in the index file
- code rewrite for clarity
- HTML output is more precisely styleable
## 0.0.6
Woops, we missed that one.
## 0.0.5
- Use loose bounds for dependencies
- Don't call exiftools on every execution
- If `PILLOW` can't thumbnail an image it is included as a link instead
## 0.0.4
- Add captions from EXIF metadata
- Custom stylesheet usage
- Ability to hide filenames

View File

@ -1,6 +1,4 @@
PIPENV := pipenv run
publish:
@rm -rf dist
@$(PIPENV) python setup.py bdist_wheel --universal
@$(PIPENV) twine upload dist/*
@python setup.py bdist_wheel --universal
@twine upload dist/*

11
Pipfile
View File

@ -1,11 +0,0 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
pillow = "*"
python-magic = "*"
twine = "*"
[packages]

169
Pipfile.lock generated
View File

@ -1,169 +0,0 @@
{
"_meta": {
"hash": {
"sha256": "84f37d28add02d81cc62fe51ee634dcc491c25ebdd9e0076e67f43039cd58c56"
},
"pipfile-spec": 6,
"requires": {},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {},
"develop": {
"bleach": {
"hashes": [
"sha256:48d39675b80a75f6d1c3bdbffec791cf0bbbab665cf01e20da701c77de278718",
"sha256:73d26f018af5d5adcdabf5c1c974add4361a9c76af215fe32fdec8a6fc5fb9b9"
],
"version": "==3.0.2"
},
"certifi": {
"hashes": [
"sha256:339dc09518b07e2fa7eda5450740925974815557727d6bd35d319c1524a04a4c",
"sha256:6d58c986d22b038c8c0df30d639f23a3e6d172a05c3583e766f4c0b785c0986a"
],
"version": "==2018.10.15"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"docutils": {
"hashes": [
"sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6",
"sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274",
"sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6"
],
"version": "==0.14"
},
"idna": {
"hashes": [
"sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e",
"sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16"
],
"version": "==2.7"
},
"pillow": {
"hashes": [
"sha256:00203f406818c3f45d47bb8fe7e67d3feddb8dcbbd45a289a1de7dd789226360",
"sha256:0616f800f348664e694dddb0b0c88d26761dd5e9f34e1ed7b7a7d2da14b40cb7",
"sha256:1f7908aab90c92ad85af9d2fec5fc79456a89b3adcc26314d2cde0e238bd789e",
"sha256:2ea3517cd5779843de8a759c2349a3cd8d3893e03ab47053b66d5ec6f8bc4f93",
"sha256:48a9f0538c91fc136b3a576bee0e7cd174773dc9920b310c21dcb5519722e82c",
"sha256:5280ebc42641a1283b7b1f2c20e5b936692198b9dd9995527c18b794850be1a8",
"sha256:5e34e4b5764af65551647f5cc67cf5198c1d05621781d5173b342e5e55bf023b",
"sha256:63b120421ab85cad909792583f83b6ca3584610c2fe70751e23f606a3c2e87f0",
"sha256:696b5e0109fe368d0057f484e2e91717b49a03f1e310f857f133a4acec9f91dd",
"sha256:870ed021a42b1b02b5fe4a739ea735f671a84128c0a666c705db2cb9abd528eb",
"sha256:916da1c19e4012d06a372127d7140dae894806fad67ef44330e5600d77833581",
"sha256:9303a289fa0811e1c6abd9ddebfc770556d7c3311cb2b32eff72164ddc49bc64",
"sha256:9577888ecc0ad7d06c3746afaba339c94d62b59da16f7a5d1cff9e491f23dace",
"sha256:987e1c94a33c93d9b209315bfda9faa54b8edfce6438a1e93ae866ba20de5956",
"sha256:99a3bbdbb844f4fb5d6dd59fac836a40749781c1fa63c563bc216c27aef63f60",
"sha256:99db8dc3097ceafbcff9cb2bff384b974795edeb11d167d391a02c7bfeeb6e16",
"sha256:a5a96cf49eb580756a44ecf12949e52f211e20bffbf5a95760ac14b1e499cd37",
"sha256:aa6ca3eb56704cdc0d876fc6047ffd5ee960caad52452fbee0f99908a141a0ae",
"sha256:aade5e66795c94e4a2b2624affeea8979648d1b0ae3fcee17e74e2c647fc4a8a",
"sha256:b78905860336c1d292409e3df6ad39cc1f1c7f0964e66844bbc2ebfca434d073",
"sha256:b92f521cdc4e4a3041cc343625b699f20b0b5f976793fb45681aac1efda565f8",
"sha256:bfde84bbd6ae5f782206d454b67b7ee8f7f818c29b99fd02bf022fd33bab14cb",
"sha256:c2b62d3df80e694c0e4a0ed47754c9480521e25642251b3ab1dff050a4e60409",
"sha256:c5e2be6c263b64f6f7656e23e18a4a9980cffc671442795682e8c4e4f815dd9f",
"sha256:c99aa3c63104e0818ec566f8ff3942fb7c7a8f35f9912cb63fd8e12318b214b2",
"sha256:dae06620d3978da346375ebf88b9e2dd7d151335ba668c995aea9ed07af7add4",
"sha256:db5499d0710823fa4fb88206050d46544e8f0e0136a9a5f5570b026584c8fd74",
"sha256:f36baafd82119c4a114b9518202f2a983819101dcc14b26e43fc12cbefdce00e",
"sha256:f52b79c8796d81391ab295b04e520bda6feed54d54931708872e8f9ae9db0ea1",
"sha256:ff8cff01582fa1a7e533cb97f628531c4014af4b5f38e33cdcfe5eec29b6d888"
],
"index": "pypi",
"version": "==5.3.0"
},
"pkginfo": {
"hashes": [
"sha256:5878d542a4b3f237e359926384f1dde4e099c9f5525d236b1840cf704fa8d474",
"sha256:a39076cb3eb34c333a0dd390b568e9e1e881c7bf2cc0aee12120636816f55aee"
],
"version": "==1.4.2"
},
"pygments": {
"hashes": [
"sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d",
"sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc"
],
"version": "==2.2.0"
},
"python-magic": {
"hashes": [
"sha256:f2674dcfad52ae6c49d4803fa027809540b130db1dec928cfbb9240316831375",
"sha256:f3765c0f582d2dfc72c15f3b5a82aecfae9498bd29ca840d72f37d7bd38bfcd5"
],
"index": "pypi",
"version": "==0.4.15"
},
"readme-renderer": {
"hashes": [
"sha256:bb16f55b259f27f75f640acf5e00cf897845a8b3e4731b5c1a436e4b8529202f",
"sha256:c8532b79afc0375a85f10433eca157d6b50f7d6990f337fa498c96cd4bfc203d"
],
"version": "==24.0"
},
"requests": {
"hashes": [
"sha256:65b3a120e4329e33c9889db89c80976c5272f56ea92d3e74da8a463992e3ff54",
"sha256:ea881206e59f41dbd0bd445437d792e43906703fff75ca8ff43ccdb11f33f263"
],
"version": "==2.20.1"
},
"requests-toolbelt": {
"hashes": [
"sha256:42c9c170abc2cacb78b8ab23ac957945c7716249206f90874651971a4acff237",
"sha256:f6a531936c6fa4c6cfce1b9c10d5c4f498d16528d2a54a22ca00011205a187b5"
],
"version": "==0.8.0"
},
"six": {
"hashes": [
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
],
"version": "==1.11.0"
},
"tqdm": {
"hashes": [
"sha256:3c4d4a5a41ef162dd61f1edb86b0e1c7859054ab656b2e7c7b77e7fbf6d9f392",
"sha256:5b4d5549984503050883bc126280b386f5f4ca87e6c023c5d015655ad75bdebb"
],
"version": "==4.28.1"
},
"twine": {
"hashes": [
"sha256:7d89bc6acafb31d124e6e5b295ef26ac77030bf098960c2a4c4e058335827c5c",
"sha256:fad6f1251195f7ddd1460cb76d6ea106c93adb4e56c41e0da79658e56e547d2c"
],
"index": "pypi",
"version": "==1.12.1"
},
"urllib3": {
"hashes": [
"sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39",
"sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"
],
"version": "==1.24.1"
},
"webencodings": {
"hashes": [
"sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"
],
"version": "==0.5.1"
}
}
}

View File

@ -1,5 +1,7 @@
# Distribusi CMS
[![PyPI version](https://badge.fury.io/py/distribusi.svg)](https://badge.fury.io/py/distribusi)
`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
@ -13,22 +15,32 @@ dependencies. This package requires two underlying packages. Those are
`python-magic`, and `pillow`. Here are the installation documentation for those
packages:
* https://github.com/threatstack/libmagic
* https://pillow.readthedocs.io/en/5.3.x/installation.html#external-libraries
* [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
### Optional requirements
Using [--user] or [a virtual environment] is recommended:
If you wish to use the `--caption` flag to add image captions read from EXIF comment metadata you will need a utility called `exiftool`.
[--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
You can install it via your package manager. For other options please consult the website: [https://www.sno.phy.queensu.ca/~phil/exiftool/](https://www.sno.phy.queensu.ca/~phil/exiftool/)
## Install It
```bash
$ export PATH=$PATH:$HOME/.local/bin
$ pip install --user distribusi
```
## Usage
## Upgrade It
If you already have it, you can upgrade with:
```bash
$ pip install -U distribusi
```
## Use It
Get help with:
@ -66,21 +78,53 @@ $ distribusi -d /var/www/archive/my_event -t -v
# ✌
## History
Distribusi was first conceptualized as a tool which supported a contribution by Dennis de Bel, Danny van der Kleij and Roel Roscam Abbing to the [ruru house](http://ruruhuis.nl/) organized by [Reinaart Vanhoe](http://vanhoe.org/) and the [ruangrupa](http://ruru.ruangrupa.org/) collective during 2016 Sonsbeek Biennale in Arnhem. During the biennale time the ruru house was a lively meeting place with a programme of discussions, workshops, lectures, culinary activities, performances, pop-up markets and even karaoke evenings, where curators and Arnhemmers met.
The contribution consisted of setting up distribusi.ruruhuis.nl (distribusi is bahasa Indonesian for 'distribution') which was a website connected to a server in the space. Rather than a hidden administrative interface, the server was present and visible and an invitation was extended to visitors to use it to publish material online. This was done by inserting a USB-drive into any of the ports. The distribusi script would then turn the contents of that stick it into a website. Once the USB-drive was removed that website was no longer on-line. Over time distribusi.ruruhuis.nl hosted photos, books and movies. The website is now off-line but the tool that was used to make it is still used in Varia
## Uses
### 디스트리붓시
[Dianaband](http://www.dianaband.info/) used distribusi for https://fragments1444.ink/.
> "Individuals collecting fragments each have their own folder. When they put a story, picture, audio, or video file inside a folder, each fragment is assigned a serial number, and gets accumulated in the fragments of hospitality website.The fragments connect us. We hope that we can choose the “nature and attitude” of the medium that mediates our connection."
[https://fragments1444.ink/about.html](source)
### Distribusi verse
> An attempt to make distribusi into a web interface that can be operated remotely without any knowlegde of CLI. Trying to somehow combine the ideas of distribusi with the ideas of a [tildeverse](https://tildeverse.org/) or [Tilde club](https://tilde.club), but also be neither of these ideas. This project is made for Autonomous Practices at the WDKA in Rotterdam.
See [`crunk/distribusi-verse`](https://git.vvvvvvaria.org/crunk/distribusi-verse) for more.
### `distribusi-go`
Inspired a re-implementation in Go, see [`decentral1se/distribusi-go`](https://git.vvvvvvaria.org/decentral1se/distribusi-go) for more.
## Change It
Install [Pipenv] and then run:
You'll need to get a copy of the repository and then do an [editable] install:
[Pipenv]: https://pipenv.readthedocs.io/en/latest/install/#installing-pipenv
[editable]: https://setuptools.readthedocs.io/en/latest/setuptools.html#development-mode
```
$ pipenv install --dev
$ pipenv run pip install -e .
$ pipenv run distribusi --help
```bash
$ git clone https://git.vvvvvvaria.org/varia/distribusi.git && cd distribusi
$ python3 -m venv .venv && source .venv/bin/activate
$ pip install -e .
```
You're then ready to make your changes and experiment with them.
## Release It
You'll need a [PyPi](https://pypi.org/) account and to be added as a maintainer.
Please ask around @ Varia for who has PyPi access.
```
$ # ... change the version number in setup.py ... #
$ pip install twine wheel
$ make publish
```

12
build.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/bash
# Ultra wild west single binary compiling
# https://github.com/linkedin/shiv
shiv --output-file=distribusi.pyz \
--site-packages=.venv/lib/python3.7/site-packages/ \
--entry-point=distribusi.cli.cli_entrypoint \
--python="/usr/bin/env python3" \
--compressed \
--compile-pyc \
.

View File

@ -1,59 +1,93 @@
import argparse
import os
from distribusi.distribusi import distribusify
def build_argparser():
parser = argparse.ArgumentParser("""
distbusi 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.
parser = argparse.ArgumentParser(
"""
distribusi is a content management system for the web that produces static
index pages based on folders in the files system. It is inspired by the
automatic index functions featured in several popular web servers.
distribusi works by traversing the file system and directory hierarchy to
automatically list all the files in the directory, detect the file types
and providing them with relevant html classes and tags for easy styling.
""")
parser.add_argument(
'-d',
'--directory',
help="Select which directory to distribute"
'-d', '--directory', help="Select which directory to distribute", default="."
)
parser.add_argument(
'-v',
'--verbose',
help="Print verbose debug output",
action="store_true"
'-s', '--style', help="Select a CSS style sheet to include"
)
parser.add_argument(
'-v', '--verbose', help="Print verbose debug output", action="store_true"
)
parser.add_argument(
'-t',
'--thumbnail',
help="Generate 150x150 thumbnails for images",
action="store_true"
help="Generate 450x450 thumbnails for images",
action="store_true",
)
parser.add_argument(
'-n',
'--no-template',
help="Don't use the template to ouput html",
action="store_true"
help="Don't use the template to output html",
action="store_true",
)
parser.add_argument(
'-nf',
'--no-filenames',
help="Don't add file names to listing",
action="store_true",
)
parser.add_argument(
'-c',
'--captions',
help="Adds image captions based on EXIF metadata, requires 'exiftool'",
action="store_true",
)
parser.add_argument(
'-r',
'--remove-index',
help="Recursively removes all instances of index.html that have been previously made by distribusi",
action="store_true")
parser.add_argument(
'-e',
'--exclude-directory',
help="Exclude one or multiple directories from indexing",
nargs="*",
metavar='DIR')
parser.add_argument(
'-f',
'--force',
help="Force whether distribusi overwrites or removes instances of index.html not generated by distribusi, use at own risk!",
action="store_true")
parser.add_argument(
'--no-hidden',
help="Exclude hidden directories",
action="store_true")
parser.add_argument(
'--menu-with-index',
help="Append index.html to menu items to aid navigation",
action="store_true")
return parser
def cli_entrypoint():
parser = build_argparser()
args = parser.parse_args()
if args.directory:
if args.verbose:
print('Generating directory listing for', args.directory)
if args.thumbnail:
print('Making thumbnails')
directory = args.directory
else:
directory = '.'
distribusify(args, directory)
distribusify(args, args.directory)

View File

@ -1,127 +1,219 @@
import base64
import os
import subprocess
from io import BytesIO
import magic
from distribusi.page_template import html_footer, html_head
from PIL import Image
CODE_TYPES = [
'x-c',
'html'
]
FILE_TYPES = {
'image': '<img class="image" src="{}">',
'pdf': (
'<object data="{}" class="pdf" type="application/pdf">'
'<embed src="{}" type="application/pdf" /></object>'
),
'text': '<a href="{}" class="text">{}</a>',
'video': (
'<video class="video" controls>'
'<source src="{}"></source></video>'
),
'audio': (
'<audio controls class="audio">'
'<source src="{}"></source></audio>'
),
}
from distribusi.page_template import html_footer, html_head
from distribusi.mappings import CODE_TYPES, FILE_TYPES, SUB_TYPES
MIME_TYPE = magic.Magic(mime=True)
def thumbnail(image, name):
size = (450, 450)
im = Image.open(image)
im.thumbnail(size)
output = BytesIO()
im.save(output, format='JPEG')
im_data = output.getvalue()
data_url = base64.b64encode(im_data).decode()
return (
"<a href='{}'><img class='thumbnail' "
"src='data:image/jpg;base64,{}'></a>"
).format(name, data_url)
def caption(image):
try:
process = subprocess.Popen(
['exiftool', '-Comment', image], stdout=subprocess.PIPE)
out, err = process.communicate()
except Exception as e:
print(e)
print('Do you have exiftool installed?')
try:
caption = out.decode("utf-8").split(": ", 1)[1]
except Exception as e:
caption = ''
print(e)
return caption
def div(mime, tag, *values):
id_name = values[0].split('.')[0].replace(' ', '_')
def thumbnail(image, name, args):
try:
size = (450, 450)
im = Image.open(image)
im.thumbnail(size)
if (im.mode == 'RGBA'):
bg = Image.new('RGBA', im.size, (255,255,255))
composite = Image.alpha_composite(bg, im)
im=composite.convert('RGB')
output = BytesIO()
im.save(output, format='JPEG')
im_data = output.getvalue()
data_url = base64.b64encode(im_data).decode()
if args.captions:
cap = caption(image)
else:
cap = name
return (
"<figure><a href='{}'><img class='thumbnail' src='data:image/jpg;base64,{}'></a><figcaption>{}</figcaption></figure>"
).format(name, data_url, cap)
except Exception as e:
print('Thumbnailer:', e)
return "<figure><a href='{}'><img src='{}'></a><figcaption>{}</figcaption></figure>".format(name, name, name)
if 'image' in mime:
html = '<div id="{}">{}<br><span class="filename">{}</span></div>'
elif 'pdf' in mime:
html = '<div id="{}">{}<br><class="filename">{}</span></div>'
def div(args, type_, subtype, tag, name):
id_name = name.split('.')[0].replace(' ', '_')
if args.no_filenames:
filename = ''
else:
html = '<div id="{}">{}</div>'
filename = '<span class="filename">{}</span>'.format(name)
return html.format(id_name, tag, values[0])
if 'image' in type_:
html = '<div id="{}" class="{}">{}</div>'
elif 'pdf' in subtype:
html = '<div id="{}" class="{}">{}' + filename + '</div>'
elif 'dir' in type_ or 'html' in subtype or 'unkown-file' in subtype:
html = '<div id="{}" class="{}">{}</div>'
else:
html = '<div id="{}" class="{}">{}' + filename + '</div>'
return html.format(id_name, subtype, tag)
def check_distribusi_index(args, index):
"""
check whether a index.html file is generated by distribusi
"""
if not args.force:
with open(index, 'r') as f:
if '<meta name="generator" content="distribusi" />' in f.read():
return True
else:
if args.verbose:
print(index, 'not generated by distribusi, skipping')
return False
elif args.force:
return True
def write_index(args,index, html, html_head, html_footer):
with open(index, 'w') as f:
if not args.no_template:
if args.style:
fs = open(args.style, "r")
style = fs.read()
styled_html_head = html_head % style
else:
styled_html_head = html_head % ''
f.write(styled_html_head)
for line in html:
f.write(line + '\n')
if not args.no_template:
f.write(html_footer)
def distribusify(args, directory): # noqa
for root, dirs, files in os.walk(directory):
html = []
if args.verbose:
print('Listing', root)
for name in files:
if args.exclude_directory:
if args.verbose:
print('Adding', name)
if 'index.html' not in name:
full_path = os.path.join(root, name)
mime = MIME_TYPE.from_file(full_path)
mime, format = mime.split('/') # example: plain text
print('Excluding directory:', ", ".join(args.exclude_directory))
dirs[:] = [d for d in dirs if d not in args.exclude_directory]
if args.verbose:
print(mime, format)
if args.no_hidden:
dirs = list(filter(lambda d: not d.startswith('.'), dirs))
files = list(filter(lambda f: not f.startswith('.'), files))
if mime in FILE_TYPES:
# expansion for different kind of textfiles
if mime == 'text':
if name.endswith('.html') or name.endswith('.txt'):
# what types of text files to expand
a = open(full_path).read()
elif format in CODE_TYPES:
# if the plain text is code,
# which types do we wrap in pre-tags?
a = "<pre>"+open(full_path).read()+"</pre>"
else:
a = FILE_TYPES[mime]
dirs.sort()
files.sort()
if mime == 'image' and args.thumbnail:
a = thumbnail(full_path, name)
else:
a = FILE_TYPES[mime]
if not args.remove_index:
html = []
if format in FILE_TYPES:
a = FILE_TYPES[format]
if args.verbose:
print('Generating directory listing for', root)
for name in sorted(files):
if 'index.html' not in name:
full_path = os.path.join(root, name)
mime = MIME_TYPE.from_file(full_path)
# example: MIME plain/text becomes 'type' plain 'subtype' text
type_, subtype = mime.split('/')
caption = name
if mime not in FILE_TYPES and format not in FILE_TYPES:
# catch exceptions not defined in FILE_TYPES before
a = "<a href='{}'>{}</a>"
if args.verbose:
message = 'mime-type not in list, adding as href: \n'
print(message, mime, format, name)
print('Found', name, 'as', mime)
a = a.replace('{}', name)
html.append(div(mime, a, name))
if type_ in FILE_TYPES:
a = FILE_TYPES[type_].format(name, caption)
if root != directory:
html.append('<a href="../">../</a>')
# expansion for different kind of text files
if type_ == 'text':
if name.endswith('.html') or subtype == 'html':
subtype = 'html'
# what types of text files to expand
a = '<section id="{}">{}</section>'.format(name, open(full_path).read())
elif subtype in CODE_TYPES or name.endswith('.txt'):
# if the plain text is code,
# which types do we wrap in pre-tags?
a = "<pre>" + open(full_path).read() + "</pre>"
else:
subtype = subtype+' unkown-file'
a = "<a href='{}'>{}</a>"
# a = FILE_TYPES[type_]
for name in dirs:
a = "<a href='{}' class='dir'>{}/</a>".replace('{}', name)
html.append(div('dir', a, 'folder'))
if type_ == 'image':
if args.thumbnail:
a = thumbnail(full_path, name, args)
if args.no_filenames:
caption = ""
a = FILE_TYPES[type_].format(name, caption)
if args.captions:
caption = caption(full_path)
a = FILE_TYPES[type_].format(name, caption)
with open(os.path.join(root, 'index.html'), 'w') as f:
if not args.no_template:
f.write(html_head)
if subtype in SUB_TYPES:
a = SUB_TYPES[subtype]
for line in html:
f.write(line+'\n')
if type_ not in FILE_TYPES and subtype not in SUB_TYPES:
# catch exceptions not yet defined in FILE_TYPES or SUB_TYPES
a = "<a href='{}'>{}</a>"
if args.verbose:
message = 'not in list of file types, adding as plain href: \n'
print(type_, subtype, message, name)
subtype = subtype + ' unkown-file'
if not args.no_template:
f.write(html_footer)
a = a.replace('{}', name)
html.append(div(args, type_, subtype, a, name))
if root != directory:
if args.menu_with_index:
html.append('<a href="../index.html">../</a>')
else:
html.append('<a href="../">../</a>')
for name in dirs:
if args.menu_with_index:
a = "<a href='{}/index.html'>{}</a>".replace('{}', name)
else:
a = "<a href='{}'>{}/</a>".replace('{}', name)
html.insert(0, div(args, 'dir', 'dir', a, 'folder'))
index = os.path.join(root, 'index.html')
if os.path.exists(index):
if check_distribusi_index(args, index):
write_index(args,index,html, html_head, html_footer)
elif not os.path.exists(index):
write_index(args,index,html, html_head, html_footer)
if args.remove_index:
index = os.path.join(root, 'index.html')
if 'index.html' in files:
try:
if check_distribusi_index(args, index):
if args.verbose:
print('Removing index.html from', root)
os.remove(index)
except Exception as e:
print(e)

14
distribusi/mappings.py Normal file
View File

@ -0,0 +1,14 @@
CODE_TYPES = ['x-c', 'x-shellscript', 'x-python']
FILE_TYPES = {
'image': '<figure><img class="image" src="{}"><figcaption>{}</figcaption></figure>',
'text': '<a href="{}" class="text">{}</a>',
'video': ('<video controls>' '<source src="{}"></video>'),
'audio': ('<audio controls class="audio">' '<source src="{}"></audio>'),
}
SUB_TYPES = {
'pdf': (
'<object data="{}" class="pdf" type="application/pdf">'
'<embed src="{}" type="application/pdf" /></object>')
}

View File

@ -2,15 +2,19 @@ html_head = """
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Generated with distribusi https://git.vvvvvvaria.org/rra/distribusi -->
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<!-- Generated with distribusi https://git.vvvvvvaria.org/varia/distribusi -->
<meta name="generator" content="distribusi" />
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<style>
.image{max-width: 100%;}
.pdf {width:100%;}
div{width: 640px;float:left;padding:1em;}
video {width:640px;}
.dir::before{content:"📁";font-size:18px;}
</style>
.image{max-width: 100%%;}
.pdf object{width:640px;height: 640px;}
.dir::before{content:"📁 ";font-size:18px;}
.filename{display:block;font-family:mono;}
.unkown-file::before{content:"📄 ";font-size:18px;}
div{max-width: 640px;display:inline-block;vertical-align:top;margin:1em;padding:1em;}
video {width:640px;max-height:640px;}
%s
</style>
</head>
<body>
"""

9
pyproject.toml Normal file
View File

@ -0,0 +1,9 @@
[build-system]
requires = [
"setuptools >= 41.0.0",
"wheel",
]
build-backend = "setuptools.build_meta"
[tool.black]
skip-string-normalization = true

View File

@ -1,22 +1,18 @@
from setuptools import find_packages, setup
dependencies = [
# pinned because https://github.com/python-pillow/Pillow/issues/2609
'pillow==4.1',
'python-magic',
]
dependencies = ['pillow >= 6.1.0, < 7.0', 'python-magic >= 0.4.15, < 1.0']
with open('README.md', 'r') as handle:
long_description = handle.read()
setup(
name='distribusi',
version='0.0.3',
url='https://git.vvvvvvaria.org/rra/distribusi',
version='0.0.10',
url='https://git.vvvvvvaria.org/varia/distribusi',
license='GPLv3',
author='rra',
author='Varia',
description=(
'distribusi is a content management system for '
'Distribusi is a content management system for '
'the web that produces static pages based on '
'the file system.'
),
@ -27,13 +23,6 @@ setup(
zip_safe=False,
platforms='any',
install_requires=dependencies,
entry_points={
'console_scripts': [
'distribusi = distribusi.cli:cli_entrypoint',
],
},
classifiers=[
'Programming Language :: Python :: 3',
'Environment :: Console',
],
entry_points={'console_scripts': ['distribusi = distribusi.cli:cli_entrypoint']},
classifiers=['Programming Language :: Python :: 3', 'Environment :: Console'],
)