mirror of
https://github.com/imputnet/cobalt.git
synced 2026-01-25 16:21:38 +00:00
twitter spaces and a ton of improvements
This commit is contained in:
@@ -15,7 +15,7 @@ export default async function(obj) {
|
||||
let streamData = JSON.parse(html.split('<script>window.__playinfo__=')[1].split('</script>')[0]);
|
||||
if (streamData.data.timelength <= maxVideoDuration) {
|
||||
let video = streamData["data"]["dash"]["video"].filter((v) => {
|
||||
if (!v["baseUrl"].includes("https://upos-sz-mirrorcosov.bilivideo.com/") && v["height"] != 4320) return true;
|
||||
if (!v["baseUrl"].includes("https://upos-sz-mirrorcosov.bilivideo.com/") && v["height"] !== 4320) return true;
|
||||
}).sort((a, b) => Number(b.bandwidth) - Number(a.bandwidth));
|
||||
let audio = streamData["data"]["dash"]["audio"].filter((a) => {
|
||||
if (!a["baseUrl"].includes("https://upos-sz-mirrorcosov.bilivideo.com/")) return true;
|
||||
|
||||
@@ -7,30 +7,28 @@ export default async function(obj) {
|
||||
let html;
|
||||
if (!obj.author && !obj.song && obj.shortLink) {
|
||||
html = await got.get(`https://soundcloud.app.goo.gl/${obj.shortLink}/`, { headers: { "user-agent": genericUserAgent } });
|
||||
html.on('error', (err) => {
|
||||
return { error: loc(obj.lang, 'ErrorCouldntFetch', 'soundcloud') };
|
||||
});
|
||||
html = html.body
|
||||
}
|
||||
if (obj.author && obj.song) {
|
||||
html = await got.get(`https://soundcloud.com/${obj.author}/${obj.song}`, { headers: { "user-agent": genericUserAgent } });
|
||||
html.on('error', (err) => {
|
||||
return { error: loc(obj.lang, 'ErrorCouldntFetch', 'soundcloud') };
|
||||
});
|
||||
html = html.body
|
||||
}
|
||||
if (html.includes('<script>window.__sc_hydration = ') && html.includes('"format":{"protocol":"progressive","mime_type":"audio/mpeg"},') && html.includes('{"hydratable":"sound","data":')) {
|
||||
let json = JSON.parse(html.split('{"hydratable":"sound","data":')[1].split('}];</script>')[0])
|
||||
if (json["media"]["transcodings"]) {
|
||||
let fileUrl = `${json.media.transcodings[0]["url"].replace("/hls", "/progressive")}?client_id=${services["soundcloud"]["clientid"]}&track_authorization=${json.track_authorization}`;
|
||||
if (fileUrl.substring(0, 54) == "https://api-v2.soundcloud.com/media/soundcloud:tracks:") {
|
||||
if ((json.duration < maxAudioDuration) || obj.format == "best" || obj.format == "mp3") {
|
||||
if (fileUrl.substring(0, 54) === "https://api-v2.soundcloud.com/media/soundcloud:tracks:") {
|
||||
if (json.duration < maxAudioDuration) {
|
||||
let file = await got.get(fileUrl, { headers: { "user-agent": genericUserAgent } });
|
||||
file.on('error', (err) => {
|
||||
return { error: loc(obj.lang, 'ErrorCouldntFetch', 'soundcloud') };
|
||||
});
|
||||
file = JSON.parse(file.body).url
|
||||
return { urls: file, audioFilename: `soundcloud_${json.id}` }
|
||||
return {
|
||||
urls: file,
|
||||
audioFilename: `soundcloud_${json.id}`,
|
||||
fileMetadata: {
|
||||
title: json.title,
|
||||
artist: json.user.username,
|
||||
}
|
||||
}
|
||||
} else return { error: loc(obj.lang, 'ErrorLengthAudioConvert', maxAudioDuration / 60000) }
|
||||
}
|
||||
} else return { error: loc(obj.lang, 'ErrorEmptyDownload') }
|
||||
|
||||
@@ -7,7 +7,7 @@ let userAgent = genericUserAgent.split(' Chrome/1')[0]
|
||||
let config = {
|
||||
tiktok: {
|
||||
short: "https://vt.tiktok.com/",
|
||||
api: "https://api.tiktokv.com/aweme/v1/feed/?aweme_id={postId}&version_code=262&app_name=musical_ly&channel=App&device_id=null&os_version=14.4.2&device_platform=iphone&device_type=iPhone9", // thanks to https://github.com/wukko/cobalt/pull/41#issue-1380090574
|
||||
api: "https://api2.musical.ly/aweme/v1/feed/?aweme_id={postId}&version_code=262&app_name=musical_ly&channel=App&device_id=null&os_version=14.4.2&device_platform=iphone&device_type=iPhone9", // ill always find more endpoints lmfao
|
||||
},
|
||||
douyin: {
|
||||
short: "https://v.douyin.com/",
|
||||
@@ -27,37 +27,37 @@ export default async function(obj) {
|
||||
try {
|
||||
if (!obj.postId) {
|
||||
let html = await got.get(`${config[obj.host]["short"]}${obj.id}`, { followRedirect: false, headers: { "user-agent": userAgent } });
|
||||
html.on('error', (err) => {
|
||||
return { error: loc(obj.lang, 'ErrorCantConnectToServiceAPI', obj.host) };
|
||||
});
|
||||
html = html.body;
|
||||
if (html.slice(0, 17) === '<a href="https://' && html.includes('/video/')) obj.postId = html.split('video/')[1].split('?')[0].replace("/", '')
|
||||
}
|
||||
if (!obj.postId) return { error: loc(obj.lang, 'ErrorCantGetID') };
|
||||
|
||||
let detail = await got.get(config[obj.host]["api"].replace("{postId}", obj.postId), { headers: {"User-Agent":"TikTok 26.2.0 rv:262018 (iPhone; iOS 14.4.2; en_US) Cronet"} });
|
||||
detail.on('error', (err) => {
|
||||
return { error: loc(obj.lang, 'ErrorCantConnectToServiceAPI', obj.host) };
|
||||
});
|
||||
detail = selector(JSON.parse(detail.body), obj.host);
|
||||
let detail;
|
||||
try {
|
||||
detail = await got.get(config[obj.host]["api"].replace("{postId}", obj.postId), { headers: {"User-Agent":"TikTok 26.2.0 rv:262018 (iPhone; iOS 14.4.2; en_US) Cronet"} });
|
||||
detail = selector(JSON.parse(detail.body), obj.host);
|
||||
} catch (e) {
|
||||
if (obj.host === "tiktok") {
|
||||
let html = await got.get(`https://tiktok.com/@video/video/${obj.postId}`, { headers: { "user-agent": userAgent } });
|
||||
html = html.body;
|
||||
if (html.includes(',"preloadList":[{"url":"')) {
|
||||
return {
|
||||
urls: unicodeDecode(html.split(',"preloadList":[{"url":"')[1].split('","id":"')[0].trim()),
|
||||
filename: `${obj.host}_${obj.postId}_video.mp4`
|
||||
}
|
||||
} else throw new Error()
|
||||
} else throw new Error()
|
||||
}
|
||||
|
||||
let video, videoFilename, audioFilename, isMp3, audio,
|
||||
images = detail["image_post_info"] ? detail["image_post_info"]["images"] : false,
|
||||
filenameBase = `${obj.host}_${obj.postId}`;
|
||||
|
||||
if (!obj.isAudioOnly && !images) {
|
||||
if (obj.host == "tiktok") {
|
||||
video = detail["video"]["play_addr"]["url_list"][0]
|
||||
} else {
|
||||
video = detail["video"]["play_addr"]["url_list"][0].replace("playwm", "play")
|
||||
}
|
||||
video = obj.host === "tiktok" ? detail["video"]["play_addr"]["url_list"][0] : detail["video"]["play_addr"]["url_list"][0].replace("playwm", "play");
|
||||
videoFilename = `${filenameBase}_video_nw.mp4` // nw - no watermark
|
||||
if (!obj.noWatermark) {
|
||||
if (obj.host == "tiktok") {
|
||||
if (obj.host === "tiktok") {
|
||||
let html = await got.get(`https://tiktok.com/@video/video/${obj.postId}`, { headers: { "user-agent": userAgent } });
|
||||
html.on('error', (err) => {
|
||||
return { error: loc(obj.lang, 'ErrorCantConnectToServiceAPI', obj.host) };
|
||||
});
|
||||
html = html.body;
|
||||
if (html.includes(',"preloadList":[{"url":"')) {
|
||||
video = unicodeDecode(html.split(',"preloadList":[{"url":"')[1].split('","id":"')[0].trim())
|
||||
@@ -68,7 +68,7 @@ export default async function(obj) {
|
||||
videoFilename = `${filenameBase}_video.mp4`
|
||||
}
|
||||
} else {
|
||||
let fallback = obj.host == "douyin" ? detail["video"]["play_addr"]["url_list"][0].replace("playwm", "play") : detail["video"]["play_addr"]["url_list"][0];
|
||||
let fallback = obj.host === "douyin" ? detail["video"]["play_addr"]["url_list"][0].replace("playwm", "play") : detail["video"]["play_addr"]["url_list"][0];
|
||||
if (obj.fullAudio || fallback.includes("music")) {
|
||||
audio = detail["music"]["play_url"]["url_list"][0]
|
||||
audioFilename = `${filenameBase}_audio`
|
||||
@@ -76,7 +76,7 @@ export default async function(obj) {
|
||||
audio = fallback
|
||||
audioFilename = `${filenameBase}_audio_fv` // fv - from video
|
||||
}
|
||||
if (audio.slice(-4) == ".mp3") isMp3 = true;
|
||||
if (audio.slice(-4) === ".mp3") isMp3 = true;
|
||||
}
|
||||
if (video) return {
|
||||
urls: video,
|
||||
|
||||
@@ -3,7 +3,7 @@ import loc from "../../localization/manager.js";
|
||||
import { genericUserAgent } from "../config.js";
|
||||
|
||||
function bestQuality(arr) {
|
||||
return arr.filter((v) => { if (v["content_type"] == "video/mp4") return true; }).sort((a, b) => Number(b.bitrate) - Number(a.bitrate))[0]["url"].split("?")[0]
|
||||
return arr.filter((v) => { if (v["content_type"] === "video/mp4") return true; }).sort((a, b) => Number(b.bitrate) - Number(a.bitrate))[0]["url"].split("?")[0]
|
||||
}
|
||||
const apiURL = "https://api.twitter.com/1.1"
|
||||
|
||||
@@ -11,7 +11,7 @@ export default async function(obj) {
|
||||
try {
|
||||
let _headers = {
|
||||
"User-Agent": genericUserAgent,
|
||||
"Authorization": "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA",
|
||||
"Authorization": "Bearer AAAAAAAAAAAAAAAAAAAAAPYXBAAAAAAACLXUNDekMxqa8h%2F40K4moUkGsoc%3DTYfbDKbT3jJPCEVnMYqilB28NHfOPqkca3qaAxGfsyKCs0wRbw",
|
||||
"Host": "api.twitter.com",
|
||||
"Content-Type": "application/json",
|
||||
"Content-Length": 0
|
||||
@@ -19,36 +19,66 @@ export default async function(obj) {
|
||||
let req_act = await got.post(`${apiURL}/guest/activate.json`, {
|
||||
headers: _headers
|
||||
});
|
||||
req_act.on('error', (err) => {
|
||||
return { error: loc(obj.lang, 'ErrorCantConnectToServiceAPI', 'twitter') }
|
||||
})
|
||||
_headers["x-guest-token"] = req_act.body["guest_token"];
|
||||
let req_status = await got.get(`${apiURL}/statuses/show/${obj.id}.json?tweet_mode=extended&include_user_entities=0&trim_user=1&include_entities=0&cards_platform=Web-12&include_cards=1`, {
|
||||
headers: _headers
|
||||
});
|
||||
req_status.on('error', (err) => {
|
||||
return { error: loc(obj.lang, 'ErrorCantConnectToServiceAPI', 'twitter') }
|
||||
})
|
||||
let parsbod = JSON.parse(req_status.body);
|
||||
if (parsbod["extended_entities"] && parsbod["extended_entities"]["media"]) {
|
||||
let single, multiple = [], media = parsbod["extended_entities"]["media"];
|
||||
media = media.filter((i) => { if (i["type"] == "video" || i["type"] == "animated_gif") return true })
|
||||
if (media.length > 1) {
|
||||
for (let i in media) {
|
||||
multiple.push({type: "video", thumb: media[i]["media_url_https"], url: bestQuality(media[i]["video_info"]["variants"])})
|
||||
req_act = JSON.parse(req_act.body)
|
||||
_headers["x-guest-token"] = req_act["guest_token"];
|
||||
if (!obj.spaceId) {
|
||||
let req_status = await got.get(
|
||||
`${apiURL}/statuses/show/${obj.id}.json?tweet_mode=extended&include_user_entities=0&trim_user=1&include_entities=0&cards_platform=Web-12&include_cards=1`,
|
||||
{ headers: _headers }
|
||||
);
|
||||
|
||||
req_status = JSON.parse(req_status.body);
|
||||
if (req_status["extended_entities"] && req_status["extended_entities"]["media"]) {
|
||||
let single, multiple = [], media = req_status["extended_entities"]["media"];
|
||||
media = media.filter((i) => { if (i["type"] === "video" || i["type"] === "animated_gif") return true })
|
||||
if (media.length > 1) {
|
||||
for (let i in media) { multiple.push({type: "video", thumb: media[i]["media_url_https"], url: bestQuality(media[i]["video_info"]["variants"])}) }
|
||||
} else if (media.length > 0) {
|
||||
single = bestQuality(media[0]["video_info"]["variants"])
|
||||
} else {
|
||||
return { error: loc(obj.lang, 'ErrorNoVideosInTweet') }
|
||||
}
|
||||
if (single) {
|
||||
return { urls: single, audioFilename: `twitter_${obj.id}_audio` }
|
||||
} else if (multiple) {
|
||||
return { picker: multiple }
|
||||
} else {
|
||||
return { error: loc(obj.lang, 'ErrorNoVideosInTweet') }
|
||||
}
|
||||
} else {
|
||||
single = bestQuality(media[0]["video_info"]["variants"])
|
||||
}
|
||||
if (single) {
|
||||
return { urls: single, audioFilename: `twitter_${obj.id}_audio` }
|
||||
} else if (multiple) {
|
||||
return { picker: multiple }
|
||||
} else {
|
||||
return { error: loc(obj.lang, 'ErrorNoVideosInTweet') }
|
||||
}
|
||||
} else {
|
||||
return { error: loc(obj.lang, 'ErrorNoVideosInTweet') }
|
||||
_headers["host"] = "twitter.com"
|
||||
let query = {
|
||||
variables: {"id": obj.spaceId,"isMetatagsQuery":true,"withSuperFollowsUserFields":true,"withDownvotePerspective":false,"withReactionsMetadata":false,"withReactionsPerspective":false,"withSuperFollowsTweetFields":true,"withReplays":true}, features: {"spaces_2022_h2_clipping":true,"spaces_2022_h2_spaces_communities":true,"verified_phone_label_enabled":false,"tweetypie_unmention_optimization_enabled":true,"responsive_web_uc_gql_enabled":true,"vibe_api_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":false,"responsive_web_graphql_timeline_navigation_enabled":false,"interactive_text_enabled":true,"responsive_web_text_conversations_enabled":false,"responsive_web_enhance_cards_enabled":true}
|
||||
}
|
||||
let AudioSpaceById = await got.get(`https://twitter.com/i/api/graphql/wJ5g4zf7v8qPHSQbaozYuw/AudioSpaceById?variables=${new URLSearchParams(JSON.stringify(query.variables)).toString().slice(0, -1)}&features=${new URLSearchParams(JSON.stringify(query.features)).toString().slice(0, -1)}`, { headers: _headers });
|
||||
|
||||
AudioSpaceById = JSON.parse(AudioSpaceById.body);
|
||||
if (AudioSpaceById.data.audioSpace.metadata.is_space_available_for_replay === true) {
|
||||
let streamStatus = await got.get(`https://twitter.com/i/api/1.1/live_video_stream/status/${AudioSpaceById.data.audioSpace.metadata.media_key}`, { headers: _headers });
|
||||
streamStatus = JSON.parse(streamStatus.body);
|
||||
let participants = AudioSpaceById.data.audioSpace.participants.speakers
|
||||
let listOfParticipants = `Twitter Space speakers: `
|
||||
for (let i in participants) {
|
||||
listOfParticipants += `@${participants[i]["twitter_screen_name"]}, `
|
||||
}
|
||||
listOfParticipants = listOfParticipants.slice(0, -2);
|
||||
return {
|
||||
urls: streamStatus.source.noRedirectPlaybackUrl,
|
||||
audioFilename: `twitterspaces_${obj.spaceId}`,
|
||||
isAudioOnly: true,
|
||||
fileMetadata: {
|
||||
title: AudioSpaceById.data.audioSpace.metadata.title,
|
||||
artist: `Twitter Space by @${AudioSpaceById.data.audioSpace.metadata.creator_results.result.legacy.screen_name}`,
|
||||
comment: listOfParticipants,
|
||||
// cover: AudioSpaceById.data.audioSpace.metadata.creator_results.result.legacy.profile_image_url_https.replace("_normal", "")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return { error: loc(obj.lang, 'TwitterSpaceWasntRecorded') };
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
return { error: loc(obj.lang, 'ErrorBadFetch') };
|
||||
|
||||
@@ -15,7 +15,7 @@ export default async function(obj) {
|
||||
let all = api["request"]["files"]["progressive"].sort((a, b) => Number(b.width) - Number(a.width));
|
||||
let best = all[0]
|
||||
try {
|
||||
if (obj.quality != "max") {
|
||||
if (obj.quality !== "max") {
|
||||
let pref = parseInt(quality[obj.quality], 10)
|
||||
for (let i in all) {
|
||||
let currQuality = parseInt(all[i]["quality"].replace('p', ''), 10)
|
||||
|
||||
@@ -5,37 +5,41 @@ import selectQuality from "../stream/selectQuality.js";
|
||||
|
||||
export default async function(obj) {
|
||||
try {
|
||||
let info = await ytdl.getInfo(obj.id);
|
||||
if (info) {
|
||||
info = info.formats;
|
||||
let infoInitial = await ytdl.getInfo(obj.id);
|
||||
if (infoInitial) {
|
||||
let info = infoInitial.formats;
|
||||
if (!info[0]["isLive"]) {
|
||||
let videoMatch = [], fullVideoMatch = [], video = [], audio = info.filter((a) => {
|
||||
if (!a["isHLS"] && !a["isDashMPD"] && a["hasAudio"] && !a["hasVideo"] && a["container"] == obj.format) return true;
|
||||
if (!a["isHLS"] && !a["isDashMPD"] && a["hasAudio"] && !a["hasVideo"] && a["container"] === obj.format) return true;
|
||||
}).sort((a, b) => Number(b.bitrate) - Number(a.bitrate));
|
||||
if (!obj.isAudioOnly) {
|
||||
video = info.filter((a) => {
|
||||
if (!a["isHLS"] && !a["isDashMPD"] && a["hasVideo"] && a["container"] == obj.format && a["height"] != 4320) {
|
||||
if (obj.quality != "max") {
|
||||
if (a["hasAudio"] && mq[obj.quality] == a["height"]) {
|
||||
if (!a["isHLS"] && !a["isDashMPD"] && a["hasVideo"] && a["container"] === obj.format && a["height"] !== 4320) {
|
||||
if (obj.quality !== "max") {
|
||||
if (a["hasAudio"] && mq[obj.quality] === a["height"]) {
|
||||
fullVideoMatch.push(a)
|
||||
} else if (!a["hasAudio"] && mq[obj.quality] == a["height"]) {
|
||||
} else if (!a["hasAudio"] && mq[obj.quality] === a["height"]) {
|
||||
videoMatch.push(a);
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}).sort((a, b) => Number(b.bitrate) - Number(a.bitrate));
|
||||
if (obj.quality != "max") {
|
||||
if (videoMatch.length == 0) {
|
||||
if (obj.quality !== "max") {
|
||||
if (videoMatch.length === 0) {
|
||||
let ss = selectQuality("youtube", obj.quality, video[0]["qualityLabel"].slice(0, 5).replace('p', '').trim())
|
||||
videoMatch = video.filter((a) => {
|
||||
if (a["qualityLabel"].slice(0, 5).replace('p', '').trim() == ss) return true;
|
||||
if (a["qualityLabel"].slice(0, 5).replace('p', '').trim() === ss) return true;
|
||||
})
|
||||
} else if (fullVideoMatch.length > 0) {
|
||||
videoMatch = [fullVideoMatch[0]]
|
||||
}
|
||||
} else videoMatch = [video[0]];
|
||||
if (obj.quality == "los") videoMatch = [video[video.length - 1]];
|
||||
if (obj.quality === "los") videoMatch = [video[video.length - 1]];
|
||||
}
|
||||
let generalMeta = {
|
||||
title: infoInitial.videoDetails.title,
|
||||
artist: infoInitial.videoDetails.ownerChannelName.replace("- Topic", "").trim(),
|
||||
}
|
||||
if (audio[0]["approxDurationMs"] <= maxVideoDuration) {
|
||||
if (!obj.isAudioOnly && videoMatch.length > 0) {
|
||||
@@ -60,7 +64,21 @@ export default async function(obj) {
|
||||
filename: `youtube_${obj.id}_${video[0]["width"]}x${video[0]["height"]}.${video[0]["container"]}`
|
||||
};
|
||||
} else if (audio.length > 0) {
|
||||
return { type: "bridge", isAudioOnly: true, urls: audio[0]["url"], audioFilename: `youtube_${obj.id}_audio` };
|
||||
let r = {
|
||||
type: "render",
|
||||
isAudioOnly: true,
|
||||
urls: audio[0]["url"],
|
||||
audioFilename: `youtube_${obj.id}_audio`,
|
||||
fileMetadata: generalMeta
|
||||
};
|
||||
let isAutoGenAudio = infoInitial.videoDetails.description.startsWith("Provided to YouTube by");
|
||||
if (isAutoGenAudio) {
|
||||
let descItems = infoInitial.videoDetails.description.split("\n\n")
|
||||
r.fileMetadata.album = descItems[2]
|
||||
r.fileMetadata.copyright = descItems[3]
|
||||
r.fileMetadata.date = descItems[4].replace("Released on: ", '').trim()
|
||||
}
|
||||
return r
|
||||
} else {
|
||||
return { error: loc(obj.lang, 'ErrorBadFetch') };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user