pixboards 0.2.25__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.
- pixboards-0.2.25/PKG-INFO +92 -0
- pixboards-0.2.25/pixBoards/__init__.py +25 -0
- pixboards-0.2.25/pixBoards/__main__.py +4 -0
- pixboards-0.2.25/pixBoards/arguments.py +66 -0
- pixboards-0.2.25/pixBoards/boardmakers.py +321 -0
- pixboards-0.2.25/pixBoards/classes.py +83 -0
- pixboards-0.2.25/pixBoards/cli.py +112 -0
- pixboards-0.2.25/pixBoards/config_loader.py +64 -0
- pixboards-0.2.25/pixBoards/create.py +265 -0
- pixboards-0.2.25/pixBoards/db.py +57 -0
- pixboards-0.2.25/pixBoards/filemaking.py +42 -0
- pixboards-0.2.25/pixBoards/git.py +84 -0
- pixboards-0.2.25/pixBoards/imgchest.py +303 -0
- pixboards-0.2.25/pixBoards/log_utils.py +29 -0
- pixboards-0.2.25/pixBoards/nest_boards.py +24 -0
- pixboards-0.2.25/pixBoards/templates/__init__.py +5 -0
- pixboards-0.2.25/pixBoards/templates/__init__.py.bak +5 -0
- pixboards-0.2.25/pixBoards/templates/configTemplate.yml +20 -0
- pixboards-0.2.25/pixBoards/templates/favicon.png +0 -0
- pixboards-0.2.25/pixBoards/templates/index_template.html +30 -0
- pixboards-0.2.25/pixBoards/templates/template.css +206 -0
- pixboards-0.2.25/pixBoards/templates/template.html +39 -0
- pixboards-0.2.25/pixBoards/templates/template.js +56 -0
- pixboards-0.2.25/pixBoards.egg-info/PKG-INFO +92 -0
- pixboards-0.2.25/pixBoards.egg-info/SOURCES.txt +37 -0
- pixboards-0.2.25/pixBoards.egg-info/dependency_links.txt +1 -0
- pixboards-0.2.25/pixBoards.egg-info/entry_points.txt +2 -0
- pixboards-0.2.25/pixBoards.egg-info/requires.txt +8 -0
- pixboards-0.2.25/pixBoards.egg-info/top_level.txt +1 -0
- pixboards-0.2.25/pyproject.toml +3 -0
- pixboards-0.2.25/setup.cfg +4 -0
- pixboards-0.2.25/setup.py +47 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pixboards
|
|
3
|
+
Version: 0.2.25
|
|
4
|
+
Description-Content-Type: text/markdown
|
|
5
|
+
|
|
6
|
+
# notice
|
|
7
|
+
|
|
8
|
+
repo finally changed to [https://github.com/flynnsharwood/pixBoards.git](https://github.com/flynnsharwood/pixBoards.git)
|
|
9
|
+
|
|
10
|
+
Gui has not been set up yet. Don't try to run the gui.py
|
|
11
|
+
|
|
12
|
+
title and heading use the board name now. favicon added, can be changed, check the templates folder.
|
|
13
|
+
|
|
14
|
+
~~if you are using the PyPi package, you will need to have a running postgres server, else it won't work.~~ It works now.
|
|
15
|
+
|
|
16
|
+
# pixBoards
|
|
17
|
+
|
|
18
|
+
pixBoards is a python library to help you organise your images and videos into masonry-containers (the pinterest layout)
|
|
19
|
+
|
|
20
|
+
It outputs simple html files with links to the images. Simplicity was the aim here, so don't expect fancy stuff in it.
|
|
21
|
+
|
|
22
|
+
It organises the images into "boards" which can be nested inside other boards. (Just think of them as a folder for now)
|
|
23
|
+
|
|
24
|
+
You can also automatically upload the files to imgchest and use those links in the output html files. You can then simply host these output files and you will have a working webpage. (If you had to use local files, you'd have to host the images too, which would be a pain in) (use --upload for that)
|
|
25
|
+
|
|
26
|
+
you can also use lists of imagelinks to create boards.
|
|
27
|
+
|
|
28
|
+
I also recently added the ability to use links in sidecarfiles to use as upload links instead of uploading the image to imgchest. (if you used a bulk downloader like gallery-dl, you'd be able to easily create such sidecar files every time you download.)
|
|
29
|
+
|
|
30
|
+
### screenshots
|
|
31
|
+
|
|
32
|
+
example of index file
|
|
33
|
+

|
|
34
|
+
|
|
35
|
+
example of image board
|
|
36
|
+

|
|
37
|
+
|
|
38
|
+
## how to use
|
|
39
|
+
|
|
40
|
+
first, rename the config_example.yml to config.yml. or start afresh, your choice.
|
|
41
|
+
|
|
42
|
+
if you wish to upload to imgchest and use those links instead, use `--upload`
|
|
43
|
+
|
|
44
|
+
If it is the first time you are uploading, you will have to install postgresql first if not already installed.
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
### install
|
|
48
|
+
|
|
49
|
+
you can install the pixBoards package from PyPi
|
|
50
|
+
|
|
51
|
+
`pip install pixBoards`
|
|
52
|
+
|
|
53
|
+
or clone the repo.
|
|
54
|
+
|
|
55
|
+
`git clone https://github.com/flynnsharwood/boards_v2.git`
|
|
56
|
+
|
|
57
|
+
### First time upload
|
|
58
|
+
If it is the first time you are uploading, you will have to install postgresql first if not already installed.
|
|
59
|
+
|
|
60
|
+
Also, if you have a LOT of files to upload for the first time, go to the uploader folder. [repo link](https://github.com/flynnsharwood/imgUploader)
|
|
61
|
+
|
|
62
|
+
1. set up the directories list in config.yml
|
|
63
|
+
1. run listFiles.py. it will create a file named "MediaFiles.py"
|
|
64
|
+
2. now run calc_hex.py
|
|
65
|
+
3. run fileListUpload.py
|
|
66
|
+
|
|
67
|
+
fileListUpload basically uploads in batches of 20, unlike the original script where it uploads each image individually. This will upload and cache the links and hashes to postgress db
|
|
68
|
+
|
|
69
|
+
### Usage
|
|
70
|
+
simply use the binary file provided `boards.exe`
|
|
71
|
+
|
|
72
|
+
Or do `python3 cli.py` or `python3 __main__.py`
|
|
73
|
+
|
|
74
|
+
flags you might use
|
|
75
|
+
|
|
76
|
+
`--upload` ensure all files are being uploaded. If you have pictures with the same filenames in different folders, you might need to fix this.
|
|
77
|
+
|
|
78
|
+
`--gitPush` this will push your created html files to a github repo. set up your repo to trigger a gh_pages deployment every push.
|
|
79
|
+
|
|
80
|
+
`--config` change the config file being used
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
here is a link to an example board - [https://flynnsharwood.github.io/exampleBoard/index.html](https://flynnsharwood.github.io/exampleBoard/index.html)
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
## features
|
|
87
|
+
|
|
88
|
+
added an button to load a random page in 0.2.18
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
# this gets the latest git commit hash. i will not be setting the version in the setup.py.
|
|
5
|
+
# def get_git_version():
|
|
6
|
+
# try:
|
|
7
|
+
# commit_hash = (
|
|
8
|
+
# subprocess.check_output(["git", "rev-parse", "--short", "HEAD"])
|
|
9
|
+
# .decode()
|
|
10
|
+
# .strip()
|
|
11
|
+
# )
|
|
12
|
+
# return commit_hash
|
|
13
|
+
# except Exception:
|
|
14
|
+
# return "untracked"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# __version__ = get_git_version()
|
|
18
|
+
__version__ = "0.2.25"
|
|
19
|
+
|
|
20
|
+
templates_folder_path = os.path.join(
|
|
21
|
+
os.path.dirname(os.path.abspath(__file__)), "templates"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
with open(os.path.join(templates_folder_path, "configTemplate.yml"), "r") as f:
|
|
25
|
+
configTemplate = f.read()
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
# Parse arguments
|
|
4
|
+
parser = argparse.ArgumentParser(description="Generate HTML for media directories.")
|
|
5
|
+
parser.add_argument(
|
|
6
|
+
"--random",
|
|
7
|
+
type=int,
|
|
8
|
+
help="number of images to sample from all boards. "
|
|
9
|
+
"use a number higher than the number of total images, or a negative number,"
|
|
10
|
+
" to shuffle instead",
|
|
11
|
+
)
|
|
12
|
+
parser.add_argument(
|
|
13
|
+
"--recent",
|
|
14
|
+
"--desc",
|
|
15
|
+
type=int,
|
|
16
|
+
help="sorts filename by decending and truncates to input count.",
|
|
17
|
+
)
|
|
18
|
+
parser.add_argument(
|
|
19
|
+
"--ranDir",
|
|
20
|
+
type=str,
|
|
21
|
+
help="Directory to search images in for --random",
|
|
22
|
+
)
|
|
23
|
+
parser.add_argument("--dir", type=str, help="Directory to use for the images")
|
|
24
|
+
# parser.add_argument("--csvs", nargs="+", help="List of CSV files to use")
|
|
25
|
+
parser.add_argument(
|
|
26
|
+
"--useLists", action="store_true", help="Use list files from config"
|
|
27
|
+
)
|
|
28
|
+
parser.add_argument("--imageLists", nargs="+", help="List of imagelist files to use.")
|
|
29
|
+
parser.add_argument("--col", type=int, help="Number of columns to default to")
|
|
30
|
+
parser.add_argument("--margin", type=int, help="Margin in px")
|
|
31
|
+
|
|
32
|
+
parser.add_argument(
|
|
33
|
+
"--sidecar", action="store_true", default=False, help="use links from sidecar files"
|
|
34
|
+
)
|
|
35
|
+
parser.add_argument(
|
|
36
|
+
"--includeLocal",
|
|
37
|
+
action="store_true",
|
|
38
|
+
default=False,
|
|
39
|
+
help="include local files if using lists",
|
|
40
|
+
)
|
|
41
|
+
parser.add_argument(
|
|
42
|
+
"--makeConfig",
|
|
43
|
+
action="store_true",
|
|
44
|
+
default=False,
|
|
45
|
+
help="include local files if using lists",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# parser.add_argument("--rancount", type=int, help="number of images to sample from all boards. use a number higher than the number of total images to shuffle instead")
|
|
49
|
+
parser.add_argument(
|
|
50
|
+
"--upload", action="store_true", default=False, help="Upload images to Imgchest"
|
|
51
|
+
)
|
|
52
|
+
parser.add_argument("--config", type=str, default=False, help="config file to use")
|
|
53
|
+
parser.add_argument(
|
|
54
|
+
"--saveBoards", action="store_true", help="Save generated boards to PostgreSQL"
|
|
55
|
+
)
|
|
56
|
+
parser.add_argument(
|
|
57
|
+
"--gitPush", action="store_true", help="Push outputDir to existing Git repo"
|
|
58
|
+
)
|
|
59
|
+
parser.add_argument("--useSaved", action="store_true", help="use only saved images")
|
|
60
|
+
parser.add_argument(
|
|
61
|
+
"--reddit",
|
|
62
|
+
action="store_true",
|
|
63
|
+
help="this is helpful for --desc when using reddit images. (the id in the front of filename is converted to int using base 36)",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
args = parser.parse_args()
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import random
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import psycopg2
|
|
6
|
+
|
|
7
|
+
from pixBoards.arguments import args
|
|
8
|
+
from pixBoards.classes import board
|
|
9
|
+
from pixBoards.imgchest import append_sidecar_links, process_images
|
|
10
|
+
|
|
11
|
+
# append_sidecar_links not working properly rn. a future me problem.
|
|
12
|
+
from pixBoards.log_utils import setup_logger
|
|
13
|
+
|
|
14
|
+
from pixBoards.config_loader import config, outputDir
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# import yaml
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
logger = setup_logger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# from pixBoards.config_loader import config
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def boardsForImglist(imgList_List, listDir, paginate):
|
|
27
|
+
# Now I might need to sanitise the ( image list )list
|
|
28
|
+
# so that there aren't instances with the same name.
|
|
29
|
+
# But as the imagelist files are in the same folder,
|
|
30
|
+
# they won't have the same name, so I leave this for the future me.
|
|
31
|
+
os.makedirs(listDir, exist_ok=True)
|
|
32
|
+
|
|
33
|
+
boards = []
|
|
34
|
+
|
|
35
|
+
for idx, imgListFile in enumerate(imgList_List):
|
|
36
|
+
boardName = os.path.splitext(os.path.basename(imgListFile))[0]
|
|
37
|
+
with open(imgListFile, "r", encoding="utf-8") as f:
|
|
38
|
+
images = [line.strip() for line in f if line.strip()]
|
|
39
|
+
|
|
40
|
+
outputFile = listDir
|
|
41
|
+
logger.info(f"output file = {outputFile}")
|
|
42
|
+
|
|
43
|
+
b = board(
|
|
44
|
+
name=boardName,
|
|
45
|
+
output_file_loc=outputFile,
|
|
46
|
+
image_paths=images,
|
|
47
|
+
paginate=paginate,
|
|
48
|
+
# images_per_page=config["page_size"] if paginate else 10000,
|
|
49
|
+
img_list_status=True,
|
|
50
|
+
)
|
|
51
|
+
b.paginate_board()
|
|
52
|
+
boards.append(b)
|
|
53
|
+
|
|
54
|
+
return boards
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def standardBoards(directories, outputDir, paginate, upload):
|
|
58
|
+
boards = []
|
|
59
|
+
# outputDir = Path(outputDir)
|
|
60
|
+
# outputDir.mkdir(parents=True, exist_ok=True)
|
|
61
|
+
|
|
62
|
+
media_extensions = (
|
|
63
|
+
".jpg",
|
|
64
|
+
".jpeg",
|
|
65
|
+
".png",
|
|
66
|
+
".gif",
|
|
67
|
+
".bmp",
|
|
68
|
+
".webp",
|
|
69
|
+
# ".heic", # i will possibly add support to convert these to normal imgs
|
|
70
|
+
# before using them.
|
|
71
|
+
".mp4",
|
|
72
|
+
".avi",
|
|
73
|
+
".webm",
|
|
74
|
+
".mov",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
for d in directories:
|
|
78
|
+
# normalize to a Path
|
|
79
|
+
src_dir = Path(d)
|
|
80
|
+
|
|
81
|
+
if not src_dir.exists():
|
|
82
|
+
logger.warning(f"Skipping non-existent directory: {src_dir}")
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
for root, dirs, files in os.walk(src_dir):
|
|
86
|
+
image_paths = []
|
|
87
|
+
|
|
88
|
+
for fname in sorted(files):
|
|
89
|
+
if fname.lower().endswith(media_extensions):
|
|
90
|
+
abs_path = Path(root) / fname
|
|
91
|
+
image_paths.append(abs_path.resolve().as_uri())
|
|
92
|
+
|
|
93
|
+
logger.debug(f"Processing {root} with {len(image_paths)} images.")
|
|
94
|
+
|
|
95
|
+
rel = Path(root).relative_to(os.path.dirname(src_dir))
|
|
96
|
+
|
|
97
|
+
board_name = str(rel).replace(os.sep, "_~")
|
|
98
|
+
output_path = outputDir # everything writes into this one folder
|
|
99
|
+
# collect local files
|
|
100
|
+
local_files = [
|
|
101
|
+
Path(root) / f
|
|
102
|
+
for f in sorted(files)
|
|
103
|
+
if f.lower().endswith(media_extensions)
|
|
104
|
+
]
|
|
105
|
+
if not local_files:
|
|
106
|
+
logger.debug(f"No media in {root}, creating empty board.")
|
|
107
|
+
b = board(
|
|
108
|
+
name=board_name,
|
|
109
|
+
output_file_loc=str(outputDir),
|
|
110
|
+
image_paths=[],
|
|
111
|
+
paginate=paginate,
|
|
112
|
+
upload=upload,
|
|
113
|
+
dummy_status=True,
|
|
114
|
+
)
|
|
115
|
+
else:
|
|
116
|
+
# create a Board object and paginate it
|
|
117
|
+
b = board(
|
|
118
|
+
name=board_name,
|
|
119
|
+
output_file_loc=str(output_path),
|
|
120
|
+
image_paths=image_paths,
|
|
121
|
+
paginate=paginate,
|
|
122
|
+
upload=upload,
|
|
123
|
+
dummy_status=False,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
b.paginate_board()
|
|
127
|
+
boards.append(b)
|
|
128
|
+
|
|
129
|
+
logger.debug(f"Board created: {board_name} ({len(image_paths)} images)")
|
|
130
|
+
|
|
131
|
+
return boards
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def uploadBoards(directories, outputDir, paginate, upload=True):
|
|
135
|
+
def connect_db():
|
|
136
|
+
return psycopg2.connect(
|
|
137
|
+
dbname=config["dbname"],
|
|
138
|
+
user=config["user"],
|
|
139
|
+
password=config["password"],
|
|
140
|
+
host=config["host"],
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
conn = connect_db()
|
|
144
|
+
boards = []
|
|
145
|
+
outputDir = Path(outputDir)
|
|
146
|
+
outputDir.mkdir(parents=True, exist_ok=True)
|
|
147
|
+
|
|
148
|
+
media_extensions = (
|
|
149
|
+
".jpg",
|
|
150
|
+
".jpeg",
|
|
151
|
+
".png",
|
|
152
|
+
".gif",
|
|
153
|
+
".bmp",
|
|
154
|
+
".webp",
|
|
155
|
+
".mp4",
|
|
156
|
+
".avi",
|
|
157
|
+
".webm",
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
for d in directories:
|
|
161
|
+
src_dir = Path(d)
|
|
162
|
+
if not src_dir.exists():
|
|
163
|
+
logger.warning(f"Skipping non-existent directory: {src_dir}")
|
|
164
|
+
continue
|
|
165
|
+
|
|
166
|
+
for root, _, files in os.walk(src_dir):
|
|
167
|
+
rel = Path(root).relative_to(os.path.dirname(src_dir))
|
|
168
|
+
board_name = (
|
|
169
|
+
src_dir.name if rel == Path(".") else str(rel).replace(os.sep, "_~")
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
local_files = [
|
|
173
|
+
Path(root) / f
|
|
174
|
+
for f in sorted(files)
|
|
175
|
+
if f.lower().endswith(media_extensions)
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
if not local_files:
|
|
179
|
+
logger.debug(f"No media in {root}, creating empty board.")
|
|
180
|
+
boards.append(
|
|
181
|
+
board(
|
|
182
|
+
name=board_name,
|
|
183
|
+
output_file_loc=str(outputDir),
|
|
184
|
+
image_paths=[],
|
|
185
|
+
paginate=paginate,
|
|
186
|
+
upload=upload,
|
|
187
|
+
dummy_status=True,
|
|
188
|
+
)
|
|
189
|
+
)
|
|
190
|
+
continue
|
|
191
|
+
|
|
192
|
+
logger.debug(f"Uploading {len(local_files)} images from {root}…")
|
|
193
|
+
try:
|
|
194
|
+
# local_files = append_sidecar_links(local_files, conn) # fix this function.
|
|
195
|
+
# this function has problems in the images not being counted in the index maker or the randomiser.
|
|
196
|
+
http_links, hash_map = process_images(local_files, conn)
|
|
197
|
+
img_filenames = [f.name for f in local_files]
|
|
198
|
+
except Exception as e:
|
|
199
|
+
logger.error(f"Failed to upload images in {root}: {e}")
|
|
200
|
+
continue
|
|
201
|
+
|
|
202
|
+
b = board(
|
|
203
|
+
name=board_name,
|
|
204
|
+
output_file_loc=str(outputDir),
|
|
205
|
+
image_paths=http_links,
|
|
206
|
+
img_filenames=img_filenames,
|
|
207
|
+
paginate=paginate,
|
|
208
|
+
# images_per_page=(config["page_size"] if paginate else 10000),
|
|
209
|
+
upload=upload,
|
|
210
|
+
# no_of_imgs=len(http_links),
|
|
211
|
+
# outputDir=outputDir
|
|
212
|
+
)
|
|
213
|
+
b.link_hash_map = hash_map
|
|
214
|
+
b.paginate_board()
|
|
215
|
+
boards.append(b)
|
|
216
|
+
logger.debug(
|
|
217
|
+
f"Uploaded board created: {board_name} ({len(http_links)} images)"
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
conn.close()
|
|
221
|
+
return boards
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def randomBoard(boards, count, outputDir, paginate, upload):
|
|
225
|
+
images = []
|
|
226
|
+
for b in boards:
|
|
227
|
+
images.extend(b.image_paths)
|
|
228
|
+
|
|
229
|
+
try:
|
|
230
|
+
ran_images = random.sample(images, count)
|
|
231
|
+
except:
|
|
232
|
+
random.shuffle(images)
|
|
233
|
+
ran_images = images
|
|
234
|
+
|
|
235
|
+
images = list({os.path.basename(p): p for p in images}.values())
|
|
236
|
+
|
|
237
|
+
ranBoard = board(
|
|
238
|
+
name="randomised_set",
|
|
239
|
+
output_file_loc=outputDir,
|
|
240
|
+
image_paths=ran_images,
|
|
241
|
+
paginate=paginate,
|
|
242
|
+
upload=upload,
|
|
243
|
+
dummy_status=False,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
ranBoard.paginate_board()
|
|
247
|
+
|
|
248
|
+
logger.info(f"there were {len(images)} imgs")
|
|
249
|
+
|
|
250
|
+
return ranBoard
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
import re
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def extract_reddit_id_as_int(path: str) -> int:
|
|
257
|
+
filename = os.path.basename(path)
|
|
258
|
+
# Split on space or %20
|
|
259
|
+
reddit_id = re.split(r"(?:\s|%20)", filename, maxsplit=1)[0]
|
|
260
|
+
return reddit_id
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def descBoard(boards, count, outputDir, paginate, upload):
|
|
264
|
+
images = []
|
|
265
|
+
img_filenames = []
|
|
266
|
+
|
|
267
|
+
if upload:
|
|
268
|
+
for b in boards:
|
|
269
|
+
images.extend(b.image_paths)
|
|
270
|
+
img_filenames.extend(b.img_filenames)
|
|
271
|
+
|
|
272
|
+
# map filenames → paths
|
|
273
|
+
paired = list(zip(img_filenames, images))
|
|
274
|
+
|
|
275
|
+
# sort by filename (descending)
|
|
276
|
+
paired.sort(key=lambda x: x[0], reverse=True)
|
|
277
|
+
|
|
278
|
+
# deduplicate by filename
|
|
279
|
+
seen = {}
|
|
280
|
+
for fname, path in paired:
|
|
281
|
+
if fname not in seen:
|
|
282
|
+
seen[fname] = path
|
|
283
|
+
images = list(seen.values())
|
|
284
|
+
|
|
285
|
+
else:
|
|
286
|
+
for b in boards:
|
|
287
|
+
images.extend(b.image_paths)
|
|
288
|
+
|
|
289
|
+
if args.reddit:
|
|
290
|
+
images.sort(key=lambda x: extract_reddit_id_as_int(x), reverse=True)
|
|
291
|
+
else:
|
|
292
|
+
images.sort(key=lambda x: os.path.basename(x), reverse=True)
|
|
293
|
+
|
|
294
|
+
# deduplicate by basename
|
|
295
|
+
images = list({os.path.basename(p): p for p in images}.values())
|
|
296
|
+
|
|
297
|
+
# exclude top 10
|
|
298
|
+
top = 10
|
|
299
|
+
if count > 0:
|
|
300
|
+
images = images[top : count + top]
|
|
301
|
+
else:
|
|
302
|
+
images = images[top:]
|
|
303
|
+
|
|
304
|
+
desc_Board = board(
|
|
305
|
+
name="recent imgs",
|
|
306
|
+
output_file_loc=outputDir,
|
|
307
|
+
image_paths=images,
|
|
308
|
+
paginate=paginate,
|
|
309
|
+
upload=upload,
|
|
310
|
+
dummy_status=False,
|
|
311
|
+
)
|
|
312
|
+
desc_Board.paginate_board()
|
|
313
|
+
|
|
314
|
+
with open("desc_images.log", "w") as f:
|
|
315
|
+
for img in images:
|
|
316
|
+
f.write(f"{img}\n")
|
|
317
|
+
with open("desc_images_path.log", "w") as f:
|
|
318
|
+
for img in images:
|
|
319
|
+
f.write(f"{os.path.basename(img)}\n")
|
|
320
|
+
|
|
321
|
+
return desc_Board
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from datetime import date
|
|
3
|
+
|
|
4
|
+
from pixBoards.config_loader import config
|
|
5
|
+
from pixBoards.log_utils import setup_logger
|
|
6
|
+
|
|
7
|
+
# set up logger
|
|
8
|
+
today = date.today()
|
|
9
|
+
logger = setup_logger(__name__)
|
|
10
|
+
|
|
11
|
+
# masterDir = config["masterDir"]
|
|
12
|
+
|
|
13
|
+
padding = config["padding"]
|
|
14
|
+
imgs_per_page = config["page_size"]
|
|
15
|
+
# print(f'imgs per page are {imgs_per_page}' )
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class page:
|
|
19
|
+
def __init__(self, page_number, total_pages, images, file_location, bname):
|
|
20
|
+
self.page_number = page_number # Current page number
|
|
21
|
+
self.images = images # image list for the page
|
|
22
|
+
self.total_pages = total_pages
|
|
23
|
+
self.file_location = file_location
|
|
24
|
+
self.bname = bname
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
from math import ceil
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class board:
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
name,
|
|
34
|
+
output_file_loc,
|
|
35
|
+
image_paths,
|
|
36
|
+
# no_of_imgs,
|
|
37
|
+
img_filenames=[],
|
|
38
|
+
paginate=True,
|
|
39
|
+
upload=False,
|
|
40
|
+
dummy_status=False,
|
|
41
|
+
img_list_status=False,
|
|
42
|
+
):
|
|
43
|
+
self.name = name
|
|
44
|
+
self.image_paths = image_paths
|
|
45
|
+
self.img_filenames = img_filenames
|
|
46
|
+
self.pages = [] # will be storing a list of instances of class, page.
|
|
47
|
+
self.imgs_per_page = imgs_per_page
|
|
48
|
+
self.output_file_loc = output_file_loc
|
|
49
|
+
self.upload_status = upload
|
|
50
|
+
self.paginate_status = paginate
|
|
51
|
+
self.link_hash_map = {} if self.upload_status else None
|
|
52
|
+
self.no_of_imgs = len(image_paths)
|
|
53
|
+
self.nested_boards = []
|
|
54
|
+
self.dummy_status = dummy_status
|
|
55
|
+
self.img_list_status = img_list_status
|
|
56
|
+
parts = self.name.split("_~")
|
|
57
|
+
self.clean_name = parts[-1]
|
|
58
|
+
self.parent = "_~".join(parts[:-1])
|
|
59
|
+
|
|
60
|
+
def paginate_board(self):
|
|
61
|
+
total_images = len(self.image_paths)
|
|
62
|
+
# logger.info(f'total images = {total_images}')
|
|
63
|
+
total_pages = ceil(total_images / self.imgs_per_page)
|
|
64
|
+
output_base = self.output_file_loc
|
|
65
|
+
for i in range(total_pages):
|
|
66
|
+
|
|
67
|
+
start = i * self.imgs_per_page
|
|
68
|
+
end = start + self.imgs_per_page
|
|
69
|
+
page_images = self.image_paths[start:end]
|
|
70
|
+
file_loc = (
|
|
71
|
+
os.path.join(output_base, self.name) + f"_{(i+1):0{padding}}.html"
|
|
72
|
+
)
|
|
73
|
+
Page = page(
|
|
74
|
+
page_number=i + 1,
|
|
75
|
+
total_pages=total_pages,
|
|
76
|
+
images=page_images,
|
|
77
|
+
file_location=file_loc,
|
|
78
|
+
bname=self,
|
|
79
|
+
)
|
|
80
|
+
self.pages.append(Page)
|
|
81
|
+
logger.debug(
|
|
82
|
+
f"Finished with - Board: {self.name}, page {i + 1} of {total_pages}"
|
|
83
|
+
)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from datetime import date
|
|
3
|
+
|
|
4
|
+
from pixBoards.arguments import args
|
|
5
|
+
from pixBoards.boardmakers import (
|
|
6
|
+
boardsForImglist,
|
|
7
|
+
randomBoard,
|
|
8
|
+
descBoard,
|
|
9
|
+
standardBoards,
|
|
10
|
+
uploadBoards,
|
|
11
|
+
)
|
|
12
|
+
from pixBoards.log_utils import setup_logger
|
|
13
|
+
|
|
14
|
+
logger = setup_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
from pixBoards.config_loader import config, outputDir
|
|
18
|
+
from pixBoards.db import create_boards_table, create_conn
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def main():
|
|
22
|
+
|
|
23
|
+
start_time = time.time()
|
|
24
|
+
today = date.today()
|
|
25
|
+
logger.info(f"Today is {today}, Starting ...")
|
|
26
|
+
|
|
27
|
+
conn = None
|
|
28
|
+
if args.upload or args.saveBoards:
|
|
29
|
+
conn = create_conn()
|
|
30
|
+
|
|
31
|
+
if args.saveBoards:
|
|
32
|
+
create_boards_table(conn)
|
|
33
|
+
|
|
34
|
+
paginate = config.get("paginate", True) is True
|
|
35
|
+
boards = []
|
|
36
|
+
|
|
37
|
+
# Handle imagelist mode
|
|
38
|
+
if args.useLists or args.imageLists:
|
|
39
|
+
usingLists = True
|
|
40
|
+
imgList_List = (
|
|
41
|
+
args.imageLists if args.imageLists else config.get("imageLists", [])
|
|
42
|
+
)
|
|
43
|
+
boards.extend(boardsForImglist(imgList_List, outputDir, paginate))
|
|
44
|
+
|
|
45
|
+
if args.includeLocal:
|
|
46
|
+
usingLists = False
|
|
47
|
+
else:
|
|
48
|
+
usingLists = False
|
|
49
|
+
|
|
50
|
+
# Handle upload
|
|
51
|
+
if args.upload:
|
|
52
|
+
logger.info("Upload case")
|
|
53
|
+
upload = True
|
|
54
|
+
else:
|
|
55
|
+
upload = False
|
|
56
|
+
|
|
57
|
+
if args.dir:
|
|
58
|
+
directories = [args.dir]
|
|
59
|
+
logger.debug("Using --dir → %s", directories)
|
|
60
|
+
elif config.get("directories"):
|
|
61
|
+
directories = config["directories"]
|
|
62
|
+
logger.debug(f"Using config.directories → %s", directories)
|
|
63
|
+
else:
|
|
64
|
+
directories = []
|
|
65
|
+
|
|
66
|
+
# board generation standar case
|
|
67
|
+
if directories and not usingLists:
|
|
68
|
+
if upload:
|
|
69
|
+
boards.extend(uploadBoards(directories, outputDir, paginate, upload=True))
|
|
70
|
+
else:
|
|
71
|
+
boards.extend(
|
|
72
|
+
standardBoards(directories, outputDir, paginate, upload=False)
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# for random case
|
|
76
|
+
if args.random:
|
|
77
|
+
rancount = args.random
|
|
78
|
+
boards.append(randomBoard(boards, rancount, outputDir, paginate, upload))
|
|
79
|
+
|
|
80
|
+
if args.recent:
|
|
81
|
+
desc_count = args.recent
|
|
82
|
+
boards.append(descBoard(boards, desc_count, outputDir, paginate, upload))
|
|
83
|
+
|
|
84
|
+
from pixBoards.nest_boards import assign_nested_boards
|
|
85
|
+
|
|
86
|
+
root_boards = assign_nested_boards(boards)
|
|
87
|
+
logger.debug(root_boards)
|
|
88
|
+
|
|
89
|
+
# Group boards by output directory and create output
|
|
90
|
+
logger.info(f"Total boards to generate HTML for: {len(boards)}")
|
|
91
|
+
from pixBoards.filemaking import create_output_files
|
|
92
|
+
|
|
93
|
+
create_output_files(root_boards, boards, conn)
|
|
94
|
+
|
|
95
|
+
# Print nested board tree
|
|
96
|
+
def print_board_tree(boards, depth=0):
|
|
97
|
+
for b in boards:
|
|
98
|
+
print(" " * depth + f"- {b.clean_name}")
|
|
99
|
+
print_board_tree(b.nested_boards, depth + 1)
|
|
100
|
+
|
|
101
|
+
print("Boards structure - ")
|
|
102
|
+
print_board_tree(root_boards)
|
|
103
|
+
|
|
104
|
+
logger.debug(root_boards)
|
|
105
|
+
print(f"browse boards at - {outputDir}")
|
|
106
|
+
|
|
107
|
+
elapsed_time = time.time() - start_time
|
|
108
|
+
logger.info(f"Finished in {elapsed_time:.2f} seconds.")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
if __name__ == "__main__":
|
|
112
|
+
main()
|