mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
Merge branch 'master' of github.com:bitsocialnet/5chan
This commit is contained in:
+1
-1
@@ -71,7 +71,7 @@
|
||||
"build:fdroid": "cross-env PUBLIC_URL=./ GENERATE_SOURCEMAP=false VITE_APP_DISTRIBUTION=fdroid vite build",
|
||||
"build:preload": "vite build --config electron/vite.preload.config.js",
|
||||
"build-vercel": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" PUBLIC_URL=./ GENERATE_SOURCEMAP=true VITE_COMMIT_REF=$COMMIT_REF CI='' vite build",
|
||||
"test": "vitest",
|
||||
"test": "vitest --maxWorkers=4",
|
||||
"test:leaks": "vitest run --detectAsyncLeaks",
|
||||
"preview": "vite preview",
|
||||
"analyze-bundle": "cross-env PUBLIC_URL=./ GENERATE_SOURCEMAP=true vite build && npx source-map-explorer 'dist/assets/*.js'",
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "حظر",
|
||||
"timestamp": "الطابع الزمني",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "فيديو YouTube"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "নিষেধ করুন",
|
||||
"timestamp": "টাইমস্ট্যাম্প",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube ভিডিও"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "zakázat",
|
||||
"timestamp": "časové razítko",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "video YouTube"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "forbyd",
|
||||
"timestamp": "tidsstempel",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube-video"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "sperren",
|
||||
"timestamp": "Zeitstempel",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube-Video"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "απαγόρευση",
|
||||
"timestamp": "χρονοσήμανση",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "βίντεο YouTube"
|
||||
}
|
||||
|
||||
@@ -418,5 +418,6 @@
|
||||
"ban": "ban",
|
||||
"timestamp": "timestamp",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube video"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "prohibir",
|
||||
"timestamp": "marca de tiempo",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "video de YouTube"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "مسدود کردن",
|
||||
"timestamp": "مهر زمان",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "ویدیوی YouTube"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "kielto",
|
||||
"timestamp": "aikaleimaustiedosto",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube-video"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "ipagbawal",
|
||||
"timestamp": "timestamp",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "video sa YouTube"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "interdire",
|
||||
"timestamp": "horodatage",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "vidéo YouTube"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "חסימה",
|
||||
"timestamp": "חותמת זמן",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "סרטון YouTube"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "प्रतिबंध",
|
||||
"timestamp": "टाइमस्टैंप",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube वीडियो"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "tiltás",
|
||||
"timestamp": "időbélyeg",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube-videó"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "larangan",
|
||||
"timestamp": "stempel waktu",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "video YouTube"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "vietare",
|
||||
"timestamp": "marca temporale",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "video YouTube"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "ブロック",
|
||||
"timestamp": "タイムスタンプ",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube動画"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "차단",
|
||||
"timestamp": "타임스탬프",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube 동영상"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "प्रतिबंध",
|
||||
"timestamp": "टाइमस्टॅम्प",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube व्हिडिओ"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "verbannen",
|
||||
"timestamp": "tijdstempel",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube-video"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "forbud",
|
||||
"timestamp": "tidsstempel",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube-video"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "zbanować",
|
||||
"timestamp": "znacznik czasu",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "wideo YouTube"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "banir",
|
||||
"timestamp": "marca de tempo",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "vídeo do YouTube"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "interzice",
|
||||
"timestamp": "marcă de timp",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "video YouTube"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "блокировка",
|
||||
"timestamp": "временная метка",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "видео YouTube"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "ndalim",
|
||||
"timestamp": "vulë kohe",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "video YouTube"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "förbjud",
|
||||
"timestamp": "tidsstämpel",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube-video"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "నిషేధం",
|
||||
"timestamp": "టైమ్స్టాంప్",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube వీడియో"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "แบน",
|
||||
"timestamp": "แสตมป์เวลา",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "วิดีโอ YouTube"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "yasakla",
|
||||
"timestamp": "zaman damgası",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube videosu"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "заблокувати",
|
||||
"timestamp": "часова мітка",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "відео YouTube"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "روک",
|
||||
"timestamp": "ٹائم سٹیمپ",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube ویڈیو"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "cấm",
|
||||
"timestamp": "dấu thời gian",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "video YouTube"
|
||||
}
|
||||
|
||||
@@ -399,5 +399,6 @@
|
||||
"ban": "封禁",
|
||||
"timestamp": "时间戳",
|
||||
"tag": "tag",
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled."
|
||||
"post_form_flash_upload_prompt": "Recommended SWF host: <catbox>Catbox</catbox>. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.",
|
||||
"youtube_video": "YouTube 视频"
|
||||
}
|
||||
|
||||
@@ -151,6 +151,8 @@ vi.mock('../../lib/utils/media-utils', () => ({
|
||||
getDisplayMediaInfoType: (type?: string) => type ?? 'unknown',
|
||||
getHasThumbnail: () => true,
|
||||
getMediaDimensions: () => '100x100',
|
||||
getPostMediaTypeLabel: (_commentMediaInfo: unknown, resolvedType?: string) => resolvedType ?? '',
|
||||
getYouTubeEmbedPostMediaFileLink: () => undefined,
|
||||
}));
|
||||
|
||||
vi.mock('../../lib/utils/post-utils', () => ({
|
||||
@@ -171,6 +173,7 @@ vi.mock('../../lib/utils/pending-approval-moderation', () => ({
|
||||
|
||||
vi.mock('../../lib/utils/url-utils', () => ({
|
||||
isValidURL: () => true,
|
||||
parseHttpUrl: (value: string) => new URL(value),
|
||||
}));
|
||||
|
||||
vi.mock('../../lib/utils/view-utils', () => ({
|
||||
|
||||
@@ -295,6 +295,21 @@ describe('CommentMedia', () => {
|
||||
expect(setShowThumbnailMock).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('renders a youtube thumbnail before the embed is opened', async () => {
|
||||
await renderMedia({
|
||||
commentMediaInfo: {
|
||||
patternThumbnailUrl: 'https://img.youtube.com/vi/abc123/0.jpg',
|
||||
type: 'iframe',
|
||||
url: 'https://www.youtube.com/watch?v=abc123',
|
||||
},
|
||||
setShowThumbnail: setShowThumbnailMock,
|
||||
showThumbnail: true,
|
||||
});
|
||||
|
||||
expect(container.querySelector('img[src="https://img.youtube.com/vi/abc123/0.jpg"]')).toBeTruthy();
|
||||
expect(container.querySelector('[data-testid="embed"]')).toBeNull();
|
||||
});
|
||||
|
||||
it('renders the expanded embed view with a close button on mobile', async () => {
|
||||
testState.isMobile = true;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { createElement } from 'react';
|
||||
import { createRoot, type Root } from 'react-dom/client';
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||
import Embed from '../embed';
|
||||
import { canEmbed } from '../embed-utils';
|
||||
import { canEmbed, getYouTubeVideoId } from '../embed-utils';
|
||||
|
||||
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
const act = (React as { act?: (cb: () => void | Promise<void>) => void | Promise<void> }).act as (cb: () => void | Promise<void>) => void | Promise<void>;
|
||||
@@ -56,6 +56,18 @@ describe('Embed', () => {
|
||||
expect(container.innerHTML).toBe('');
|
||||
});
|
||||
|
||||
it('extracts youtube video ids for standard, short, and invidious urls', () => {
|
||||
expect(getYouTubeVideoId(new URL('https://www.youtube.com/watch?v=abc123'))).toBe('abc123');
|
||||
expect(getYouTubeVideoId(new URL('https://m.youtube.com/watch?v=mobile123'))).toBe('mobile123');
|
||||
expect(getYouTubeVideoId(new URL('https://music.youtube.com/watch?v=music123'))).toBe('music123');
|
||||
expect(getYouTubeVideoId(new URL('https://youtu.be/short123'))).toBe('short123');
|
||||
expect(getYouTubeVideoId(new URL('https://www.youtube.com/shorts/short456'))).toBe('short456');
|
||||
expect(getYouTubeVideoId(new URL('https://yewtu.be/invidious789'))).toBe('invidious789');
|
||||
expect(getYouTubeVideoId(new URL('https://m.youtube.com/@channel'))).toBeNull();
|
||||
expect(getYouTubeVideoId(new URL('https://music.youtube.com/channel/UC123'))).toBeNull();
|
||||
expect(getYouTubeVideoId(new URL('https://www.youtube.com/playlist?list=PL123'))).toBeNull();
|
||||
});
|
||||
|
||||
it('reports embeddable hosts through canEmbed and rejects unsupported reddit pages', () => {
|
||||
expect(canEmbed(new URL('https://www.youtube.com/watch?v=abc123'))).toBe(true);
|
||||
expect(canEmbed(new URL('https://yt.example/watch?v=abc123'))).toBe(true);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
const STANDARD_YOUTUBE_HOSTS = new Set<string>(['youtube.com', 'www.youtube.com', 'm.youtube.com', 'music.youtube.com', 'youtu.be', 'www.youtu.be']);
|
||||
|
||||
export const youtubeHosts = new Set<string>([
|
||||
'youtube.com',
|
||||
'www.youtube.com',
|
||||
@@ -46,6 +48,35 @@ const canEmbedHosts = new Set<string>([
|
||||
...spotifyHosts,
|
||||
]);
|
||||
|
||||
export const getYouTubeVideoId = (parsedUrl: URL): string | null => {
|
||||
if (parsedUrl.searchParams.has('list') && !parsedUrl.searchParams.get('v') && !parsedUrl.host.includes('youtu.be') && !parsedUrl.pathname.includes('/shorts/')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const videoIdFromQuery = parsedUrl.searchParams.get('v');
|
||||
if (videoIdFromQuery) {
|
||||
return videoIdFromQuery;
|
||||
}
|
||||
|
||||
if (parsedUrl.host.includes('youtu.be')) {
|
||||
return parsedUrl.pathname.slice(1).split('/')[0] || null;
|
||||
}
|
||||
|
||||
if (parsedUrl.pathname.includes('/shorts/')) {
|
||||
return parsedUrl.pathname.split('/shorts/')[1]?.split('/')[0] || null;
|
||||
}
|
||||
|
||||
const isInvidious = !STANDARD_YOUTUBE_HOSTS.has(parsedUrl.host) && (youtubeHosts.has(parsedUrl.host) || parsedUrl.host.startsWith('yt.'));
|
||||
if (isInvidious) {
|
||||
if (parsedUrl.pathname.startsWith('/watch')) {
|
||||
return parsedUrl.searchParams.get('v');
|
||||
}
|
||||
return parsedUrl.pathname.split('/').filter(Boolean).pop() || null;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const canEmbed = (parsedUrl: URL): boolean => {
|
||||
if (xHosts.has(parsedUrl.host)) {
|
||||
return Boolean(getXTweetId(parsedUrl));
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
streamableHosts,
|
||||
tiktokHosts,
|
||||
twitchHosts,
|
||||
getYouTubeVideoId,
|
||||
xHosts,
|
||||
youtubeHosts,
|
||||
} from './embed-utils';
|
||||
@@ -66,28 +67,11 @@ const remoteEmbedSandbox = `${srcDocSandbox} allow-forms allow-presentation allo
|
||||
const YoutubeEmbed = ({ parsedUrl }: EmbedComponentProps) => {
|
||||
let embedSrc = '';
|
||||
|
||||
const isInvidious = parsedUrl.host !== 'youtube.com' && parsedUrl.host !== 'www.youtube.com' && parsedUrl.host !== 'youtu.be' && parsedUrl.host !== 'www.youtu.be';
|
||||
|
||||
if (parsedUrl.searchParams.has('list')) {
|
||||
if (parsedUrl.searchParams.has('list') && !getYouTubeVideoId(parsedUrl)) {
|
||||
const playlistId = parsedUrl.searchParams.get('list');
|
||||
embedSrc = `https://www.youtube.com/embed/videoseries?list=${playlistId}`;
|
||||
} else {
|
||||
let videoId = parsedUrl.searchParams.get('v');
|
||||
|
||||
if (!videoId) {
|
||||
if (parsedUrl.host.includes('youtu.be')) {
|
||||
videoId = parsedUrl.pathname.substring(1);
|
||||
} else if (parsedUrl.pathname.includes('/shorts/')) {
|
||||
videoId = parsedUrl.pathname.split('/shorts/')[1];
|
||||
} else if (isInvidious) {
|
||||
if (parsedUrl.pathname.startsWith('/watch')) {
|
||||
videoId = parsedUrl.searchParams.get('v');
|
||||
} else {
|
||||
videoId = parsedUrl.pathname.split('/').pop() || null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const videoId = getYouTubeVideoId(parsedUrl);
|
||||
if (videoId) {
|
||||
embedSrc = `https://www.youtube.com/embed/${videoId}`;
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { default } from './embed';
|
||||
export { canEmbed } from './embed-utils';
|
||||
export { canEmbed, getYouTubeVideoId } from './embed-utils';
|
||||
|
||||
@@ -24,7 +24,9 @@ const testState = vi.hoisted(() => ({
|
||||
cidToNumber: {} as Record<string, number>,
|
||||
internalPathByHref: {} as Record<string, string | null>,
|
||||
isMobile: false,
|
||||
mediaInfoByHref: {} as Record<string, { thumbnail?: string; type: string; url: string }>,
|
||||
mediaInfoByHref: {} as Record<string, { patternThumbnailUrl?: string; thumbnail?: string; type: string; url: string }>,
|
||||
floatingMediaProps: null as { showThumbnail?: boolean } | null,
|
||||
floatingOnOpenChange: null as ((open: boolean) => void) | null,
|
||||
numberToCid: {} as Record<string, Record<number, string>>,
|
||||
unavailableCids: new Set<string>(),
|
||||
}));
|
||||
@@ -42,20 +44,32 @@ vi.mock('@floating-ui/react', () => ({
|
||||
shift: () => ({}),
|
||||
size: () => ({}),
|
||||
useDismiss: () => ({}),
|
||||
useFloating: () => ({
|
||||
context: {},
|
||||
floatingStyles: {},
|
||||
refs: {
|
||||
setFloating: () => undefined,
|
||||
setReference: () => undefined,
|
||||
},
|
||||
update: () => undefined,
|
||||
}),
|
||||
useFloating: ({ onOpenChange, open }: { onOpenChange?: (open: boolean) => void; open?: boolean }) => {
|
||||
testState.floatingOnOpenChange = onOpenChange ?? null;
|
||||
return {
|
||||
context: {},
|
||||
floatingStyles: {},
|
||||
open,
|
||||
onOpenChange,
|
||||
refs: {
|
||||
setFloating: () => undefined,
|
||||
setReference: () => undefined,
|
||||
},
|
||||
update: () => undefined,
|
||||
};
|
||||
},
|
||||
useFocus: () => ({}),
|
||||
useHover: () => ({}),
|
||||
useInteractions: () => ({
|
||||
useHover: () => ({
|
||||
getReferenceProps: (props?: Record<string, unknown>) => ({
|
||||
...props,
|
||||
onMouseEnter: () => testState.floatingOnOpenChange?.(true),
|
||||
onMouseLeave: () => testState.floatingOnOpenChange?.(false),
|
||||
}),
|
||||
}),
|
||||
useInteractions: (interactions: Array<{ getReferenceProps?: (props?: Record<string, unknown>) => Record<string, unknown> }>) => ({
|
||||
getFloatingProps: (props?: Record<string, unknown>) => props || {},
|
||||
getReferenceProps: (props?: Record<string, unknown>) => props || {},
|
||||
getReferenceProps: (props?: Record<string, unknown>) =>
|
||||
interactions.reduce((mergedProps, interaction) => interaction.getReferenceProps?.(mergedProps) || mergedProps, props || {}),
|
||||
}),
|
||||
}));
|
||||
|
||||
@@ -75,8 +89,13 @@ vi.mock('../../../hooks/use-is-mobile', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('../../../lib/utils/media-utils', () => ({
|
||||
getHasThumbnail: (linkMediaInfo?: { thumbnail?: string; type?: string }, href?: string) =>
|
||||
Boolean(linkMediaInfo?.thumbnail || linkMediaInfo?.type === 'image' || (href && testState.mediaInfoByHref[href]?.thumbnail)),
|
||||
getHasThumbnail: (linkMediaInfo?: { patternThumbnailUrl?: string; thumbnail?: string; type?: string }, href?: string) =>
|
||||
Boolean(
|
||||
linkMediaInfo?.thumbnail ||
|
||||
linkMediaInfo?.patternThumbnailUrl ||
|
||||
linkMediaInfo?.type === 'image' ||
|
||||
(href && (testState.mediaInfoByHref[href]?.thumbnail || testState.mediaInfoByHref[href]?.patternThumbnailUrl)),
|
||||
),
|
||||
getLinkMediaInfo: (href: string) => testState.mediaInfoByHref[href],
|
||||
}));
|
||||
|
||||
@@ -139,8 +158,28 @@ vi.mock('../../../stores/use-post-number-store', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('../../comment-media', () => ({
|
||||
default: ({ commentMediaInfo }: { commentMediaInfo?: { type?: string; url?: string } }) =>
|
||||
createElement('div', { 'data-testid': 'comment-media' }, `${commentMediaInfo?.type}:${commentMediaInfo?.url}`),
|
||||
default: ({
|
||||
commentMediaInfo,
|
||||
isFloatingEmbed,
|
||||
showThumbnail,
|
||||
}: {
|
||||
commentMediaInfo?: { type?: string; url?: string };
|
||||
isFloatingEmbed?: boolean;
|
||||
showThumbnail?: boolean;
|
||||
}) => {
|
||||
if (isFloatingEmbed) {
|
||||
testState.floatingMediaProps = { showThumbnail };
|
||||
}
|
||||
return createElement(
|
||||
'div',
|
||||
{
|
||||
'data-floating': String(Boolean(isFloatingEmbed)),
|
||||
'data-show-thumbnail': String(showThumbnail),
|
||||
'data-testid': 'comment-media',
|
||||
},
|
||||
`${commentMediaInfo?.type}:${commentMediaInfo?.url}`,
|
||||
);
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../embed', () => ({
|
||||
@@ -198,6 +237,8 @@ describe('Markdown', () => {
|
||||
testState.internalPathByHref = {};
|
||||
testState.isMobile = false;
|
||||
testState.mediaInfoByHref = {};
|
||||
testState.floatingMediaProps = null;
|
||||
testState.floatingOnOpenChange = null;
|
||||
testState.numberToCid = {};
|
||||
testState.unavailableCids = new Set<string>();
|
||||
|
||||
@@ -460,6 +501,33 @@ describe('Markdown', () => {
|
||||
expect(lazyLinks.map((node) => node.textContent)).toEqual(['>>42', '>>>/fit/77']);
|
||||
});
|
||||
|
||||
it('shows youtube thumbnails in the floating hover preview instead of loading the iframe', async () => {
|
||||
testState.embeddableHosts = new Set(['www.youtube.com']);
|
||||
testState.mediaInfoByHref = {
|
||||
'https://www.youtube.com/watch?v=abc123': {
|
||||
patternThumbnailUrl: 'https://img.youtube.com/vi/abc123/0.jpg',
|
||||
type: 'iframe',
|
||||
url: 'https://www.youtube.com/watch?v=abc123',
|
||||
},
|
||||
};
|
||||
|
||||
await renderMarkdown({
|
||||
content: 'https://www.youtube.com/watch?v=abc123',
|
||||
});
|
||||
|
||||
const embedButton = Array.from(container.querySelectorAll('button')).find((node) => node.textContent === 'embed');
|
||||
expect(embedButton).toBeTruthy();
|
||||
|
||||
await act(async () => {
|
||||
testState.floatingOnOpenChange?.(true);
|
||||
});
|
||||
|
||||
const floatingMedia = container.querySelector('[data-floating="true"]');
|
||||
expect(floatingMedia).toBeTruthy();
|
||||
expect(floatingMedia?.getAttribute('data-show-thumbnail')).toBe('true');
|
||||
expect(testState.floatingMediaProps?.showThumbnail).toBe(true);
|
||||
});
|
||||
|
||||
it('toggles inline media embeds for embeddable links outside catalog view', async () => {
|
||||
testState.mediaInfoByHref = {
|
||||
'https://cdn.example/image.png': {
|
||||
|
||||
@@ -123,8 +123,8 @@ const ContentLinkEmbed = ({ children, href, linkMediaInfo }: ContentLinkEmbedPro
|
||||
disableToggle={true}
|
||||
isFloatingEmbed={true}
|
||||
isReply={false}
|
||||
setShowThumbnail={setShowMedia}
|
||||
showThumbnail={false}
|
||||
setShowThumbnail={() => {}}
|
||||
showThumbnail={true}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -5,11 +5,11 @@ import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
|
||||
import { Comment, useEditedComment, useReplies, useAccount } from '@bitsocial/bitsocial-react-hooks';
|
||||
import getShortAddress from '../../lib/get-short-address';
|
||||
import styles from '../../views/post/post.module.css';
|
||||
import { CommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail, getMediaDimensions } from '../../lib/utils/media-utils';
|
||||
import { CommentMediaInfo, getHasThumbnail, getMediaDimensions, getPostMediaTypeLabel, getYouTubeEmbedPostMediaFileLink } from '../../lib/utils/media-utils';
|
||||
import { hashStringToColor, getTextColorForBackground } from '../../lib/utils/post-utils';
|
||||
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
||||
import { approvePendingCommentModeration, isPendingApprovalRejected, rejectPendingCommentModeration } from '../../lib/utils/pending-approval-moderation';
|
||||
import { isValidURL } from '../../lib/utils/url-utils';
|
||||
import { isValidURL, parseHttpUrl } from '../../lib/utils/url-utils';
|
||||
import { isAllView, isModQueueView, isModView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||
import { formatUserIDForDisplay, truncateWithEllipsisInMiddle } from '../../lib/utils/string-utils';
|
||||
import useModQueueStore from '../../stores/use-mod-queue-store';
|
||||
@@ -642,12 +642,16 @@ const PostMedia = ({
|
||||
type = 'static gif';
|
||||
}
|
||||
|
||||
const embedUrl = url && new URL(url);
|
||||
const youtubeFileLink = getYouTubeEmbedPostMediaFileLink(commentMediaInfo);
|
||||
const fileLinkUrl = youtubeFileLink ?? url;
|
||||
const embedUrl = url ? parseHttpUrl(url) : null;
|
||||
const [showThumbnail, setShowThumbnail] = useState(true);
|
||||
|
||||
const mediaDimensions = getMediaDimensions(commentMediaInfo);
|
||||
const directoryEntry = findDirectoryByAddress(directories, communityAddress);
|
||||
const requirePostLinkIsMedia = directoryEntry?.features?.requirePostLinkIsMedia === true;
|
||||
const fileLabel = youtubeFileLink || requirePostLinkIsMedia ? t('file') : t('link');
|
||||
const mediaTypeLabel = type ? lowerCase(getPostMediaTypeLabel(commentMediaInfo, type, t)) : '';
|
||||
const boardPath = communityAddress ? getBoardPath(communityAddress, directories) : undefined;
|
||||
const displayBoardPath =
|
||||
boardPath && communityAddress && boardPath !== communityAddress
|
||||
@@ -666,10 +670,11 @@ const PostMedia = ({
|
||||
{t('board')}: <Link to={`/${boardPath}`}>{displayBoardPath}</Link>{' '}
|
||||
</>
|
||||
)}
|
||||
{requirePostLinkIsMedia ? t('file') : t('link')}:{' '}
|
||||
<a href={url} target='_blank' rel='noopener noreferrer'>
|
||||
{fileLabel}:{' '}
|
||||
<a href={fileLinkUrl} target='_blank' rel='noopener noreferrer'>
|
||||
{(() => {
|
||||
if (spoiler) return capitalize(t('spoiler'));
|
||||
if (youtubeFileLink) return truncateWithEllipsisInMiddle(youtubeFileLink);
|
||||
if (requirePostLinkIsMedia && url) {
|
||||
try {
|
||||
const pathParts = new URL(url).pathname.split('/');
|
||||
@@ -677,10 +682,10 @@ const PostMedia = ({
|
||||
if (filename && /\.\w+$/.test(filename)) return truncateWithEllipsisInMiddle(filename);
|
||||
} catch {}
|
||||
}
|
||||
return truncateWithEllipsisInMiddle(url ?? '');
|
||||
return truncateWithEllipsisInMiddle(fileLinkUrl ?? '');
|
||||
})()}
|
||||
</a>{' '}
|
||||
({type && lowerCase(getDisplayMediaInfoType(type, t))}
|
||||
({mediaTypeLabel}
|
||||
{mediaDimensions && `, ${mediaDimensions}`})
|
||||
{!showThumbnail && (type === 'iframe' || type === 'video' || type === 'audio') && (
|
||||
<span>
|
||||
|
||||
@@ -20,9 +20,13 @@ vi.mock('@bitsocial/bitsocial-react-hooks/dist/lib/localforage-lru/index.js', ()
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../components/embed', () => ({
|
||||
canEmbed: (url: URL) => testState.canEmbedHosts.has(url.hostname),
|
||||
}));
|
||||
vi.mock('../../../components/embed/embed-utils', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('../../../components/embed/embed-utils')>();
|
||||
return {
|
||||
...actual,
|
||||
canEmbed: (url: URL) => testState.canEmbedHosts.has(url.hostname),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('@capacitor/core', () => ({
|
||||
Capacitor: {
|
||||
@@ -33,7 +37,16 @@ vi.mock('@capacitor/core', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
import { fetchWebpageThumbnailIfNeeded, getCommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail, getLinkMediaInfo, getMediaDimensions } from '../media-utils';
|
||||
import {
|
||||
fetchWebpageThumbnailIfNeeded,
|
||||
getCommentMediaInfo,
|
||||
getDisplayMediaInfoType,
|
||||
getHasThumbnail,
|
||||
getLinkMediaInfo,
|
||||
getMediaDimensions,
|
||||
getPostMediaTypeLabel,
|
||||
getYouTubeEmbedPostMediaFileLink,
|
||||
} from '../media-utils';
|
||||
|
||||
const clearMemoizedCache = (fn: unknown) => {
|
||||
const memoized = fn as { clear?: () => void };
|
||||
@@ -102,6 +115,18 @@ describe('media-utils', () => {
|
||||
expect(getDisplayMediaInfoType('unknown', t)).toBe('translated:webpage');
|
||||
});
|
||||
|
||||
it('uses the youtube thumbnail url for post media file links and labels', () => {
|
||||
const mediaInfo = {
|
||||
patternThumbnailUrl: 'https://img.youtube.com/vi/abc123/0.jpg',
|
||||
type: 'iframe',
|
||||
url: 'https://www.youtube.com/watch?v=abc123',
|
||||
};
|
||||
|
||||
expect(getYouTubeEmbedPostMediaFileLink(mediaInfo)).toBe('https://img.youtube.com/vi/abc123/0.jpg');
|
||||
expect(getPostMediaTypeLabel(mediaInfo, 'iframe', (key) => key)).toBe('youtube_video');
|
||||
expect(getYouTubeEmbedPostMediaFileLink({ type: 'iframe', url: 'https://streamable.com/clip123' })).toBeUndefined();
|
||||
});
|
||||
|
||||
it('recognizes which media types expose thumbnails', () => {
|
||||
expect(getHasThumbnail(undefined, 'https://example.com/file.png')).toBe(false);
|
||||
expect(getHasThumbnail({ type: 'image', url: 'https://example.com/file.png' }, 'https://example.com/file.png')).toBe(true);
|
||||
@@ -156,6 +181,12 @@ describe('media-utils', () => {
|
||||
type: 'iframe',
|
||||
url: 'https://yt.example/watch?v=yt123',
|
||||
});
|
||||
testState.canEmbedHosts = new Set(['yewtu.be']);
|
||||
expect(getLinkMediaInfo('https://yewtu.be/invidious123')).toEqual({
|
||||
patternThumbnailUrl: 'https://img.youtube.com/vi/invidious123/0.jpg',
|
||||
type: 'iframe',
|
||||
url: 'https://yewtu.be/invidious123',
|
||||
});
|
||||
});
|
||||
|
||||
it('builds comment media info and strips thumbnails for blacklisted domains', () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import localForageLru from '@bitsocial/bitsocial-react-hooks/dist/lib/localforage-lru/index.js';
|
||||
import { canEmbed } from '../../components/embed';
|
||||
import { canEmbed, getYouTubeVideoId } from '../../components/embed/embed-utils';
|
||||
import memoize from 'memoizee';
|
||||
import { isPrivateNetworkHostname, isValidURL, parseHttpUrl } from './url-utils';
|
||||
import { Capacitor, CapacitorHttp } from '@capacitor/core';
|
||||
@@ -40,6 +40,31 @@ export const getDisplayMediaInfoType = (type: string, t: Translate) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getYouTubeEmbedPostMediaFileLink = (commentMediaInfo: CommentMediaInfo | undefined): string | undefined => {
|
||||
if (!commentMediaInfo || commentMediaInfo.type !== 'iframe' || !commentMediaInfo.url) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const parsedUrl = parseHttpUrl(commentMediaInfo.url);
|
||||
if (!parsedUrl || !getYouTubeVideoId(parsedUrl)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return commentMediaInfo.patternThumbnailUrl || getPatternThumbnailUrl(parsedUrl);
|
||||
};
|
||||
|
||||
export const getPostMediaTypeLabel = (commentMediaInfo: CommentMediaInfo | undefined, resolvedType: string | undefined, t: Translate): string => {
|
||||
if (getYouTubeEmbedPostMediaFileLink(commentMediaInfo)) {
|
||||
return t('youtube_video');
|
||||
}
|
||||
|
||||
if (!resolvedType) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return getDisplayMediaInfoType(resolvedType, t);
|
||||
};
|
||||
|
||||
export const getHasThumbnail = memoize(
|
||||
(commentMediaInfo: CommentMediaInfo | undefined, link: string | undefined): boolean => {
|
||||
if (!link || !commentMediaInfo) return false;
|
||||
@@ -55,17 +80,6 @@ export const getHasThumbnail = memoize(
|
||||
{ max: 1000 },
|
||||
);
|
||||
|
||||
const getYouTubeVideoId = (url: URL): string | null => {
|
||||
if (url.host.includes('youtu.be')) {
|
||||
return url.pathname.slice(1);
|
||||
} else if (url.pathname.includes('/shorts/')) {
|
||||
return url.pathname.split('/shorts/')[1].split('/')[0];
|
||||
} else if (url.searchParams.has('v')) {
|
||||
return url.searchParams.get('v');
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const getPatternThumbnailUrl = (url: URL): string | undefined => {
|
||||
const videoId = getYouTubeVideoId(url);
|
||||
if (videoId) {
|
||||
|
||||
Reference in New Issue
Block a user