diff --git a/docs/api.md b/docs/api.md index b63c9216a..2b23e2c25 100644 --- a/docs/api.md +++ b/docs/api.md @@ -35,14 +35,15 @@ Content-Type: application/json | `tiktokH265` | `boolean` | `true / false` | `false` | changes whether 1080p h265 videos are preferred or not. | ### response body variables -| key | type | variables | -|:-------------|:---------|:------------------------------------------------------------| -| `status` | `string` | `error / redirect / stream / success / rate-limit / picker` | -| `text` | `string` | various text, mostly used for errors | -| `url` | `string` | direct link to a file or a link to cobalt's live render | -| `pickerType` | `string` | `various / images` | -| `picker` | `array` | array of picker items | -| `audio` | `string` | direct link to a file or a link to cobalt's live render | +| key | type | variables | +|:----------------|:---------|:------------------------------------------------------------| +| `status` | `string` | `error / redirect / stream / success / rate-limit / picker` | +| `text` | `string` | various text, mostly used for errors | +| `url` | `string` | direct link to a file or a link to cobalt's live render | +| `pickerType` | `string` | `various / images` | +| `picker` | `array` | array of picker items | +| `audio` | `string` | direct link to a file or a link to cobalt's live render | +| `mediaMetadata` | `object` | Supported only on YouTube and Twitter videos. Object, that contains values `duration` (duration of the video in seconds), `likes`, `views`, `title` | ### picker item variables item type: `object` diff --git a/src/modules/processing/matchActionDecider.js b/src/modules/processing/matchActionDecider.js index 74f0f8c77..2f6c7c8a8 100644 --- a/src/modules/processing/matchActionDecider.js +++ b/src/modules/processing/matchActionDecider.js @@ -14,6 +14,7 @@ export default function(r, host, userFormat, isAudioOnly, lang, isAudioMuted, di filename: r.filenameAttributes ? createFilename(r.filenameAttributes, filenamePattern, isAudioOnly, isAudioMuted) : r.filename, fileMetadata: !disableMetadata ? r.fileMetadata : false, + mediaMetadata: r.mediaMetadata, requestIP }, params = {}, diff --git a/src/modules/processing/request.js b/src/modules/processing/request.js index f2f58c65c..269ea5043 100644 --- a/src/modules/processing/request.js +++ b/src/modules/processing/request.js @@ -59,13 +59,15 @@ export function createResponse(responseType, responseData) { case "redirect": response = { - url: responseData.u + url: responseData.u, + mediaMetadata: responseData.mediaMetadata, } break; case "stream": response = { - url: createStream(responseData) + url: createStream(responseData), + mediaMetadata: responseData.mediaMetadata, } break; diff --git a/src/modules/processing/services/twitter.js b/src/modules/processing/services/twitter.js index 36a8669bd..5add46200 100644 --- a/src/modules/processing/services/twitter.js +++ b/src/modules/processing/services/twitter.js @@ -36,6 +36,17 @@ function bestQuality(arr) { .url } +function buildMediaMetadata(tweetResult, media){ + return { + duration: Math.round(media.video_info.duration_millis / 1000) || 0, + likes: tweetResult.legacy.favorite_count || 0, + views: Number(tweetResult.views.count) || 0, + title: (tweetResult.legacy && tweetResult.legacy.full_text && Array.isArray(tweetResult.legacy.display_text_range) && tweetResult.legacy.display_text_range[0] !== undefined && tweetResult.legacy.display_text_range[1] !== undefined) + ? tweetResult.legacy.full_text.substr(tweetResult.legacy.display_text_range[0], tweetResult.legacy.display_text_range[1] - tweetResult.legacy.display_text_range[0]) + : undefined + } +} + let _cachedToken; const getGuestToken = async (dispatcher, forceReload = false) => { if (_cachedToken && !forceReload) { @@ -164,7 +175,8 @@ export default async function({ id, index, toGif, dispatcher }) { urls: bestQuality(media[0].video_info.variants), filename: `twitter_${id}.mp4`, audioFilename: `twitter_${id}_audio`, - isGif: media[0].type === "animated_gif" + isGif: media[0].type === "animated_gif", + mediaMetadata: buildMediaMetadata(tweetResult, media[0]) }; default: const picker = media.map((content, i) => { @@ -184,6 +196,7 @@ export default async function({ id, index, toGif, dispatcher }) { type: 'video', url, thumb: content.media_url_https, + mediaMetadata: buildMediaMetadata(tweetResult, content) } }); return { picker }; diff --git a/src/modules/processing/services/vk.js b/src/modules/processing/services/vk.js index e95f12bec..fbc6fef27 100644 --- a/src/modules/processing/services/vk.js +++ b/src/modules/processing/services/vk.js @@ -49,6 +49,12 @@ export default async function(o) { resolution: `${quality}p`, qualityLabel: `${quality}p`, extension: "mp4" + }, + mediaMetadata: { + duration: js.player.params[0].duration, + likes: js.mvData.likes, + views: js.videoModalInfoData.views, + title: js.mvData.title } } return { error: 'ErrorEmptyDownload' } diff --git a/src/modules/processing/services/youtube.js b/src/modules/processing/services/youtube.js index 1c86b4ed0..bb630dca2 100644 --- a/src/modules/processing/services/youtube.js +++ b/src/modules/processing/services/youtube.js @@ -218,16 +218,26 @@ export default async function(o) { urls = [video.decipher(yt.session.player), audio.decipher(yt.session.player)]; } + const mediaMetadata = { + duration: info.basic_info.duration, + likes: info.basic_info.like_count, + views: info.basic_info.view_count, + title: info.basic_info.title, + }; + if (match) { filenameAttributes.qualityLabel = match.quality_label; filenameAttributes.resolution = `${match.width}x${match.height}`; filenameAttributes.extension = codecMatch[format].container; filenameAttributes.youtubeFormat = format; + + return { type, urls, filenameAttributes, - fileMetadata + fileMetadata, + mediaMetadata, } }