smashed updated
This commit is contained in:
1 parent
77921ba6a8
commit
1da0b26320
14 files changed
+321
-96
No files matched your search
@@ -56,6 +56,15 @@ async def s_api_v1_blockchain_send_new_content_message(request):
|
||||
assert field_key in request.json, f"No {field_key} provided"
|
||||
assert field_value(request.json[field_key]), f"Invalid {field_key} provided"
|
||||
|
||||
artist = request.json.get('artist')
|
||||
if artist is not None:
|
||||
assert isinstance(artist, str), "Invalid artist provided"
|
||||
artist = artist.strip()
|
||||
if artist == "":
|
||||
artist = None
|
||||
else:
|
||||
artist = None
|
||||
|
||||
# Support legacy: 'content' as decrypted ContentId; and new: 'content' as encrypted IPFS CID
|
||||
source_content_cid, cid_err = resolve_content(request.json['content'])
|
||||
assert not cid_err, f"Invalid content CID provided: {cid_err}"
|
||||
@@ -85,11 +94,16 @@ async def s_api_v1_blockchain_send_new_content_message(request):
|
||||
image_content = None
|
||||
|
||||
|
||||
content_title = f"{', '.join(request.json['authors'])} – {request.json['title']}" if request.json['authors'] else request.json['title']
|
||||
content_title = request.json['title']
|
||||
if artist:
|
||||
content_title = f"{artist} – {content_title}"
|
||||
elif request.json['authors']:
|
||||
content_title = f"{', '.join(request.json['authors'])} – {request.json['title']}"
|
||||
|
||||
metadata_content = await create_metadata_for_item(
|
||||
request.ctx.db_session,
|
||||
title=content_title,
|
||||
title=request.json['title'],
|
||||
artist=artist,
|
||||
cover_url=f"{PROJECT_HOST}/api/v1.5/storage/{image_content_cid.serialize_v2()}" if image_content_cid else None,
|
||||
authors=request.json['authors'],
|
||||
hashtags=request.json['hashtags'],
|
||||
|
||||
+28
-5
@@ -181,11 +181,14 @@ def _storage_download_url(file_hash: Optional[str]) -> Optional[str]:
|
||||
def _pick_primary_download(candidates: List[tuple[str, Optional[str], Optional[int]]]) -> Optional[str]:
|
||||
priority = (
|
||||
'decrypted_high',
|
||||
'decrypted_original',
|
||||
'decrypted_low',
|
||||
'decrypted_preview',
|
||||
'high',
|
||||
'low',
|
||||
'preview',
|
||||
'original',
|
||||
'stored',
|
||||
)
|
||||
for target in priority:
|
||||
for kind, url, _ in candidates:
|
||||
@@ -656,6 +659,7 @@ async def s_api_v1_admin_uploads(request):
|
||||
}
|
||||
|
||||
search_parts: List[Any] = [
|
||||
content.artist,
|
||||
content.title,
|
||||
content.description,
|
||||
content.encrypted_cid,
|
||||
@@ -697,6 +701,7 @@ async def s_api_v1_admin_uploads(request):
|
||||
'metadata_cid': metadata_cid,
|
||||
'content_hash': content_hash,
|
||||
'title': content.title,
|
||||
'artist': content.artist,
|
||||
'description': content.description,
|
||||
'content_type': content.content_type,
|
||||
'size': {
|
||||
@@ -1342,10 +1347,21 @@ async def s_api_v1_admin_licenses(request):
|
||||
metadata_candidate = stored_meta.get('metadata') if isinstance(stored_meta.get('metadata'), dict) else {}
|
||||
title_candidates = [
|
||||
stored_meta.get('title'),
|
||||
metadata_candidate.get('title') if isinstance(metadata_candidate, dict) else None,
|
||||
metadata_candidate.get('name') if isinstance(metadata_candidate, dict) else None,
|
||||
stored_meta.get('license', {}).get('title') if isinstance(stored_meta.get('license'), dict) else None,
|
||||
]
|
||||
title_value = next((value for value in title_candidates if isinstance(value, str) and value.strip()), None)
|
||||
artist_candidates = []
|
||||
if isinstance(metadata_candidate, dict):
|
||||
artist_candidates.extend([
|
||||
metadata_candidate.get('artist'),
|
||||
(metadata_candidate.get('authors') or [None])[0] if isinstance(metadata_candidate.get('authors'), list) else None,
|
||||
])
|
||||
artist_candidates.extend([
|
||||
stored_meta.get('artist'),
|
||||
])
|
||||
artist_value = next((value for value in artist_candidates if isinstance(value, str) and value.strip()), None)
|
||||
try:
|
||||
cid_value = stored_content.cid.serialize_v2()
|
||||
except Exception:
|
||||
@@ -1354,7 +1370,8 @@ async def s_api_v1_admin_licenses(request):
|
||||
'id': stored_content.id,
|
||||
'hash': stored_content.hash,
|
||||
'cid': cid_value,
|
||||
'title': title_value or stored_content.hash,
|
||||
'title': (title_value or stored_content.hash),
|
||||
'artist': artist_value,
|
||||
'type': stored_content.type,
|
||||
'owner_address': stored_content.owner_address,
|
||||
'onchain_index': stored_content.onchain_index,
|
||||
@@ -1773,11 +1790,17 @@ async def s_api_v1_admin_status(request):
|
||||
ec = (await session.execute(select(EncryptedContent))).scalars().all()
|
||||
backlog = 0
|
||||
for e in ec:
|
||||
if not e.preview_enabled:
|
||||
ctype = (e.content_type or '').lower()
|
||||
if ctype.startswith('audio/'):
|
||||
req = {'decrypted_low', 'decrypted_high'}
|
||||
elif ctype.startswith('video/'):
|
||||
req = {'decrypted_low', 'decrypted_high', 'decrypted_preview'}
|
||||
else:
|
||||
req = {'decrypted_original'}
|
||||
if not req:
|
||||
continue
|
||||
kinds = [d.kind for d in deriv if d.content_id == e.id and d.status == 'ready']
|
||||
req = {'decrypted_low', 'decrypted_high', 'decrypted_preview'}
|
||||
if not req.issubset(set(kinds)):
|
||||
kinds = {d.kind for d in deriv if d.content_id == e.id and d.status == 'ready'}
|
||||
if not req.issubset(kinds):
|
||||
backlog += 1
|
||||
try:
|
||||
bs = await bitswap_stat()
|
||||
|
||||
+161
-40
@@ -2,7 +2,6 @@ from datetime import datetime, timedelta
|
||||
from sanic import response
|
||||
from sqlalchemy import select, and_, func
|
||||
from aiogram import Bot, types
|
||||
from sqlalchemy import and_
|
||||
from app.core.logger import make_log
|
||||
from app.core.models._config import ServiceConfig
|
||||
from app.core.models.node_storage import StoredContent
|
||||
@@ -67,31 +66,53 @@ async def s_api_v1_content_view(request, content_address: str):
|
||||
select(StoredContent).where(StoredContent.hash == cid.content_hash_b58)
|
||||
)).scalars().first()
|
||||
async def open_content_async(session, sc: StoredContent):
|
||||
encrypted = sc if sc.encrypted else None
|
||||
decrypted = sc if not sc.encrypted else None
|
||||
if not sc.encrypted:
|
||||
decrypted = sc
|
||||
encrypted = (await session.execute(select(StoredContent).where(StoredContent.decrypted_content_id == sc.id))).scalars().first()
|
||||
else:
|
||||
encrypted = sc
|
||||
decrypted = (await session.execute(select(StoredContent).where(StoredContent.id == sc.decrypted_content_id))).scalars().first()
|
||||
assert decrypted and encrypted, "Can't open content"
|
||||
ctype = decrypted.json_format().get('content_type', 'application/x-binary')
|
||||
if not encrypted:
|
||||
raise AssertionError("Can't open content")
|
||||
content_mime = None
|
||||
if decrypted:
|
||||
try:
|
||||
content_mime = decrypted.json_format().get('content_type')
|
||||
except Exception:
|
||||
content_mime = None
|
||||
if not content_mime:
|
||||
meta = encrypted.meta or {}
|
||||
content_mime = meta.get('content_type') or 'application/octet-stream'
|
||||
try:
|
||||
content_type = ctype.split('/')[0]
|
||||
content_type = content_mime.split('/')[0]
|
||||
except Exception:
|
||||
content_type = 'application'
|
||||
return {'encrypted_content': encrypted, 'decrypted_content': decrypted, 'content_type': content_type}
|
||||
return {
|
||||
'encrypted_content': encrypted,
|
||||
'decrypted_content': decrypted,
|
||||
'content_type': content_type,
|
||||
'content_mime': content_mime,
|
||||
}
|
||||
content = await open_content_async(request.ctx.db_session, r_content)
|
||||
encrypted_content = content['encrypted_content']
|
||||
decrypted_content = content.get('decrypted_content')
|
||||
content_mime = content.get('content_mime') or 'application/octet-stream'
|
||||
is_audio = content_mime.startswith('audio/')
|
||||
is_video = content_mime.startswith('video/')
|
||||
content_kind = 'audio' if is_audio else ('video' if is_video else 'other')
|
||||
|
||||
master_address = content['encrypted_content'].meta.get('item_address', '')
|
||||
master_address = encrypted_content.meta.get('item_address', '')
|
||||
opts = {
|
||||
'content_type': content['content_type'], # возможно с ошибками, нужно переделать на ffprobe
|
||||
'content_type': content_kind,
|
||||
'content_kind': content_kind,
|
||||
'content_mime': content_mime,
|
||||
'content_address': license_address or master_address,
|
||||
'license_address': license_address,
|
||||
'master_address': master_address,
|
||||
}
|
||||
if content['encrypted_content'].key_id:
|
||||
if encrypted_content.key_id:
|
||||
known_key = (await request.ctx.db_session.execute(
|
||||
select(KnownKey).where(KnownKey.id == content['encrypted_content'].key_id)
|
||||
select(KnownKey).where(KnownKey.id == encrypted_content.key_id)
|
||||
)).scalars().first()
|
||||
if known_key:
|
||||
opts['key_hash'] = known_key.seed_hash # нахер не нужно на данный момент
|
||||
@@ -104,14 +125,14 @@ async def s_api_v1_content_view(request, content_address: str):
|
||||
if request.ctx.user:
|
||||
user_wallet_address = await request.ctx.user.wallet_address_async(request.ctx.db_session)
|
||||
have_access = (
|
||||
(content['encrypted_content'].owner_address == user_wallet_address)
|
||||
(encrypted_content.owner_address == user_wallet_address)
|
||||
or bool((await request.ctx.db_session.execute(select(UserContent).where(
|
||||
and_(UserContent.owner_address == user_wallet_address, UserContent.status == 'active', UserContent.content_id == content['encrypted_content'].id)
|
||||
and_(UserContent.owner_address == user_wallet_address, UserContent.status == 'active', UserContent.content_id == encrypted_content.id)
|
||||
))).scalars().first()) \
|
||||
or bool((await request.ctx.db_session.execute(select(StarsInvoice).where(
|
||||
and_(
|
||||
StarsInvoice.user_id == request.ctx.user.id,
|
||||
StarsInvoice.content_hash == content['encrypted_content'].hash,
|
||||
StarsInvoice.content_hash == encrypted_content.hash,
|
||||
StarsInvoice.paid == True
|
||||
)
|
||||
))).scalars().first())
|
||||
@@ -122,7 +143,7 @@ async def s_api_v1_content_view(request, content_address: str):
|
||||
if current_star_rate < 0:
|
||||
current_star_rate = 0.00000001
|
||||
|
||||
stars_cost = int(int(content['encrypted_content'].meta['license']['resale']['price']) / 1e9 / current_star_rate * 1.2)
|
||||
stars_cost = int(int(encrypted_content.meta['license']['resale']['price']) / 1e9 / current_star_rate * 1.2)
|
||||
if request.ctx.user.telegram_id in [5587262915, 6861699286]:
|
||||
stars_cost = 2
|
||||
|
||||
@@ -132,7 +153,7 @@ async def s_api_v1_content_view(request, content_address: str):
|
||||
StarsInvoice.user_id == request.ctx.user.id,
|
||||
StarsInvoice.created > datetime.now() - timedelta(minutes=25),
|
||||
StarsInvoice.amount == stars_cost,
|
||||
StarsInvoice.content_hash == content['encrypted_content'].hash,
|
||||
StarsInvoice.content_hash == encrypted_content.hash,
|
||||
)
|
||||
))).scalars().first()
|
||||
if exist_invoice:
|
||||
@@ -154,7 +175,7 @@ async def s_api_v1_content_view(request, content_address: str):
|
||||
type='access',
|
||||
amount=stars_cost,
|
||||
user_id=request.ctx.user.id,
|
||||
content_hash=content['encrypted_content'].hash,
|
||||
content_hash=encrypted_content.hash,
|
||||
invoice_url=invoice_url
|
||||
)
|
||||
)
|
||||
@@ -168,12 +189,17 @@ async def s_api_v1_content_view(request, content_address: str):
|
||||
'amount': stars_cost,
|
||||
}
|
||||
|
||||
display_options = {'content_url': None}
|
||||
display_options = {
|
||||
'content_url': None,
|
||||
'download_url': None,
|
||||
'content_kind': content_kind,
|
||||
'content_mime': content_mime,
|
||||
}
|
||||
|
||||
if have_access:
|
||||
opts['have_licenses'].append('listen')
|
||||
|
||||
enc_cid = content['encrypted_content'].meta.get('content_cid') or content['encrypted_content'].meta.get('encrypted_cid')
|
||||
enc_cid = encrypted_content.meta.get('content_cid') or encrypted_content.meta.get('encrypted_cid')
|
||||
ec_v3 = None
|
||||
derivative_rows = []
|
||||
if enc_cid:
|
||||
@@ -185,7 +211,7 @@ async def s_api_v1_content_view(request, content_address: str):
|
||||
if enc_cid:
|
||||
upload_row = (await request.ctx.db_session.execute(select(UploadSession).where(UploadSession.encrypted_cid == enc_cid))).scalars().first()
|
||||
|
||||
converted_meta_map = dict(content['encrypted_content'].meta.get('converted_content') or {})
|
||||
converted_meta_map = dict(encrypted_content.meta.get('converted_content') or {})
|
||||
|
||||
derivative_latest = {}
|
||||
if derivative_rows:
|
||||
@@ -199,24 +225,57 @@ async def s_api_v1_content_view(request, content_address: str):
|
||||
file_hash = row.local_path.split('/')[-1]
|
||||
return file_hash, f"{PROJECT_HOST}/api/v1.5/storage/{file_hash}"
|
||||
|
||||
chosen_row = None
|
||||
preview_row = None
|
||||
download_row = None
|
||||
if have_access:
|
||||
for key in ('decrypted_low', 'decrypted_high'):
|
||||
preview_priority = ['decrypted_low', 'decrypted_high'] if (is_audio or is_video) else []
|
||||
download_priority = ['decrypted_high', 'decrypted_low']
|
||||
if content_kind == 'other':
|
||||
download_priority = ['decrypted_original']
|
||||
for key in preview_priority:
|
||||
if key in derivative_latest:
|
||||
chosen_row = derivative_latest[key]
|
||||
preview_row = derivative_latest[key]
|
||||
break
|
||||
for key in download_priority:
|
||||
if key in derivative_latest:
|
||||
download_row = derivative_latest[key]
|
||||
break
|
||||
else:
|
||||
for key in ('decrypted_preview', 'decrypted_low'):
|
||||
preview_priority = ['decrypted_preview', 'decrypted_low'] if (is_audio or is_video) else []
|
||||
for key in preview_priority:
|
||||
if key in derivative_latest:
|
||||
chosen_row = derivative_latest[key]
|
||||
preview_row = derivative_latest[key]
|
||||
break
|
||||
|
||||
if chosen_row:
|
||||
file_hash, url = _row_to_hash_and_url(chosen_row)
|
||||
if preview_row:
|
||||
file_hash, url = _row_to_hash_and_url(preview_row)
|
||||
if url:
|
||||
display_options['content_url'] = url
|
||||
opts['content_ext'] = (chosen_row.content_type or '').split('/')[-1] if chosen_row.content_type else None
|
||||
converted_meta_map.setdefault('low' if have_access else 'low_preview', file_hash)
|
||||
if preview_row.content_type and not opts.get('content_ext'):
|
||||
opts['content_ext'] = (preview_row.content_type or '').split('/')[-1]
|
||||
preview_map = {
|
||||
'decrypted_low': 'low',
|
||||
'decrypted_high': 'high',
|
||||
'decrypted_preview': 'low_preview',
|
||||
}
|
||||
cache_key = preview_map.get(preview_row.kind)
|
||||
if cache_key:
|
||||
converted_meta_map.setdefault(cache_key, file_hash)
|
||||
|
||||
if download_row and have_access:
|
||||
download_hash, download_url = _row_to_hash_and_url(download_row)
|
||||
if download_url:
|
||||
display_options['download_url'] = download_url
|
||||
if download_row.content_type and not opts.get('content_ext'):
|
||||
opts['content_ext'] = download_row.content_type.split('/')[-1]
|
||||
download_map = {
|
||||
'decrypted_high': 'high',
|
||||
'decrypted_low': 'low',
|
||||
'decrypted_original': 'original',
|
||||
}
|
||||
d_cache_key = download_map.get(download_row.kind)
|
||||
if d_cache_key:
|
||||
converted_meta_map.setdefault(d_cache_key, download_hash)
|
||||
|
||||
if not display_options['content_url'] and converted_meta_map:
|
||||
preference = ['low', 'high', 'low_preview'] if have_access else ['low_preview', 'low', 'high']
|
||||
@@ -227,11 +286,28 @@ async def s_api_v1_content_view(request, content_address: str):
|
||||
stored = (await request.ctx.db_session.execute(select(StoredContent).where(StoredContent.hash == hash_value))).scalars().first()
|
||||
if stored:
|
||||
display_options['content_url'] = stored.web_url
|
||||
opts['content_ext'] = stored.filename.split('.')[-1]
|
||||
if not opts.get('content_ext'):
|
||||
opts['content_ext'] = stored.filename.split('.')[-1]
|
||||
break
|
||||
|
||||
if have_access and not display_options['download_url'] and converted_meta_map:
|
||||
download_keys = ['original', 'high', 'low']
|
||||
for key in download_keys:
|
||||
hash_value = converted_meta_map.get(key)
|
||||
if not hash_value:
|
||||
continue
|
||||
stored = (await request.ctx.db_session.execute(select(StoredContent).where(StoredContent.hash == hash_value))).scalars().first()
|
||||
if stored:
|
||||
display_options['download_url'] = stored.web_url
|
||||
if not opts.get('content_ext'):
|
||||
opts['content_ext'] = stored.filename.split('.')[-1]
|
||||
break
|
||||
|
||||
if not opts.get('content_ext'):
|
||||
opts['content_ext'] = content_mime.split('/')[-1] if '/' in content_mime else None
|
||||
|
||||
# Metadata fallback
|
||||
content_meta = content['encrypted_content'].json_format()
|
||||
content_meta = encrypted_content.json_format()
|
||||
content_metadata_json = None
|
||||
_mcid = content_meta.get('metadata_cid') or None
|
||||
if _mcid:
|
||||
@@ -247,20 +323,58 @@ async def s_api_v1_content_view(request, content_address: str):
|
||||
if not content_metadata_json:
|
||||
fallback_name = (ec_v3.title if ec_v3 else None) or content_meta.get('title') or content_meta.get('cid')
|
||||
fallback_description = (ec_v3.description if ec_v3 else '') or ''
|
||||
fallback_artist = content_meta.get('artist') or None
|
||||
content_metadata_json = {
|
||||
'name': fallback_name or 'Без названия',
|
||||
'title': fallback_name or 'Без названия',
|
||||
'artist': fallback_artist,
|
||||
'description': fallback_description,
|
||||
'downloadable': False,
|
||||
}
|
||||
cover_cid = content_meta.get('cover_cid')
|
||||
if cover_cid:
|
||||
content_metadata_json.setdefault('image', f"{PROJECT_HOST}/api/v1.5/storage/{cover_cid}")
|
||||
else:
|
||||
if 'title' not in content_metadata_json or not content_metadata_json.get('title'):
|
||||
content_metadata_json['title'] = content_metadata_json.get('name')
|
||||
if 'artist' not in content_metadata_json:
|
||||
inferred_artist = None
|
||||
authors_list = content_metadata_json.get('authors')
|
||||
if isinstance(authors_list, list) and authors_list:
|
||||
inferred_artist = authors_list[0]
|
||||
content_metadata_json['artist'] = inferred_artist
|
||||
if content_metadata_json.get('artist') in ('', None):
|
||||
content_metadata_json['artist'] = None
|
||||
if not content_metadata_json.get('name'):
|
||||
content_metadata_json['name'] = content_metadata_json.get('title') or 'Без названия'
|
||||
|
||||
if ec_v3 and not content_metadata_json.get('artist') and getattr(ec_v3, 'artist', None):
|
||||
content_metadata_json['artist'] = ec_v3.artist
|
||||
if ec_v3 and not content_metadata_json.get('title') and getattr(ec_v3, 'title', None):
|
||||
content_metadata_json['title'] = ec_v3.title
|
||||
|
||||
display_title = content_metadata_json.get('title') or content_metadata_json.get('name') or 'Без названия'
|
||||
display_artist = content_metadata_json.get('artist')
|
||||
if display_artist:
|
||||
content_metadata_json['display_name'] = f"{display_artist} – {display_title}"
|
||||
else:
|
||||
content_metadata_json['display_name'] = display_title
|
||||
content_metadata_json['mime_type'] = content_mime
|
||||
if 'file_extension' not in content_metadata_json or not content_metadata_json.get('file_extension'):
|
||||
try:
|
||||
content_metadata_json['file_extension'] = content_mime.split('/')[1]
|
||||
except Exception:
|
||||
content_metadata_json['file_extension'] = None
|
||||
content_metadata_json['content_kind'] = content_kind
|
||||
|
||||
display_options['metadata'] = content_metadata_json
|
||||
display_options['is_preview_available'] = bool(display_options.get('content_url'))
|
||||
display_options['is_download_available'] = bool(display_options.get('download_url'))
|
||||
|
||||
opts['downloadable'] = content_metadata_json.get('downloadable', False)
|
||||
if opts['downloadable'] and 'listen' not in opts['have_licenses']:
|
||||
opts['downloadable'] = False
|
||||
base_downloadable = content_metadata_json.get('downloadable', False)
|
||||
if content_kind == 'other':
|
||||
base_downloadable = True
|
||||
opts['downloadable'] = bool(display_options.get('download_url')) and base_downloadable and have_access
|
||||
|
||||
# Conversion status summary
|
||||
conversion_summary = {}
|
||||
@@ -278,9 +392,13 @@ async def s_api_v1_content_view(request, content_address: str):
|
||||
'updated_at': (row.last_access_at or row.created_at).isoformat() + 'Z' if (row.last_access_at or row.created_at) else None,
|
||||
})
|
||||
|
||||
required_kinds = {'decrypted_low', 'decrypted_high'}
|
||||
if ec_v3 and ec_v3.content_type.startswith('video/'):
|
||||
required_kinds.add('decrypted_preview')
|
||||
effective_mime = (ec_v3.content_type if ec_v3 and ec_v3.content_type else content_mime) or ''
|
||||
if effective_mime.startswith('audio/'):
|
||||
required_kinds = {'decrypted_low', 'decrypted_high'}
|
||||
elif effective_mime.startswith('video/'):
|
||||
required_kinds = {'decrypted_low', 'decrypted_high', 'decrypted_preview'}
|
||||
else:
|
||||
required_kinds = {'decrypted_original'}
|
||||
|
||||
statuses_by_kind = {kind: row.status for kind, row in derivative_summary_map.items() if kind in required_kinds}
|
||||
conversion_state = 'pending'
|
||||
@@ -293,7 +411,7 @@ async def s_api_v1_content_view(request, content_address: str):
|
||||
elif statuses_by_kind:
|
||||
conversion_state = 'partial'
|
||||
|
||||
if display_options['content_url']:
|
||||
if display_options['content_url'] or (content_kind == 'other' and display_options.get('download_url')):
|
||||
conversion_state = 'ready'
|
||||
|
||||
upload_info = None
|
||||
@@ -306,7 +424,7 @@ async def s_api_v1_content_view(request, content_address: str):
|
||||
'updated_at': upload_row.updated_at.isoformat() + 'Z' if upload_row.updated_at else None,
|
||||
}
|
||||
|
||||
final_state = 'ready' if display_options['content_url'] else None
|
||||
final_state = 'ready' if (display_options['content_url'] or (content_kind == 'other' and display_options.get('download_url'))) else None
|
||||
if final_state != 'ready':
|
||||
upload_state = upload_row.state if upload_row else None
|
||||
if conversion_state == 'failed' or upload_state in ('failed', 'conversion_failed'):
|
||||
@@ -331,9 +449,12 @@ async def s_api_v1_content_view(request, content_address: str):
|
||||
'upload_state': upload_info['state'] if upload_info else None,
|
||||
}
|
||||
|
||||
encrypted_payload = encrypted_content.json_format()
|
||||
if ec_v3:
|
||||
encrypted_payload['artist'] = getattr(ec_v3, 'artist', None)
|
||||
return response.json({
|
||||
**opts,
|
||||
'encrypted': content['encrypted_content'].json_format(),
|
||||
'encrypted': encrypted_payload,
|
||||
'display_options': display_options,
|
||||
})
|
||||
|
||||
|
||||
@@ -27,9 +27,12 @@ async def s_api_v1_upload_status(request, upload_id: str):
|
||||
{"kind": kind, "status": status}
|
||||
for kind, status in derivative_rows
|
||||
]
|
||||
required = {"decrypted_high", "decrypted_low"}
|
||||
if ec.preview_enabled and ec.content_type.startswith("video/"):
|
||||
required.add("decrypted_preview")
|
||||
if ec.content_type and ec.content_type.startswith("audio/"):
|
||||
required = {"decrypted_high", "decrypted_low"}
|
||||
elif ec.content_type and ec.content_type.startswith("video/"):
|
||||
required = {"decrypted_high", "decrypted_low", "decrypted_preview"}
|
||||
else:
|
||||
required = {"decrypted_original"}
|
||||
statuses = {kind: status for kind, status in derivative_rows}
|
||||
if required and all(statuses.get(k) == "ready" for k in required):
|
||||
conv_state = "ready"
|
||||
|
||||
@@ -69,6 +69,7 @@ async def s_api_v1_upload_tus_hook(request):
|
||||
meta = upload.get("MetaData") or {}
|
||||
# Common metadata keys
|
||||
title = meta.get("title") or meta.get("Title") or meta.get("name") or "Untitled"
|
||||
artist = (meta.get("artist") or meta.get("Artist") or "").strip()
|
||||
description = meta.get("description") or meta.get("Description") or ""
|
||||
content_type = meta.get("content_type") or meta.get("Content-Type") or "application/octet-stream"
|
||||
preview_enabled = content_type.startswith("audio/") or content_type.startswith("video/")
|
||||
@@ -155,6 +156,7 @@ async def s_api_v1_upload_tus_hook(request):
|
||||
ec = EncryptedContent(
|
||||
encrypted_cid=encrypted_cid,
|
||||
title=title,
|
||||
artist=artist or None,
|
||||
description=description,
|
||||
content_type=content_type,
|
||||
enc_size_bytes=enc_size,
|
||||
@@ -196,7 +198,9 @@ async def s_api_v1_upload_tus_hook(request):
|
||||
'storage': 'ipfs',
|
||||
'encrypted_cid': encrypted_cid,
|
||||
'upload_id': upload_id,
|
||||
'source': 'tusd'
|
||||
'source': 'tusd',
|
||||
'title': title,
|
||||
'artist': artist or None,
|
||||
}
|
||||
encrypted_stored_content = StoredContent(
|
||||
type="local/encrypted_ipfs",
|
||||
@@ -218,12 +222,14 @@ async def s_api_v1_upload_tus_hook(request):
|
||||
"encrypted_cid": encrypted_cid,
|
||||
"title": title,
|
||||
"description": description,
|
||||
"artist": artist,
|
||||
"content_type": content_type,
|
||||
"size_bytes": enc_size,
|
||||
"preview_enabled": preview_enabled,
|
||||
"preview_conf": ec.preview_conf,
|
||||
"issuer_node_id": key_fpr,
|
||||
"salt_b64": _b64(salt),
|
||||
"artist": artist or None,
|
||||
}
|
||||
try:
|
||||
from app.core._crypto.signer import Signer
|
||||
|
||||
Reference in new issue
Block a user