reef-beet-plugin 1.0.0b1__py3-none-any.whl
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.
- reef/__init__.py +7 -0
- reef/gslides.py +0 -0
- reef/options.py +11 -0
- reef/pdf.py +191 -0
- reef/plugin.py +38 -0
- reef_beet_plugin-1.0.0b1.dist-info/METADATA +19 -0
- reef_beet_plugin-1.0.0b1.dist-info/RECORD +9 -0
- reef_beet_plugin-1.0.0b1.dist-info/WHEEL +4 -0
- reef_beet_plugin-1.0.0b1.dist-info/licenses/LICENSE +21 -0
reef/__init__.py
ADDED
reef/gslides.py
ADDED
|
File without changes
|
reef/options.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from beet import PluginOptions
|
|
2
|
+
from pydantic import ConfigDict
|
|
3
|
+
|
|
4
|
+
class ReefPluginOptions(PluginOptions):
|
|
5
|
+
model_config = ConfigDict(extra="ignore")
|
|
6
|
+
|
|
7
|
+
tint: int | None = None
|
|
8
|
+
cache_timeout_hours: int = 24
|
|
9
|
+
|
|
10
|
+
class PdfPluginOptions(PluginOptions):
|
|
11
|
+
poppler_path: str | None = None
|
reef/pdf.py
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import pdf2image
|
|
2
|
+
from PIL import Image
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import ClassVar, List, Any
|
|
5
|
+
from beet import (
|
|
6
|
+
Context,
|
|
7
|
+
configurable,
|
|
8
|
+
PluginOptions,
|
|
9
|
+
File,
|
|
10
|
+
NamespaceFileScope,
|
|
11
|
+
Drop,
|
|
12
|
+
ResourcePack,
|
|
13
|
+
Model,
|
|
14
|
+
ItemModel,
|
|
15
|
+
Texture
|
|
16
|
+
)
|
|
17
|
+
from .options import ReefPluginOptions, PdfPluginOptions
|
|
18
|
+
from tempfile import TemporaryDirectory
|
|
19
|
+
import shutil
|
|
20
|
+
import logging
|
|
21
|
+
|
|
22
|
+
__all__ = ["pdf"]
|
|
23
|
+
|
|
24
|
+
PDF_NAMESPACE = "reef/pdf"
|
|
25
|
+
logger = logging.getLogger(PDF_NAMESPACE)
|
|
26
|
+
|
|
27
|
+
# TODO: add poppler path detection
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def create_reef_pdf_namespace(ctx: Context):
|
|
31
|
+
class ReefPdf(File):
|
|
32
|
+
"""Class representing a Reef PDF file."""
|
|
33
|
+
|
|
34
|
+
scope: ClassVar[NamespaceFileScope] = ("reef",)
|
|
35
|
+
extension: ClassVar[str] = ".pdf"
|
|
36
|
+
|
|
37
|
+
reef_opts = ctx.validate("reef", ReefPluginOptions)
|
|
38
|
+
pdf_opts = ctx.validate("reef.pdf", PdfPluginOptions)
|
|
39
|
+
|
|
40
|
+
def bind(self, pack: ResourcePack, path: str):
|
|
41
|
+
super().bind(pack, path)
|
|
42
|
+
|
|
43
|
+
# Variables -----------
|
|
44
|
+
|
|
45
|
+
namespace, _, path = path.partition(":")
|
|
46
|
+
pdf_path = Path(self.ensure_source_path())
|
|
47
|
+
poppler_path: dict[str, Any] = {"poppler_path": self.pdf_opts.poppler_path} if self.pdf_opts.poppler_path is not None else {}
|
|
48
|
+
|
|
49
|
+
# ---------------------
|
|
50
|
+
|
|
51
|
+
# Start to build the file
|
|
52
|
+
logger.debug(f"Building: {pdf_path}")
|
|
53
|
+
|
|
54
|
+
# Cache the original PDF
|
|
55
|
+
ctx.cache[PDF_NAMESPACE].download(pdf_path.as_uri())
|
|
56
|
+
logger.debug("Cached pdf %s (%s)", f"{namespace}:{path}", pdf_path)
|
|
57
|
+
|
|
58
|
+
# Cache the images if we didn't hit the cache
|
|
59
|
+
if ctx.cache[PDF_NAMESPACE].has_changed(pdf_path):
|
|
60
|
+
logger.debug("Recaching image files...")
|
|
61
|
+
with TemporaryDirectory() as temp_dir:
|
|
62
|
+
# Convert the PDF to a list of Images
|
|
63
|
+
|
|
64
|
+
uncached_images: List[str] = pdf2image.convert_from_path(
|
|
65
|
+
pdf_path=pdf_path,
|
|
66
|
+
fmt="png",
|
|
67
|
+
output_folder=temp_dir,
|
|
68
|
+
thread_count=4,
|
|
69
|
+
paths_only=True,
|
|
70
|
+
**poppler_path
|
|
71
|
+
) # type: ignore
|
|
72
|
+
|
|
73
|
+
# Cache the images
|
|
74
|
+
with ctx.cache[PDF_NAMESPACE] as cache:
|
|
75
|
+
cache.timeout(hours=self.reef_opts.cache_timeout_hours)
|
|
76
|
+
|
|
77
|
+
for i in range(0, len(uncached_images)):
|
|
78
|
+
image_path = uncached_images[i]
|
|
79
|
+
|
|
80
|
+
cache_path = cache.get_path(f"{namespace}:{path}/{i}.png")
|
|
81
|
+
|
|
82
|
+
shutil.copyfile(image_path, cache_path)
|
|
83
|
+
|
|
84
|
+
logger.debug("Cached %s (%s)", f"{namespace}:{path}/{i}.png", cache_path)
|
|
85
|
+
|
|
86
|
+
logger.debug("Done caching!")
|
|
87
|
+
|
|
88
|
+
# Get PDF debug
|
|
89
|
+
# NOTE: oh my god why is pdf2image typed so horribly
|
|
90
|
+
pdf_info = pdf2image.pdfinfo_from_path(
|
|
91
|
+
pdf_path=str(pdf_path),
|
|
92
|
+
**poppler_path
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Put the cached images into a list
|
|
96
|
+
images: List[Image.Image] = []
|
|
97
|
+
|
|
98
|
+
with ctx.cache[PDF_NAMESPACE] as cache:
|
|
99
|
+
logger.debug("Loading images from cache...")
|
|
100
|
+
for i in range(0, pdf_info["Pages"]):
|
|
101
|
+
cache_path = cache.get_path(f"{namespace}:{path}/{i}.png")
|
|
102
|
+
|
|
103
|
+
with Image.open(cache_path) as img:
|
|
104
|
+
images.append(img.copy())
|
|
105
|
+
logger.debug("Copied %s (%s)", f"{namespace}:{path}/{i}.png", cache_path)
|
|
106
|
+
|
|
107
|
+
# Generate the resource pack assets
|
|
108
|
+
self.generate_assets(pack, namespace, path, images)
|
|
109
|
+
|
|
110
|
+
# Prevent the PDF itself from getting put into the resource pack
|
|
111
|
+
raise Drop()
|
|
112
|
+
|
|
113
|
+
def generate_assets(
|
|
114
|
+
self,
|
|
115
|
+
pack: ResourcePack,
|
|
116
|
+
namespace: str,
|
|
117
|
+
path: str,
|
|
118
|
+
images: List[Image.Image]
|
|
119
|
+
) -> None:
|
|
120
|
+
resource_location_path = f"reef/mini/{path}"
|
|
121
|
+
|
|
122
|
+
# Calculate the transformation matrix
|
|
123
|
+
image_size = images[0].size
|
|
124
|
+
offset = (
|
|
125
|
+
0.5 - image_size[0] / 16,
|
|
126
|
+
0.5 - image_size[1] / 16
|
|
127
|
+
)
|
|
128
|
+
transformation_matrix = [
|
|
129
|
+
image_size[0], 0, 0, offset[0],
|
|
130
|
+
0, image_size[1], 0, offset[1],
|
|
131
|
+
0, 0, 1, 0.5,
|
|
132
|
+
0, 0, 0, 1
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
# File generation
|
|
136
|
+
logger.debug("Building resource pack files...")
|
|
137
|
+
|
|
138
|
+
item_model_entries = []
|
|
139
|
+
|
|
140
|
+
for i in range(0, len(images)):
|
|
141
|
+
logger.debug("Bulding %s", f"{resource_location_path}/{i}")
|
|
142
|
+
image = images[i]
|
|
143
|
+
|
|
144
|
+
# Texture at `assets/<ns>/textures/item/reef/mini/<pdf_name>/<page>`
|
|
145
|
+
pack[namespace].textures[f"item/{resource_location_path}/{i}"] = Texture(image)
|
|
146
|
+
|
|
147
|
+
# Model at `assets/<ns>/models/item/reef/mini/<pdf_name>/<page>`
|
|
148
|
+
pack[namespace].models[f"item/{resource_location_path}/{i}"] = Model({
|
|
149
|
+
"credit": "Generated with Reef",
|
|
150
|
+
"parent": "reef:item/element_base",
|
|
151
|
+
"textures": {
|
|
152
|
+
"image": f"{namespace}:{resource_location_path}/{i}",
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
# Build the item model entries
|
|
157
|
+
tints_field = {
|
|
158
|
+
"tints": [{
|
|
159
|
+
"type": "minecraft:constant",
|
|
160
|
+
"value": self.reef_opts.tint
|
|
161
|
+
}]
|
|
162
|
+
} if self.reef_opts.tint is not None else {}
|
|
163
|
+
item_model_entries.append({
|
|
164
|
+
"threshold": i,
|
|
165
|
+
"model": {
|
|
166
|
+
"type": "minecraft:model",
|
|
167
|
+
"model": f"{namespace}:{resource_location_path}/{i}",
|
|
168
|
+
**tints_field
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
# Item Model at `assets/<ns>/items/reef/mini/<pdf_name>`
|
|
173
|
+
pack[namespace].item_models[f"{resource_location_path}"] = ItemModel({
|
|
174
|
+
"model": {
|
|
175
|
+
"type": "minecraft:range_dispatch",
|
|
176
|
+
"property": "minecraft:custom_model_data",
|
|
177
|
+
"index": 0,
|
|
178
|
+
"entries": item_model_entries,
|
|
179
|
+
"transformation": transformation_matrix
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
logger.debug("Done building resource pack files!")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
return ReefPdf
|
|
187
|
+
|
|
188
|
+
def pdf(ctx: Context):
|
|
189
|
+
"""Adds support for Reef PDF asset files to generate Reef Mini compatible assets."""
|
|
190
|
+
|
|
191
|
+
ctx.assets.extend_namespace.append(create_reef_pdf_namespace(ctx))
|
reef/plugin.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from beet import Context, Model, PluginOptions
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from .pdf import pdf
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"reef",
|
|
8
|
+
"beet_default"
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger("reef")
|
|
12
|
+
|
|
13
|
+
def beet_default(ctx: Context):
|
|
14
|
+
ctx.require(reef)
|
|
15
|
+
ctx.require(pdf)
|
|
16
|
+
|
|
17
|
+
def reef(ctx: Context):
|
|
18
|
+
"""Initializes the Reef plugin system"""
|
|
19
|
+
logger.debug("Creating base assets...")
|
|
20
|
+
|
|
21
|
+
ctx.assets["reef"].models["item/element_base"] = Model({
|
|
22
|
+
"credit": "Generated with Reef",
|
|
23
|
+
"ambientocclusion": False,
|
|
24
|
+
"elements": [
|
|
25
|
+
{
|
|
26
|
+
"from": [0, 0, 0],
|
|
27
|
+
"to": [1, 1, 0],
|
|
28
|
+
"shade": False,
|
|
29
|
+
"light_emission": 15,
|
|
30
|
+
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 0]},
|
|
31
|
+
"faces": {
|
|
32
|
+
"north": {"uv": [0, 0, 16, 16], "texture": "#image", "tintindex": 0}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
logger.debug("Created reef:item/element_base")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: reef-beet-plugin
|
|
3
|
+
Version: 1.0.0b1
|
|
4
|
+
Summary: Beet plugin to autogenerate Reef compatible data packs and resource packs from PDF and Google Slides presentations.
|
|
5
|
+
Keywords: beet,minecraft,datapack,resource_pack,presentation,reef
|
|
6
|
+
Author: Trplnr
|
|
7
|
+
Author-email: Trplnr <triopane30@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: beet>=0.115.0
|
|
11
|
+
Requires-Dist: pdf2image>=1.17.0
|
|
12
|
+
Requires-Python: >=3.14
|
|
13
|
+
Project-URL: Homepage, https://github.com/Trioplane/reef
|
|
14
|
+
Project-URL: Issues, https://github.com/Trioplane/reef/issues
|
|
15
|
+
Project-URL: Repository, https://github.com/Trioplane/reef/blob/main/packages/reef
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# Reef Beet Plugin
|
|
19
|
+
> Beet plugin to autogenerate Reef compatible data packs and resource packs from PDF and Google Slides presentations.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
reef/__init__.py,sha256=45sG9-60c5g67pa6p2w0JqU7jQNlepSuXOzVFVDBLgA,82
|
|
2
|
+
reef/gslides.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
reef/options.py,sha256=sVwpC9CqKkMCrB2Nb9xgB_-ysqPOMsC5qiMyB1_sE74,298
|
|
4
|
+
reef/pdf.py,sha256=lqQ7JZRpuPn3rHZy4Pa7OzImbQDV1c7CuZ9bZTkHeSI,7119
|
|
5
|
+
reef/plugin.py,sha256=4qfF2zz_HToZyuroODyLJtBA6Od096QI-AwdZZV1bPc,908
|
|
6
|
+
reef_beet_plugin-1.0.0b1.dist-info/licenses/LICENSE,sha256=b8M9UbMguBmohcWXAcKjoOjSmohcUSFdBwx76yrKbc0,1083
|
|
7
|
+
reef_beet_plugin-1.0.0b1.dist-info/WHEEL,sha256=wXwAVsgVaOZ_pwDFqQm5Rd6PID-Fc74nkLc8X8gHiDo,81
|
|
8
|
+
reef_beet_plugin-1.0.0b1.dist-info/METADATA,sha256=zPl7BoKPvaVQOwgQ7yNbKqRMdbxA--d0iYZ37S90efY,824
|
|
9
|
+
reef_beet_plugin-1.0.0b1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Trplnr
|
|
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.
|