backup for home redising
This commit is contained in:
@@ -309,6 +309,30 @@ class UGCProjectDraftTests(UGCMediaTestCase, UGCGatedTestCase):
|
|||||||
self.assertFalse(ProjectDraft.objects.filter(uuid=uuid).exists())
|
self.assertFalse(ProjectDraft.objects.filter(uuid=uuid).exists())
|
||||||
self.assertFalse(TempUpload.objects.filter(draft_uuid=uuid).exists())
|
self.assertFalse(TempUpload.objects.filter(draft_uuid=uuid).exists())
|
||||||
|
|
||||||
|
def test_skin_individual_png_builds_manifest(self):
|
||||||
|
uuid, _ = self._new_draft()
|
||||||
|
self._auth(self.alice)
|
||||||
|
self._upload_temp('version', 'skin.png', make_png_bytes(size=(64, 64)), 'image/png', draft_uuid=uuid)
|
||||||
|
self.client.post(reverse('library:project_create', args=[uuid]), {
|
||||||
|
'title': 'My Skin', 'category': 'skin', 'version_name': '1.0.0',
|
||||||
|
})
|
||||||
|
version = Project.objects.get(slug='my-skin').versions.first()
|
||||||
|
manifest = version.skins_manifest
|
||||||
|
self.assertIn('skins', manifest)
|
||||||
|
self.assertEqual(manifest['skins'][0]['width'], 64)
|
||||||
|
self.assertEqual(manifest['skins'][0]['height'], 64)
|
||||||
|
self.assertTrue(manifest['skins'][0]['url'].startswith('/api/files/'))
|
||||||
|
|
||||||
|
def test_skin_rejects_non_skin_sized_png(self):
|
||||||
|
uuid, _ = self._new_draft()
|
||||||
|
self._auth(self.alice)
|
||||||
|
self._upload_temp('version', 'small.png', make_png_bytes(size=(8, 8)), 'image/png', draft_uuid=uuid)
|
||||||
|
self.client.post(reverse('library:project_create', args=[uuid]), {
|
||||||
|
'title': 'Bad Skin', 'category': 'skin', 'version_name': '1.0.0',
|
||||||
|
})
|
||||||
|
version = Project.objects.get(slug='bad-skin').versions.first()
|
||||||
|
self.assertEqual(version.skins_manifest, {})
|
||||||
|
|
||||||
|
|
||||||
class UGCVersionTests(UGCMediaTestCase, UGCGatedTestCase):
|
class UGCVersionTests(UGCMediaTestCase, UGCGatedTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|||||||
+69
-17
@@ -50,6 +50,8 @@ from .zips import (
|
|||||||
read_plugins_manifest,
|
read_plugins_manifest,
|
||||||
read_skins_manifest,
|
read_skins_manifest,
|
||||||
_mc_from_filename,
|
_mc_from_filename,
|
||||||
|
_png_size,
|
||||||
|
SKIN_SIZES,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -391,6 +393,14 @@ def project_detail(request, slug):
|
|||||||
mods_manifest = latest.mods_manifest or None
|
mods_manifest = latest.mods_manifest or None
|
||||||
plugins_manifest = latest.plugins_manifest or None
|
plugins_manifest = latest.plugins_manifest or None
|
||||||
skins_manifest = latest.skins_manifest or None
|
skins_manifest = latest.skins_manifest or None
|
||||||
|
if skins_manifest and latest.pk:
|
||||||
|
# Older manifests lack a per-skin url; fall back to the zip member.
|
||||||
|
for skin in skins_manifest.get('skins', []):
|
||||||
|
if not skin.get('url') and skin.get('member'):
|
||||||
|
skin['url'] = reverse(
|
||||||
|
'library:pack_asset',
|
||||||
|
args=[project.slug, latest.pk, skin['member']],
|
||||||
|
)
|
||||||
mod_manifest = latest.mod_manifest or None
|
mod_manifest = latest.mod_manifest or None
|
||||||
if animation_manifest:
|
if animation_manifest:
|
||||||
anims = animation_manifest.get('animations') or []
|
anims = animation_manifest.get('animations') or []
|
||||||
@@ -981,22 +991,55 @@ def _animation_meta_for_upload(request, project):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _build_skins_manifest(version, release_path=None):
|
||||||
|
"""Skins for a version: members of the release zip plus individually
|
||||||
|
uploaded PNG files. Each entry carries a ready-to-use ``url``."""
|
||||||
|
skins = []
|
||||||
|
if release_path is not None:
|
||||||
|
manifest = read_skins_manifest(release_path)
|
||||||
|
if manifest:
|
||||||
|
for skin in manifest.get('skins', []):
|
||||||
|
skin['url'] = reverse(
|
||||||
|
'library:pack_asset',
|
||||||
|
args=[version.project.slug, version.pk, skin['member']],
|
||||||
|
)
|
||||||
|
skins.append(skin)
|
||||||
|
for vf in version.files.filter(kind='png').select_related('file'):
|
||||||
|
try:
|
||||||
|
with open(_stored_path(vf.file), 'rb') as fh:
|
||||||
|
head = fh.read(24)
|
||||||
|
except OSError:
|
||||||
|
continue
|
||||||
|
size = _png_size(head)
|
||||||
|
if size and size in SKIN_SIZES:
|
||||||
|
skins.append({
|
||||||
|
'name': vf.filename,
|
||||||
|
'member': str(vf.file.uuid),
|
||||||
|
'width': size[0],
|
||||||
|
'height': size[1],
|
||||||
|
'url': reverse('library:file_request', args=[vf.file.uuid]),
|
||||||
|
})
|
||||||
|
if not skins:
|
||||||
|
return {}
|
||||||
|
return {'skins': skins}
|
||||||
|
|
||||||
|
|
||||||
def _reparse_version(version):
|
def _reparse_version(version):
|
||||||
"""Re-derive a version's metadata (id + manifests) from its release zip."""
|
"""Re-derive a version's metadata (id + manifests) from its release zip."""
|
||||||
release = version.files.filter(kind='release').first()
|
release = version.files.filter(kind='release').first()
|
||||||
if release is None:
|
path = _stored_path(release.file) if release is not None else None
|
||||||
|
if path is None and version.project.category != 'skin':
|
||||||
return
|
return
|
||||||
path = _stored_path(release.file)
|
meta = read_pack_meta(path) if path else None
|
||||||
meta = read_pack_meta(path)
|
|
||||||
if meta and meta.get('animation_id'):
|
if meta and meta.get('animation_id'):
|
||||||
version.animation_id = meta['animation_id']
|
version.animation_id = meta['animation_id']
|
||||||
else:
|
else:
|
||||||
version.animation_id = ''
|
version.animation_id = ''
|
||||||
version.animation_manifest = read_animation_manifest(path) or {}
|
version.animation_manifest = read_animation_manifest(path) or {} if path else {}
|
||||||
version.models_manifest = read_models_manifest(path, version.project.category) or {}
|
version.models_manifest = read_models_manifest(path, version.project.category) or {} if path else {}
|
||||||
version.logical_manifest = read_logical_manifest(path) or {}
|
version.logical_manifest = read_logical_manifest(path) or {} if path else {}
|
||||||
version.plugins_manifest = read_plugins_manifest(path) or {} if version.project.category == 'model' else {}
|
version.plugins_manifest = read_plugins_manifest(path) or {} if path and version.project.category == 'model' else {}
|
||||||
version.skins_manifest = read_skins_manifest(path) or {} if version.project.category == 'skin' else {}
|
version.skins_manifest = _build_skins_manifest(version, path) if version.project.category == 'skin' else {}
|
||||||
if version.project.category == 'mod':
|
if version.project.category == 'mod':
|
||||||
entries = [
|
entries = [
|
||||||
(vf.filename, _stored_path(vf.file))
|
(vf.filename, _stored_path(vf.file))
|
||||||
@@ -1006,12 +1049,15 @@ def _reparse_version(version):
|
|||||||
version.mod_manifest = read_mod_version_manifest(entries)
|
version.mod_manifest = read_mod_version_manifest(entries)
|
||||||
else:
|
else:
|
||||||
version.mod_manifest = {}
|
version.mod_manifest = {}
|
||||||
manifest = parse_mods_manifest(path, release.file.original_filename)
|
if release is not None:
|
||||||
version.mods_manifest = manifest if manifest.get('files') else {}
|
manifest = parse_mods_manifest(path, release.file.original_filename)
|
||||||
fmt, desc = read_pack_mcmeta(path)
|
version.mods_manifest = manifest if manifest.get('files') else {}
|
||||||
if fmt is not None:
|
fmt, desc = read_pack_mcmeta(path)
|
||||||
version.pack_format = fmt
|
if fmt is not None:
|
||||||
version.pack_description = desc
|
version.pack_format = fmt
|
||||||
|
version.pack_description = desc
|
||||||
|
else:
|
||||||
|
version.mods_manifest = {}
|
||||||
version.save()
|
version.save()
|
||||||
_sync_auto_tags(version.project, version.project.owner, version.project.versions.first())
|
_sync_auto_tags(version.project, version.project.owner, version.project.versions.first())
|
||||||
|
|
||||||
@@ -1080,8 +1126,14 @@ def _finalize_version(project, version_name, changelog, temp_uploads, actor):
|
|||||||
mods_manifest = manifest
|
mods_manifest = manifest
|
||||||
if plugins_manifest is None and project.category == 'model':
|
if plugins_manifest is None and project.category == 'model':
|
||||||
plugins_manifest = read_plugins_manifest(path)
|
plugins_manifest = read_plugins_manifest(path)
|
||||||
if skins_manifest is None and project.category == 'skin':
|
|
||||||
skins_manifest = read_skins_manifest(path)
|
# Skins come from the release zip members and/or individually uploaded PNGs.
|
||||||
|
if project.category == 'skin':
|
||||||
|
release = version.files.filter(kind='release').first()
|
||||||
|
release_path = None
|
||||||
|
if release is not None and (release.filename or '').lower().endswith('.zip'):
|
||||||
|
release_path = _stored_path(release.file)
|
||||||
|
skins_manifest = _build_skins_manifest(version, release_path)
|
||||||
|
|
||||||
update_fields = []
|
update_fields = []
|
||||||
if pack_format is not None:
|
if pack_format is not None:
|
||||||
@@ -1094,7 +1146,7 @@ def _finalize_version(project, version_name, changelog, temp_uploads, actor):
|
|||||||
if plugins_manifest is not None:
|
if plugins_manifest is not None:
|
||||||
version.plugins_manifest = plugins_manifest
|
version.plugins_manifest = plugins_manifest
|
||||||
update_fields.append('plugins_manifest')
|
update_fields.append('plugins_manifest')
|
||||||
if skins_manifest is not None:
|
if skins_manifest:
|
||||||
version.skins_manifest = skins_manifest
|
version.skins_manifest = skins_manifest
|
||||||
update_fields.append('skins_manifest')
|
update_fields.append('skins_manifest')
|
||||||
if project.category == 'mod' and mod_file_entries:
|
if project.category == 'mod' and mod_file_entries:
|
||||||
|
|||||||
@@ -258,10 +258,9 @@
|
|||||||
|
|
||||||
// Load a Minecraft skin into Blockbench's built-in "Minecraft Skin" project.
|
// Load a Minecraft skin into Blockbench's built-in "Minecraft Skin" project.
|
||||||
async function openSkin(btn) {
|
async function openSkin(btn) {
|
||||||
const member = btn.dataset.member;
|
const url = btn.dataset.url;
|
||||||
const name = btn.dataset.name || member;
|
const name = btn.dataset.name || 'skin';
|
||||||
const opts = { baseUrl: btn.dataset.baseUrl };
|
if (!url || busy) return;
|
||||||
if (!member || !opts.baseUrl || busy) return;
|
|
||||||
busy = true;
|
busy = true;
|
||||||
|
|
||||||
if (!(await prepareOverlay(name))) { busy = false; return; }
|
if (!(await prepareOverlay(name))) { busy = false; return; }
|
||||||
@@ -270,7 +269,7 @@
|
|||||||
try {
|
try {
|
||||||
const select = btn.closest('.skin-card')?.querySelector('.skin-model-select');
|
const select = btn.closest('.skin-card')?.querySelector('.skin-model-select');
|
||||||
const model = select ? select.value : 'steve';
|
const model = select ? select.value : 'steve';
|
||||||
const img = await loadImage(assetUrl(opts.baseUrl, member));
|
const img = await loadImage(url);
|
||||||
iframe.contentWindow.postMessage({
|
iframe.contentWindow.postMessage({
|
||||||
type: 'packs-open-skin',
|
type: 'packs-open-skin',
|
||||||
name: name,
|
name: name,
|
||||||
|
|||||||
@@ -436,7 +436,7 @@
|
|||||||
<div class="skin-grid">
|
<div class="skin-grid">
|
||||||
{% for skin in skins_manifest.skins %}
|
{% for skin in skins_manifest.skins %}
|
||||||
<div class="skin-card">
|
<div class="skin-card">
|
||||||
<img class="skin-preview" src="{% url 'library:pack_asset' project.slug versions.0.pk skin.member %}" alt="{{ skin.name }}" loading="lazy" title="{{ skin.name }}">
|
<img class="skin-preview" src="{{ skin.url }}" alt="{{ skin.name }}" loading="lazy" title="{{ skin.name }}">
|
||||||
<div class="skin-card-head">
|
<div class="skin-card-head">
|
||||||
<strong>{{ skin.name }}</strong>
|
<strong>{{ skin.name }}</strong>
|
||||||
<span class="skin-size-badge">{{ skin.width }}x{{ skin.height }}</span>
|
<span class="skin-size-badge">{{ skin.width }}x{{ skin.height }}</span>
|
||||||
@@ -450,8 +450,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<button type="button" class="btn btn-secondary btn-sm skin-render-btn"
|
<button type="button" class="btn btn-secondary btn-sm skin-render-btn"
|
||||||
data-member="{{ skin.member }}" data-name="{{ skin.name }}"
|
data-url="{{ skin.url }}" data-name="{{ skin.name }}"><i class="fas fa-user"></i> Render</button>
|
||||||
data-base-url="{% url 'library:pack_asset' project.slug versions.0.pk 'ASSET' %}"><i class="fas fa-user"></i> Render</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
Reference in New Issue
Block a user