finished Skin UGC
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 6.0.3 on 2026-08-05 14:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('library', '0009_version_plugins_manifest'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='version',
|
||||
name='skins_manifest',
|
||||
field=models.JSONField(blank=True, default=dict),
|
||||
),
|
||||
]
|
||||
@@ -210,6 +210,7 @@ class Version(models.Model):
|
||||
pack_description = models.TextField(blank=True, default='')
|
||||
mods_manifest = models.JSONField(default=dict, blank=True)
|
||||
plugins_manifest = models.JSONField(default=dict, blank=True)
|
||||
skins_manifest = models.JSONField(default=dict, blank=True)
|
||||
animation_id = models.CharField(max_length=128, blank=True, default='', db_index=True)
|
||||
animation_manifest = models.JSONField(default=dict, blank=True)
|
||||
models_manifest = models.JSONField(default=dict, blank=True)
|
||||
|
||||
@@ -42,6 +42,7 @@ from .zips import (
|
||||
read_pack_meta,
|
||||
read_pack_mcmeta,
|
||||
read_plugins_manifest,
|
||||
read_skins_manifest,
|
||||
)
|
||||
|
||||
|
||||
@@ -333,6 +334,7 @@ def project_detail(request, slug):
|
||||
logical_manifest = None
|
||||
mods_manifest = None
|
||||
plugins_manifest = None
|
||||
skins_manifest = None
|
||||
anim_stats = None
|
||||
latest = versions.first()
|
||||
if project.category == 'guide' and latest is not None:
|
||||
@@ -343,6 +345,7 @@ def project_detail(request, slug):
|
||||
logical_manifest = latest.logical_manifest or None
|
||||
mods_manifest = latest.mods_manifest or None
|
||||
plugins_manifest = latest.plugins_manifest or None
|
||||
skins_manifest = latest.skins_manifest or None
|
||||
if animation_manifest:
|
||||
anims = animation_manifest.get('animations') or []
|
||||
entity_count = sum(1 for a in anims if a.get('type') == 'Entity x Player')
|
||||
@@ -376,6 +379,7 @@ def project_detail(request, slug):
|
||||
'logical_manifest': logical_manifest,
|
||||
'mods_manifest': mods_manifest,
|
||||
'plugins_manifest': plugins_manifest,
|
||||
'skins_manifest': skins_manifest,
|
||||
'autoload_plugins_json': autoload_plugins_json,
|
||||
'anim_stats': anim_stats,
|
||||
}
|
||||
@@ -652,6 +656,7 @@ def _reparse_version(version):
|
||||
version.models_manifest = read_models_manifest(path, version.project.category) or {}
|
||||
version.logical_manifest = read_logical_manifest(path) or {}
|
||||
version.plugins_manifest = read_plugins_manifest(path) or {} if version.project.category == 'model' else {}
|
||||
version.skins_manifest = read_skins_manifest(path) or {} if version.project.category == 'skin' else {}
|
||||
manifest = parse_mods_manifest(path, release.file.original_filename)
|
||||
version.mods_manifest = manifest if manifest.get('files') else {}
|
||||
fmt, desc = read_pack_mcmeta(path)
|
||||
@@ -674,6 +679,7 @@ def _finalize_version(project, version_name, changelog, temp_uploads, actor):
|
||||
pack_description = ''
|
||||
mods_manifest = {}
|
||||
plugins_manifest = None
|
||||
skins_manifest = None
|
||||
animation_id = ''
|
||||
animation_manifest = None
|
||||
models_manifest = None
|
||||
@@ -718,6 +724,8 @@ def _finalize_version(project, version_name, changelog, temp_uploads, actor):
|
||||
mods_manifest = manifest
|
||||
if plugins_manifest is None and project.category == 'model':
|
||||
plugins_manifest = read_plugins_manifest(path)
|
||||
if skins_manifest is None and project.category == 'skin':
|
||||
skins_manifest = read_skins_manifest(path)
|
||||
|
||||
update_fields = []
|
||||
if pack_format is not None:
|
||||
@@ -730,6 +738,9 @@ def _finalize_version(project, version_name, changelog, temp_uploads, actor):
|
||||
if plugins_manifest is not None:
|
||||
version.plugins_manifest = plugins_manifest
|
||||
update_fields.append('plugins_manifest')
|
||||
if skins_manifest is not None:
|
||||
version.skins_manifest = skins_manifest
|
||||
update_fields.append('skins_manifest')
|
||||
if animation_id:
|
||||
version.animation_id = animation_id
|
||||
update_fields.append('animation_id')
|
||||
|
||||
@@ -7,6 +7,7 @@ adopted file (under MEDIA_ROOT) and return plain dicts/values.
|
||||
|
||||
import json
|
||||
import os
|
||||
import struct
|
||||
import zipfile
|
||||
|
||||
INJECTOR_NAMES = {'V': 'Vaginal', 'M': 'Mouth', 'A': 'Anal'}
|
||||
@@ -16,6 +17,48 @@ PROBLEM_TAGS = {'broken', 'bugged', 'borked'}
|
||||
MODEL_FILE_EXTENSIONS = ('.bbmodel', '.geo.json', '.jem', '.gltf', '.glb')
|
||||
|
||||
|
||||
def _png_size(head):
|
||||
"""(width, height) from a PNG IHDR header, or None when not a PNG."""
|
||||
if len(head) < 24 or head[:8] != b'\x89PNG\r\n\x1a\n':
|
||||
return None
|
||||
return (struct.unpack('>I', head[16:20])[0], struct.unpack('>I', head[20:24])[0])
|
||||
|
||||
|
||||
SKIN_SIZES = {(64, 32), (64, 64), (128, 128)}
|
||||
|
||||
|
||||
def read_skins_manifest(path):
|
||||
"""Scan the archive for Minecraft skin PNGs (64x32, 64x64 or 128x128).
|
||||
|
||||
Returns None when no skins are found; otherwise {skins: [{name, member,
|
||||
width, height}]} where ``member`` is the zip member served via pack_asset.
|
||||
"""
|
||||
zf = _open_zip(path)
|
||||
if zf is None:
|
||||
return None
|
||||
try:
|
||||
skins = []
|
||||
for name in sorted(n for n in set(zf.namelist()) if n.lower().endswith('.png')):
|
||||
try:
|
||||
with zf.open(name) as fh:
|
||||
head = fh.read(24)
|
||||
except (KeyError, OSError, zipfile.BadZipFile):
|
||||
continue
|
||||
size = _png_size(head)
|
||||
if size and size in SKIN_SIZES:
|
||||
skins.append({
|
||||
'name': name.rsplit('/', 1)[-1],
|
||||
'member': name,
|
||||
'width': size[0],
|
||||
'height': size[1],
|
||||
})
|
||||
if not skins:
|
||||
return None
|
||||
return {'skins': skins}
|
||||
finally:
|
||||
zf.close()
|
||||
|
||||
|
||||
def _in_plugins_dir(name):
|
||||
"""True when a member lives inside a plugins/ folder (any nesting)."""
|
||||
parts = name.split('/')
|
||||
|
||||
Reference in New Issue
Block a user