diff --git a/src/api/status.ts b/src/api/status.ts
index c1c0170..efd083b 100644
--- a/src/api/status.ts
+++ b/src/api/status.ts
@@ -163,7 +163,6 @@ const populateTweetProperties = async (
if (tweet.card) {
const card = renderCard(tweet.card);
if (card.external_media) {
- apiTweet.twitter_card = 'summary_large_image';
apiTweet.media = apiTweet.media || {};
apiTweet.media.external = card.external_media;
}
@@ -172,6 +171,12 @@ const populateTweetProperties = async (
}
}
+ /* Workaround: Force player card by default for videos */
+ // @ts-expect-error Inexplicably, twitter_card becomes type of 'tweet' instead of 'tweet' | 'summary' | 'summary_large_image' | 'player'
+ if (apiTweet.media?.videos && apiTweet.twitter_card !== 'player') {
+ apiTweet.twitter_card = 'player';
+ }
+
/* If a language is specified in API or by user, let's try translating it! */
if (
typeof language === 'string' &&
diff --git a/src/embed/status.ts b/src/embed/status.ts
index e1a9aa7..384f99b 100644
--- a/src/embed/status.ts
+++ b/src/embed/status.ts
@@ -1,6 +1,6 @@
import { Constants } from '../constants';
import { handleQuote } from '../helpers/quote';
-import { formatNumber, sanitizeText } from '../helpers/utils';
+import { formatNumber, sanitizeText, truncateWithEllipsis } from '../helpers/utils';
import { Strings } from '../strings';
import { getAuthorText } from '../helpers/author';
import { statusAPI } from '../api/status';
@@ -121,9 +121,6 @@ export const handleStatus = async (
``,
``,
``,
- ``,
``,
``,
``
@@ -201,6 +198,10 @@ export const handleStatus = async (
if (instructions.siteName) {
siteName = instructions.siteName;
}
+ /* Overwrite our Twitter Card if overriding media, so it displays correctly in Discord */
+ if (tweet.twitter_card === 'player') {
+ tweet.twitter_card = 'summary_large_image';
+ }
break;
case 'video':
instructions = renderVideo(
@@ -214,6 +215,10 @@ export const handleStatus = async (
if (instructions.siteName) {
siteName = instructions.siteName;
}
+ /* Overwrite our Twitter Card if overriding media, so it displays correctly in Discord */
+ if (tweet.twitter_card !== 'player') {
+ tweet.twitter_card = 'player';
+ }
/* This Tweet has a video to render. */
break;
}
@@ -349,7 +354,10 @@ export const handleStatus = async (
headers.push(
``,
``,
- ``
+ ``,
+ ``,
);
/* Special reply handling if authorText is not overriden */
@@ -366,13 +374,15 @@ export const handleStatus = async (
/* The additional oembed is pulled by Discord to enable improved embeds.
Telegram does not use this. */
headers.push(
- ``
+ ``.format({
+ base: Constants.HOST_URL,
+ text: encodeURIComponent(truncateWithEllipsis(authorText, 250)),
+ deprecatedFlag: flags?.deprecated ? '&deprecated=true' : '',
+ status: encodeURIComponent(status),
+ author: encodeURIComponent(tweet.author?.screen_name || ''),
+ useXBranding: flags?.isXDomain ? 'true' : 'false',
+ name: tweet.author.name || ''
+ })
);
/* When dealing with a Tweet of unknown lang, fall back to en */
diff --git a/src/helpers/utils.ts b/src/helpers/utils.ts
index 4568b61..0b52713 100644
--- a/src/helpers/utils.ts
+++ b/src/helpers/utils.ts
@@ -15,6 +15,8 @@ export const unescapeText = (text: string) => {
.replace(/&/g, '&');
};
+export const truncateWithEllipsis = (str: string, maxLength: number): string => str.length > maxLength ? str.substring(0, maxLength - 1) + '…' : str;
+
const numberFormat = new Intl.NumberFormat('en-US');
export const formatNumber = (num: number) => numberFormat.format(num);
diff --git a/src/render/video.ts b/src/render/video.ts
index a1d751e..7c511f5 100644
--- a/src/render/video.ts
+++ b/src/render/video.ts
@@ -46,9 +46,10 @@ export const renderVideo = (
/* Push the raw video-related headers */
instructions.addHeaders = [
- ``,
``,
``,
+ ``,
+ ``,
``,
``,
``,
diff --git a/src/server.ts b/src/server.ts
index 4e94eb5..8634e80 100644
--- a/src/server.ts
+++ b/src/server.ts
@@ -442,10 +442,12 @@ router.get('/:prefix?/:handle/status/:id', statusRequest);
router.get('/:prefix?/:handle/status/:id/photo/:mediaNumber', statusRequest);
router.get('/:prefix?/:handle/status/:id/photos/:mediaNumber', statusRequest);
router.get('/:prefix?/:handle/status/:id/video/:mediaNumber', statusRequest);
+router.get('/:prefix?/:handle/status/:id/videos/:mediaNumber', statusRequest);
router.get('/:prefix?/:handle/statuses/:id', statusRequest);
router.get('/:prefix?/:handle/statuses/:id/photo/:mediaNumber', statusRequest);
router.get('/:prefix?/:handle/statuses/:id/photos/:mediaNumber', statusRequest);
router.get('/:prefix?/:handle/statuses/:id/video/:mediaNumber', statusRequest);
+router.get('/:prefix?/:handle/statuses/:id/videos/:mediaNumber', statusRequest);
router.get('/:prefix?/:handle/status/:id/:language', statusRequest);
router.get('/:prefix?/:handle/statuses/:id/:language', statusRequest);
router.get('/status/:id', statusRequest);