Finished with Phase 2.6
This commit is contained in:
+105
-18
@@ -1,5 +1,7 @@
|
||||
import json
|
||||
import mimetypes
|
||||
import posixpath
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
from django.conf import settings
|
||||
@@ -30,7 +32,14 @@ from .models import (
|
||||
slugify_tag,
|
||||
)
|
||||
from .storage import delete_file_index, move_file_index, store_temp_file
|
||||
from .zips import parse_mods_manifest, read_animation_manifest, read_pack_mcmeta
|
||||
from .zips import (
|
||||
parse_mods_manifest,
|
||||
read_animation_manifest,
|
||||
read_logical_manifest,
|
||||
read_models_manifest,
|
||||
read_pack_meta,
|
||||
read_pack_mcmeta,
|
||||
)
|
||||
|
||||
|
||||
def _open_indexed_file(file_index):
|
||||
@@ -317,12 +326,19 @@ def project_detail(request, slug):
|
||||
|
||||
guide_docs = []
|
||||
animation_manifest = None
|
||||
models_manifest = None
|
||||
logical_manifest = None
|
||||
mods_manifest = None
|
||||
anim_stats = None
|
||||
latest = versions.first()
|
||||
if project.category == 'guide' and latest is not None:
|
||||
guide_docs = [vf for vf in latest.files.all() if vf.is_markdown]
|
||||
if latest is not None and latest.animation_manifest:
|
||||
animation_manifest = latest.animation_manifest
|
||||
if latest is not None:
|
||||
animation_manifest = latest.animation_manifest or None
|
||||
models_manifest = latest.models_manifest or None
|
||||
logical_manifest = latest.logical_manifest or None
|
||||
mods_manifest = latest.mods_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')
|
||||
solo_count = sum(1 for a in anims if a.get('type') == 'Solo')
|
||||
@@ -343,11 +359,37 @@ def project_detail(request, slug):
|
||||
'can_edit': can_edit,
|
||||
'guide_docs': guide_docs,
|
||||
'animation_manifest': animation_manifest,
|
||||
'models_manifest': models_manifest,
|
||||
'logical_manifest': logical_manifest,
|
||||
'mods_manifest': mods_manifest,
|
||||
'anim_stats': anim_stats,
|
||||
}
|
||||
return render(request, 'library/project_detail.html', context)
|
||||
|
||||
|
||||
def pack_asset(request, slug, version_id, asset_path):
|
||||
"""Serve a named member from a stored version zip (gated), used for the
|
||||
Models/Textures + Logical tabs and the model viewer."""
|
||||
version = get_object_or_404(
|
||||
Version.objects.select_related('project'),
|
||||
pk=version_id, project__slug=slug,
|
||||
)
|
||||
if not asset_path or asset_path.startswith('/') or '..' in asset_path.split('/'):
|
||||
raise Http404
|
||||
vf = (
|
||||
version.files.select_related('file').filter(kind='release').first()
|
||||
or version.files.select_related('file').first()
|
||||
)
|
||||
if vf is None:
|
||||
raise Http404
|
||||
with zipfile.ZipFile(_stored_path(vf.file)) as zf:
|
||||
if asset_path not in zf.namelist():
|
||||
raise Http404
|
||||
data = zf.read(asset_path)
|
||||
content_type = mimetypes.guess_type(asset_path)[0] or 'application/octet-stream'
|
||||
return HttpResponse(data, content_type=content_type)
|
||||
|
||||
|
||||
@login_required
|
||||
def api_packs_latest(request, namespace, pack_id):
|
||||
"""Auto-updater endpoint: newest version for a NoN pack id (namespace:pack_id)."""
|
||||
@@ -483,8 +525,8 @@ def _apply_content_tags(project, tag_names, actor):
|
||||
|
||||
def _finalize_version(project, version_name, changelog, temp_uploads, actor):
|
||||
"""Adopt pending 'version' temp uploads into a new Version as VersionFiles,
|
||||
capturing category-specific metadata (pack.mcmeta / mods manifest / animation
|
||||
manifest). Returns (version, missing_animation_id)."""
|
||||
capturing category/content-specific metadata (pack.mcmeta, mods, animation,
|
||||
models, logical manifests). Returns (version, missing_animation_id)."""
|
||||
version = Version.objects.create(
|
||||
project=project,
|
||||
version_name=version_name,
|
||||
@@ -493,7 +535,10 @@ def _finalize_version(project, version_name, changelog, temp_uploads, actor):
|
||||
pack_format = None
|
||||
pack_description = ''
|
||||
mods_manifest = {}
|
||||
animation_id = ''
|
||||
animation_manifest = None
|
||||
models_manifest = None
|
||||
logical_manifest = None
|
||||
missing_animation_id = False
|
||||
|
||||
for temp in temp_uploads:
|
||||
@@ -507,21 +552,31 @@ def _finalize_version(project, version_name, changelog, temp_uploads, actor):
|
||||
temp.save(update_fields=['status'])
|
||||
|
||||
is_zip = (index.original_filename or '').lower().endswith('.zip')
|
||||
if project.category == 'non_pack' and is_zip and pack_format is None:
|
||||
fmt, desc = read_pack_mcmeta(_stored_path(index))
|
||||
if not is_zip:
|
||||
continue
|
||||
path = _stored_path(index)
|
||||
|
||||
if project.category == 'non_pack' and pack_format is None:
|
||||
fmt, desc = read_pack_mcmeta(path)
|
||||
if fmt is not None:
|
||||
pack_format = fmt
|
||||
pack_description = desc
|
||||
elif project.category == 'modpack' and is_zip and not mods_manifest:
|
||||
manifest = parse_mods_manifest(_stored_path(index), index.original_filename)
|
||||
|
||||
if not animation_id:
|
||||
meta = read_pack_meta(path)
|
||||
if meta and meta.get('animation_id'):
|
||||
animation_id = meta['animation_id']
|
||||
|
||||
if animation_manifest is None:
|
||||
animation_manifest = read_animation_manifest(path)
|
||||
if models_manifest is None:
|
||||
models_manifest = read_models_manifest(path)
|
||||
if logical_manifest is None:
|
||||
logical_manifest = read_logical_manifest(path)
|
||||
if not mods_manifest:
|
||||
manifest = parse_mods_manifest(path, index.original_filename)
|
||||
if manifest.get('files'):
|
||||
mods_manifest = manifest
|
||||
if is_zip and animation_manifest is None:
|
||||
manifest = read_animation_manifest(_stored_path(index))
|
||||
if manifest is not None:
|
||||
animation_manifest = manifest
|
||||
if not manifest.get('animation_id'):
|
||||
missing_animation_id = True
|
||||
|
||||
update_fields = []
|
||||
if pack_format is not None:
|
||||
@@ -531,12 +586,20 @@ def _finalize_version(project, version_name, changelog, temp_uploads, actor):
|
||||
if mods_manifest:
|
||||
version.mods_manifest = mods_manifest
|
||||
update_fields.append('mods_manifest')
|
||||
if animation_id:
|
||||
version.animation_id = animation_id
|
||||
update_fields.append('animation_id')
|
||||
if animation_manifest is not None:
|
||||
version.animation_manifest = animation_manifest
|
||||
update_fields.append('animation_manifest')
|
||||
if animation_manifest.get('animation_id'):
|
||||
version.animation_id = animation_manifest['animation_id']
|
||||
update_fields.append('animation_id')
|
||||
if not animation_id:
|
||||
missing_animation_id = True
|
||||
if models_manifest is not None:
|
||||
version.models_manifest = models_manifest
|
||||
update_fields.append('models_manifest')
|
||||
if logical_manifest is not None:
|
||||
version.logical_manifest = logical_manifest
|
||||
update_fields.append('logical_manifest')
|
||||
if update_fields:
|
||||
version.save(update_fields=update_fields)
|
||||
|
||||
@@ -826,6 +889,30 @@ def asset_delete(request, slug, asset_id):
|
||||
return redirect('library:project_detail', slug=project.slug)
|
||||
|
||||
|
||||
def asset_thumbnail(request, slug, asset_id):
|
||||
"""Cached ffmpeg thumbnail for a gallery video/gif (grid preview only;
|
||||
the modal loads the real file)."""
|
||||
from pathlib import Path as _Path
|
||||
|
||||
from django.http import FileResponse
|
||||
|
||||
from .thumbnails import generate_thumbnail
|
||||
|
||||
asset = get_object_or_404(
|
||||
ProjectAsset.objects.select_related('file', 'project'),
|
||||
pk=asset_id, project__slug=slug,
|
||||
)
|
||||
if not (asset.is_video or asset.is_gif):
|
||||
raise Http404
|
||||
stored = generate_thumbnail(asset.file)
|
||||
if stored is None:
|
||||
raise Http404
|
||||
fh = open(_Path(settings.MEDIA_ROOT) / stored, 'rb')
|
||||
response = FileResponse(fh, content_type='image/jpeg')
|
||||
response['Cache-Control'] = 'public, max-age=86400'
|
||||
return response
|
||||
|
||||
|
||||
@login_required
|
||||
def contributors(request, slug):
|
||||
project = get_object_or_404(Project, slug=slug)
|
||||
|
||||
Reference in New Issue
Block a user