|
1 | 1 | (function (global) { |
2 | 2 | 'use strict'; |
3 | 3 |
|
4 | | - const API_BASE = 'https://community-api-axuaystczl.cn-hangzhou.fcapp.run'; |
5 | 4 | const COMMUNITY_API_BASE = 'https://community-api.pythonide.xin/v2/community'; |
6 | 5 | const APP_STORE_URL = 'https://apps.apple.com/app/id6753987304'; |
7 | 6 | const SITE_ORIGIN = 'https://link.pythonide.xin'; |
|
11 | 10 |
|
12 | 11 | const CATEGORY_LABELS = { |
13 | 12 | python: 'Python', |
| 13 | + games: '游戏', |
14 | 14 | miniapp: 'MiniApp', |
15 | 15 | appui: 'AppUI', |
16 | 16 | html: 'HTML', |
17 | 17 | scene: 'Scene', |
18 | 18 | pygame: 'Pygame', |
19 | 19 | widget: 'Widget', |
| 20 | + markdown: 'Markdown', |
20 | 21 | other: '社区作品', |
21 | 22 | }; |
22 | 23 |
|
|
409 | 410 | } |
410 | 411 |
|
411 | 412 | function filePresentation(script) { |
412 | | - const fileType = String(script.file_type || '').trim().replace(/^\./, '').toLowerCase(); |
| 413 | + const fileName = String(script.fileName || '').trim(); |
| 414 | + const fileType = String(script.fileType || script.file_type || fileName.split('.').pop() || '') |
| 415 | + .trim().replace(/^\./, '').toLowerCase(); |
413 | 416 | const category = String(script.category || 'other').trim().toLowerCase(); |
414 | | - const runtime = String(script.runtime || '').trim(); |
| 417 | + const runtime = String(script.runtimeKind || script.runtime || '').trim(); |
415 | 418 | return { |
416 | 419 | badge: FILE_LABELS[fileType] || FILE_LABELS[category] || String(fileType || category || 'CODE').slice(0, 4).toUpperCase(), |
417 | 420 | categoryLabel: CATEGORY_LABELS[category] || category || '社区作品', |
|
421 | 424 | } |
422 | 425 |
|
423 | 426 | function isProjectScript(script) { |
424 | | - const contentMode = String(script.content_mode || '').toLowerCase(); |
| 427 | + const contentMode = String(script.contentMode || script.content_mode || '').toLowerCase(); |
425 | 428 | const tags = normalizedTags(script).map((value) => value.toLowerCase()); |
426 | 429 | return contentMode === 'project_package' |
427 | | - || Boolean(script.package_id) |
| 430 | + || String(script.attachmentKind || '').toLowerCase() === 'project' |
| 431 | + || Boolean(script.packageID || script.package_id) |
428 | 432 | || tags.includes('project') |
429 | 433 | || tags.includes('project_package') |
430 | 434 | || tags.includes('community_content_mode:project_package'); |
|
433 | 437 | function typePresentation(script, languageValue = 'zh') { |
434 | 438 | const locale = languageValue === 'en' ? 'en' : 'zh'; |
435 | 439 | const category = String(script.category || '').trim().toLowerCase(); |
436 | | - const fileType = String(script.file_type || '').trim().replace(/^\./, '').toLowerCase(); |
| 440 | + const fileName = String(script.fileName || '').trim(); |
| 441 | + const fileType = String(script.fileType || script.file_type || fileName.split('.').pop() || '') |
| 442 | + .trim().replace(/^\./, '').toLowerCase(); |
437 | 443 | const tags = normalizedTags(script).map((value) => value.toLowerCase()); |
438 | 444 | const taggedRuntime = tags.find((value) => value.startsWith('community_runtime:'))?.split(':').slice(1).join(':') || ''; |
439 | | - const runtime = String(script.runtime || taggedRuntime).trim().toLowerCase(); |
| 445 | + const runtime = String(script.runtimeKind || script.runtime || taggedRuntime).trim().toLowerCase(); |
440 | 446 | const project = isProjectScript(script); |
441 | 447 | const format = project |
442 | 448 | ? (locale === 'en' ? 'project' : '项目') |
|
474 | 480 |
|
475 | 481 | function shareRevision(script, scriptID = '') { |
476 | 482 | const source = String( |
477 | | - script?.content_hash || script?.updated_at || script?.approved_at || script?.version || scriptID || 'preview', |
| 483 | + script?.revision || script?.updatedAt || script?.content_hash || script?.updated_at |
| 484 | + || script?.approved_at || script?.version || scriptID || 'preview', |
478 | 485 | ); |
479 | 486 | let hash = 2166136261; |
480 | 487 | for (const character of source) { |
|
485 | 492 | } |
486 | 493 |
|
487 | 494 | function shareImageURL(script, scriptID) { |
488 | | - const cover = safeImageURL(script?.cover_image_url || script?.preview_image_url || script?.thumbnail_url); |
| 495 | + const cover = safeImageURL( |
| 496 | + script?.coverURL || script?.cover_image_url || script?.preview_image_url || script?.thumbnail_url, |
| 497 | + ); |
489 | 498 | if (cover) return cover; |
490 | 499 | return `${SITE_ORIGIN}/og/script/${encodeURIComponent(scriptID)}.png?v=${shareRevision(script, scriptID)}`; |
491 | 500 | } |
492 | 501 |
|
493 | 502 | function socialDescription(script) { |
494 | | - return String(script.ai_summary || script.summary || DEFAULT_DESCRIPTION).trim().slice(0, 150) || DEFAULT_DESCRIPTION; |
| 503 | + return String(script.ai_summary || script.summary || script.body || DEFAULT_DESCRIPTION) |
| 504 | + .trim().slice(0, 150) || DEFAULT_DESCRIPTION; |
495 | 505 | } |
496 | 506 |
|
497 | 507 | function hostnameFor(value) { |
|
792 | 802 |
|
793 | 803 | function renderAuthor(script) { |
794 | 804 | const fallbackAuthor = language === 'en' ? 'Community creator' : '社区创作者'; |
795 | | - const author = String(script.author_name || fallbackAuthor).trim() || fallbackAuthor; |
796 | | - const date = formatDate(script.updated_at || script.approved_at || script.created_at); |
| 805 | + const author = String(script.author?.name || script.authorName || script.author_name || fallbackAuthor) |
| 806 | + .trim() || fallbackAuthor; |
| 807 | + const date = formatDate(script.updatedAt || script.updated_at || script.approved_at || script.createdAt || script.created_at); |
797 | 808 | setText(el.authorName, author); |
798 | 809 | setText(el.authorDetail, tr('updated')(date)); |
799 | 810 | setText(el.authorInitial, Array.from(author)[0] || 'P'); |
800 | 811 | el.authorRow.hidden = false; |
801 | 812 | el.authorAvatar.hidden = true; |
802 | 813 | el.authorInitial.hidden = false; |
803 | 814 |
|
804 | | - const avatarID = String(script.author_avatar_id || '').trim(); |
805 | | - const explicitAvatar = safeImageURL(script.author_avatar_url || script.avatar_url || script.user_avatar_url); |
| 815 | + const avatarID = String(script.author?.avatarSeed || script.author_avatar_id || '').trim(); |
| 816 | + const explicitAvatar = safeImageURL( |
| 817 | + script.author?.avatarURL || script.author_avatar_url || script.avatar_url || script.user_avatar_url, |
| 818 | + ); |
806 | 819 | const avatarURL = explicitAvatar || (avatarID |
807 | 820 | ? `https://api.dicebear.com/9.x/adventurer/png?seed=${encodeURIComponent(avatarID)}&size=160` |
808 | 821 | : ''); |
|
862 | 875 |
|
863 | 876 | function renderProject(script, presentation) { |
864 | 877 | const facts = [ |
865 | | - script.entry_file ? `${language === 'en' ? 'Entry' : '入口'} ${script.entry_file}` : '', |
866 | | - Number(script.package_file_count) > 0 |
867 | | - ? `${script.package_file_count} ${language === 'en' ? 'files' : '个文件'}` |
| 878 | + (script.entryFile || script.entry_file) |
| 879 | + ? `${language === 'en' ? 'Entry' : '入口'} ${script.entryFile || script.entry_file}` |
868 | 880 | : '', |
869 | | - formatBytes(script.package_size_bytes), |
| 881 | + Number(script.packageFileCount ?? script.package_file_count) > 0 |
| 882 | + ? `${script.packageFileCount ?? script.package_file_count} ${language === 'en' ? 'files' : '个文件'}` |
| 883 | + : '', |
| 884 | + formatBytes(script.packageSizeBytes ?? script.package_size_bytes), |
870 | 885 | ].filter(Boolean); |
871 | 886 | renderPoster(presentation, facts); |
872 | 887 | } |
|
891 | 906 | function renderScriptStats(script) { |
892 | 907 | const locale = language === 'en' ? 'en-US' : 'zh-CN'; |
893 | 908 | renderStats([ |
894 | | - { value: formatCount(script.view_count, locale), label: tr('views') }, |
895 | | - { value: formatCount(script.like_count, locale), label: tr('likes') }, |
896 | | - { value: formatCount(script.run_count, locale), label: tr('runs') }, |
897 | | - { value: formatCount(script.download_count, locale), label: tr('imports') }, |
| 909 | + { value: formatCount(script.viewCount ?? script.view_count, locale), label: tr('views') }, |
| 910 | + { value: formatCount(script.likeCount ?? script.like_count, locale), label: tr('likes') }, |
| 911 | + { value: formatCount(script.runCount ?? script.run_count, locale), label: tr('runs') }, |
| 912 | + { value: formatCount(script.downloadCount ?? script.download_count, locale), label: tr('imports') }, |
898 | 913 | ]); |
899 | 914 | } |
900 | 915 |
|
|
907 | 922 | const title = String(script.title || tr('community')).trim() || tr('community'); |
908 | 923 | const description = socialDescription(script); |
909 | 924 | const presentation = typePresentation(script, language); |
910 | | - const coverURL = safeImageURL(script.cover_image_url || script.preview_image_url || script.thumbnail_url); |
911 | | - const lines = previewLines(script.content, 34, 8000); |
| 925 | + const coverURL = safeImageURL( |
| 926 | + script.coverURL || script.cover_image_url || script.preview_image_url || script.thumbnail_url, |
| 927 | + ); |
| 928 | + const lines = previewLines(script.sourcePreview || script.content, 34, 8000); |
912 | 929 |
|
913 | 930 | setText(el.eyebrow, tr('communityWork')); |
914 | 931 | setText(el.title, title); |
|
946 | 963 | renderScriptStats(script); |
947 | 964 |
|
948 | 965 | updatePageMetadata(title, description, shareImageURL(script, route.id)); |
949 | | - setText(el.actionDescription, script.ai_usage_hint || tr('actionDescription')); |
| 966 | + setText(el.actionDescription, script.aiUsageHint || script.ai_usage_hint || tr('actionDescription')); |
950 | 967 | if (!coverURL) setLoading(false); |
951 | 968 | } |
952 | 969 |
|
|
986 | 1003 | if (!node) return null; |
987 | 1004 | try { |
988 | 1005 | const script = JSON.parse(node.textContent || '{}'); |
989 | | - return String(script?.script_id || '') === String(scriptID || '') ? script : null; |
| 1006 | + return String(script?.id || script?.script_id || '') === String(scriptID || '') ? script : null; |
990 | 1007 | } catch { |
991 | 1008 | return null; |
992 | 1009 | } |
|
1005 | 1022 | } |
1006 | 1023 | } |
1007 | 1024 |
|
1008 | | - async function loadScript(initialScript = null) { |
| 1025 | + async function loadScript(initialScript = null, forceRemote = false) { |
1009 | 1026 | currentView = 'script-loading'; |
1010 | 1027 | hideStatus(); |
1011 | 1028 | el.coverImage.onload = null; |
|
1014 | 1031 | const hasInitialScript = Boolean(initialScript); |
1015 | 1032 | if (hasInitialScript) renderScript(initialScript); |
1016 | 1033 | else setLoading(true); |
1017 | | - retryAction = () => loadScript(initialScript); |
| 1034 | + retryAction = () => loadScript(null, true); |
| 1035 | + if (hasInitialScript && !forceRemote) return; |
1018 | 1036 | const controller = new AbortController(); |
1019 | 1037 | const timeout = setTimeout(() => controller.abort(), 20000); |
1020 | 1038 | try { |
1021 | | - const response = await fetch(`${API_BASE}/v1/scripts/${encodeURIComponent(route.id)}`, { |
| 1039 | + const response = await fetch( |
| 1040 | + `${COMMUNITY_API_BASE}/works/${encodeURIComponent(route.id)}/share`, |
| 1041 | + { |
1022 | 1042 | headers: { Accept: 'application/json' }, |
1023 | 1043 | signal: controller.signal, |
1024 | | - }); |
| 1044 | + }, |
| 1045 | + ); |
1025 | 1046 | if (!response.ok) throw new Error(`HTTP ${response.status}`); |
1026 | 1047 | const payload = await response.json(); |
1027 | | - if (!payload || !payload.script) throw new Error('Missing script'); |
1028 | | - renderScript(payload.script); |
| 1048 | + if (!payload?.data || String(payload.data.id || '') !== String(route.id || '')) { |
| 1049 | + throw new Error('Missing work share payload'); |
| 1050 | + } |
| 1051 | + renderScript(payload.data); |
1029 | 1052 | } catch { |
1030 | 1053 | if (!hasInitialScript) renderScriptError(); |
1031 | 1054 | } finally { |
|
0 commit comments