Merge branch 'main' into clipboard

This commit is contained in:
Mahes1 2025-07-06 21:21:12 +05:45 committed by GitHub
commit e8703982b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 105 additions and 33 deletions

View File

@ -1,7 +1,7 @@
{ {
"name": "@imput/cobalt-api", "name": "@imput/cobalt-api",
"description": "save what you love", "description": "save what you love",
"version": "11.2", "version": "11.2.2",
"author": "imput", "author": "imput",
"exports": "./src/cobalt.js", "exports": "./src/cobalt.js",
"type": "module", "type": "module",
@ -26,6 +26,7 @@
"@datastructures-js/priority-queue": "^6.3.1", "@datastructures-js/priority-queue": "^6.3.1",
"@imput/psl": "^2.0.4", "@imput/psl": "^2.0.4",
"@imput/version-info": "workspace:^", "@imput/version-info": "workspace:^",
"@imput/youtubei.js": "^14.0.0",
"content-disposition-header": "0.6.0", "content-disposition-header": "0.6.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^16.0.1", "dotenv": "^16.0.1",
@ -37,9 +38,8 @@
"mime": "^4.0.4", "mime": "^4.0.4",
"nanoid": "^5.0.9", "nanoid": "^5.0.9",
"set-cookie-parser": "2.6.0", "set-cookie-parser": "2.6.0",
"undici": "^5.19.1", "undici": "^6.21.3",
"url-pattern": "1.0.3", "url-pattern": "1.0.3",
"youtubei.js": "^14.0.0",
"zod": "^3.23.8" "zod": "^3.23.8"
}, },
"optionalDependencies": { "optionalDependencies": {

View File

@ -1,4 +1,4 @@
import { Constants } from "youtubei.js"; import { Constants } from "@imput/youtubei.js";
import { services } from "../processing/service-config.js"; import { services } from "../processing/service-config.js";
import { updateEnv, canonicalEnv, env as currentEnv } from "../config.js"; import { updateEnv, canonicalEnv, env as currentEnv } from "../config.js";

View File

@ -49,5 +49,6 @@ const maps = {
} }
export const convertLanguageCode = (code) => { export const convertLanguageCode = (code) => {
code = code?.split("-")[0]?.split("_")[0] || "";
return maps[code.length]?.[code.toLowerCase()] || null; return maps[code.length]?.[code.toLowerCase()] || null;
} }

View File

@ -263,8 +263,9 @@ export default function({
// extractors usually return ISO 639-1 language codes, // extractors usually return ISO 639-1 language codes,
// but video players expect ISO 639-2, so we convert them here // but video players expect ISO 639-2, so we convert them here
if (defaultParams.fileMetadata?.sublanguage?.length === 2) { const sublanguage = defaultParams.fileMetadata?.sublanguage;
const code = convertLanguageCode(defaultParams.fileMetadata.sublanguage); if (sublanguage && sublanguage.length !== 3) {
const code = convertLanguageCode(sublanguage);
if (code) { if (code) {
defaultParams.fileMetadata.sublanguage = code; defaultParams.fileMetadata.sublanguage = code;
} else { } else {

View File

@ -15,7 +15,46 @@ const resolutionMatch = {
"426": 240 "426": 240
} }
const requestApiInfo = (videoId, password) => { const genericHeaders = {
Accept: 'application/vnd.vimeo.*+json; version=3.4.10',
'User-Agent': 'Vimeo/11.13.0 (com.vimeo; build:250619.102023.0; iOS 18.5.0) Alamofire/5.9.0 VimeoNetworking/5.0.0',
Authorization: 'Basic MTMxNzViY2Y0NDE0YTQ5YzhjZTc0YmU0NjVjNDQxYzNkYWVjOWRlOTpHKzRvMmgzVUh4UkxjdU5FRW80cDNDbDhDWGR5dVJLNUJZZ055dHBHTTB4V1VzaG41bEx1a2hiN0NWYWNUcldSSW53dzRUdFRYZlJEZmFoTTArOTBUZkJHS3R4V2llYU04Qnl1bERSWWxUdXRidjNqR2J4SHFpVmtFSUcyRktuQw==',
'Accept-Language': 'en-US,en;q=0.9',
}
let bearer = '';
const getBearer = async (refresh = false) => {
if (bearer && !refresh) return bearer;
const oauthResponse = await fetch(
`https://api.vimeo.com/oauth/authorize/client?sizes=216,288,300,360,640,960,1280,1920&cdm_type=fairplay`,
{
method: 'POST',
body: JSON.stringify({
scope: 'public private purchased create edit delete interact upload stats',
grant_type: 'client_credentials',
// device_identifier is a long ass base64 string of seemingly
// random data, but it doesn't seem to be required, so we just omit it lol
device_identifier: '',
}),
headers: {
...genericHeaders,
'Content-Type': 'application/json',
}
}
)
.then(a => a.json())
.catch(() => {});
if (!oauthResponse || !oauthResponse.access_token) {
return;
}
return bearer = oauthResponse.access_token;
}
const requestApiInfo = (bearerToken, videoId, password) => {
if (password) { if (password) {
videoId += `:${password}` videoId += `:${password}`
} }
@ -24,10 +63,8 @@ const requestApiInfo = (videoId, password) => {
`https://api.vimeo.com/videos/${videoId}`, `https://api.vimeo.com/videos/${videoId}`,
{ {
headers: { headers: {
Accept: 'application/vnd.vimeo.*+json; version=3.4.2', ...genericHeaders,
'User-Agent': 'Vimeo/10.19.0 (com.vimeo; build:101900.57.0; iOS 17.5.1) Alamofire/5.9.0 VimeoNetworking/5.0.0', Authorization: `Bearer ${bearerToken}`,
Authorization: 'Basic MTMxNzViY2Y0NDE0YTQ5YzhjZTc0YmU0NjVjNDQxYzNkYWVjOWRlOTpHKzRvMmgzVUh4UkxjdU5FRW80cDNDbDhDWGR5dVJLNUJZZ055dHBHTTB4V1VzaG41bEx1a2hiN0NWYWNUcldSSW53dzRUdFRYZlJEZmFoTTArOTBUZkJHS3R4V2llYU04Qnl1bERSWWxUdXRidjNqR2J4SHFpVmtFSUcyRktuQw==',
'Accept-Language': 'en'
} }
} }
) )
@ -151,9 +188,28 @@ export default async function(obj) {
if (quality < 240) quality = 240; if (quality < 240) quality = 240;
if (!quality || obj.isAudioOnly) quality = 9000; if (!quality || obj.isAudioOnly) quality = 9000;
const info = await requestApiInfo(obj.id, obj.password); const bearerToken = await getBearer();
if (!bearerToken) {
return { error: "fetch.fail" };
}
let info = await requestApiInfo(bearerToken, obj.id, obj.password);
let response; let response;
// auth error, try to refresh the token
if (info?.error_code === 8003) {
const newBearer = await getBearer(true);
if (!newBearer) {
return { error: "fetch.fail" };
}
info = await requestApiInfo(newBearer, obj.id, obj.password);
}
// if there's still no info, then return a generic error
if (!info || info.error_code) {
return { error: "fetch.empty" };
}
if (obj.isAudioOnly) { if (obj.isAudioOnly) {
response = await getHLS(info.config_url, { ...obj, quality }); response = await getHLS(info.config_url, { ...obj, quality });
} }

View File

@ -1,7 +1,7 @@
import HLS from "hls-parser"; import HLS from "hls-parser";
import { fetch } from "undici"; import { fetch } from "undici";
import { Innertube, Session } from "youtubei.js"; import { Innertube, Session } from "@imput/youtubei.js";
import { env } from "../../config.js"; import { env } from "../../config.js";
import { getCookie } from "../cookie/manager.js"; import { getCookie } from "../cookie/manager.js";

View File

@ -19,6 +19,9 @@ importers:
'@imput/version-info': '@imput/version-info':
specifier: workspace:^ specifier: workspace:^
version: link:../packages/version-info version: link:../packages/version-info
'@imput/youtubei.js':
specifier: ^14.0.0
version: 14.0.0
content-disposition-header: content-disposition-header:
specifier: 0.6.0 specifier: 0.6.0
version: 0.6.0 version: 0.6.0
@ -53,14 +56,11 @@ importers:
specifier: 2.6.0 specifier: 2.6.0
version: 2.6.0 version: 2.6.0
undici: undici:
specifier: ^5.19.1 specifier: ^6.21.3
version: 5.28.4 version: 6.21.3
url-pattern: url-pattern:
specifier: 1.0.3 specifier: 1.0.3
version: 1.0.3 version: 1.0.3
youtubei.js:
specifier: ^14.0.0
version: 14.0.0
zod: zod:
specifier: ^3.23.8 specifier: ^3.23.8
version: 3.23.8 version: 3.23.8
@ -563,6 +563,9 @@ packages:
'@imput/psl@2.0.4': '@imput/psl@2.0.4':
resolution: {integrity: sha512-vuy76JX78/DnJegLuJoLpMmw11JTA/9HvlIADg/f8dDVXyxbh0jnObL0q13h+WvlBO4Gk26Pu8sUa7/h0JGQig==} resolution: {integrity: sha512-vuy76JX78/DnJegLuJoLpMmw11JTA/9HvlIADg/f8dDVXyxbh0jnObL0q13h+WvlBO4Gk26Pu8sUa7/h0JGQig==}
'@imput/youtubei.js@14.0.0':
resolution: {integrity: sha512-YvTnh53URPlzsmMzqF/DFHZyR9HrpgoWYHzEOklx5OCkwk1/0F/CrO9gqArXw/1oI6GjaTS2CqBd1CzyFZB07A==}
'@isaacs/cliui@8.0.2': '@isaacs/cliui@8.0.2':
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -2080,6 +2083,10 @@ packages:
resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==} resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==}
engines: {node: '>=14.0'} engines: {node: '>=14.0'}
undici@6.21.3:
resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==}
engines: {node: '>=18.17'}
unist-util-stringify-position@2.0.3: unist-util-stringify-position@2.0.3:
resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==}
@ -2181,9 +2188,6 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'} engines: {node: '>=10'}
youtubei.js@14.0.0:
resolution: {integrity: sha512-KAFttOw+9fwwBUvBc1T7KzMNBLczDOuN/dfote8BA9CABxgx8MPgV+vZWlowdDB6DnHjSUYppv+xvJ4VNBLK9A==}
zimmerframe@1.1.2: zimmerframe@1.1.2:
resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==}
@ -2396,7 +2400,8 @@ snapshots:
dependencies: dependencies:
levn: 0.4.1 levn: 0.4.1
'@fastify/busboy@2.1.1': {} '@fastify/busboy@2.1.1':
optional: true
'@fontsource/ibm-plex-mono@5.0.13': {} '@fontsource/ibm-plex-mono@5.0.13': {}
@ -2423,6 +2428,13 @@ snapshots:
dependencies: dependencies:
punycode: 2.3.1 punycode: 2.3.1
'@imput/youtubei.js@14.0.0':
dependencies:
'@bufbuild/protobuf': 2.2.5
jintr: 3.3.1
tslib: 2.6.3
undici: 6.21.3
'@isaacs/cliui@8.0.2': '@isaacs/cliui@8.0.2':
dependencies: dependencies:
string-width: 5.1.2 string-width: 5.1.2
@ -3960,6 +3972,9 @@ snapshots:
undici@5.28.4: undici@5.28.4:
dependencies: dependencies:
'@fastify/busboy': 2.1.1 '@fastify/busboy': 2.1.1
optional: true
undici@6.21.3: {}
unist-util-stringify-position@2.0.3: unist-util-stringify-position@2.0.3:
dependencies: dependencies:
@ -4035,13 +4050,6 @@ snapshots:
yocto-queue@0.1.0: {} yocto-queue@0.1.0: {}
youtubei.js@14.0.0:
dependencies:
'@bufbuild/protobuf': 2.2.5
jintr: 3.3.1
tslib: 2.6.3
undici: 5.28.4
zimmerframe@1.1.2: {} zimmerframe@1.1.2: {}
zod@3.23.8: {} zod@3.23.8: {}

View File

@ -6,7 +6,7 @@ banner:
alt: "meowth plush in a forest looking at the rising sun between the trees." alt: "meowth plush in a forest looking at the rising sun between the trees."
--- ---
it's summertime! even though it's been rainy for us lately, the sun is right on the horizon, just like this cobalt update. we improved local processing, added long-awaited features, and improved a ton of other stuff. **downloading from youtube is back, btw**. it's summertime! even though it's been rainy for us lately, the sun is right on the horizon, just like this cobalt update. we improved local processing, added long-awaited features, and improved a ton of other stuff.
here's what's new since 11.0: here's what's new since 11.0:
@ -31,6 +31,8 @@ downloading from youtube on the main instance is restored! sorry that it took a
hopefully it'll last for a while, but we think downloading from youtube will get significantly more annoying/complex in next few weeks-months. **right now is the best time to download everything you've been putting off**, either with cobalt or other tools. hopefully it'll last for a while, but we think downloading from youtube will get significantly more annoying/complex in next few weeks-months. **right now is the best time to download everything you've been putting off**, either with cobalt or other tools.
**update**: unfortunately it did not last, youtube is unavailable on the main instance again. we will try one more way soon and update this changelog and post about it on socials accordingly.
we're not trying to scare you; it's our educated guess based on what youtube has been doing lately: we're not trying to scare you; it's our educated guess based on what youtube has been doing lately:
- roll out of SABR & related limitations for more clients. SABR is Server ABR, Google's proprietary HLS alternative, controlled by the server. - roll out of SABR & related limitations for more clients. SABR is Server ABR, Google's proprietary HLS alternative, controlled by the server.
- growing potoken enforcement. - growing potoken enforcement.

View File

@ -85,6 +85,8 @@
"metadata.filename.preview.video": "Video Title - Video Author", "metadata.filename.preview.video": "Video Title - Video Author",
"metadata.filename.preview.audio": "Audio Title - Audio Author", "metadata.filename.preview.audio": "Audio Title - Audio Author",
"filename.preview_desc.video": "video file preview",
"filename.preview_desc.audio": "audio file preview",
"metadata.file": "file metadata", "metadata.file": "file metadata",
"metadata.disable.title": "disable file metadata", "metadata.disable.title": "disable file metadata",

View File

@ -80,6 +80,8 @@
"video.youtube.hls.title": "предпочитать hls для видео и аудио", "video.youtube.hls.title": "предпочитать hls для видео и аудио",
"metadata.filename.preview.video": "Название Видео - Автор Видео", "metadata.filename.preview.video": "Название Видео - Автор Видео",
"metadata.filename.preview.audio": "Название Аудио - Автор Аудио", "metadata.filename.preview.audio": "Название Аудио - Автор Аудио",
"filename.preview_desc.video": "превью видео файла",
"filename.preview_desc.audio": "превью аудио файла",
"saving.description": "предпочтительный способ сохранения файла или ссылки с кобальта. если предпочитаемый метод недоступен или что-то пойдёт не так, кобальт спросит тебя как поступить.", "saving.description": "предпочтительный способ сохранения файла или ссылки с кобальта. если предпочитаемый метод недоступен или что-то пойдёт не так, кобальт спросит тебя как поступить.",
"accessibility.transparency.description": "уменьшает прозрачность поверхностей и выключает эффекты размытия. также может улучшить работу интерфейса на менее мощных устройствах.", "accessibility.transparency.description": "уменьшает прозрачность поверхностей и выключает эффекты размытия. также может улучшить работу интерфейса на менее мощных устройствах.",
"accessibility.transparency.title": "уменьшить визуальную прозрачность", "accessibility.transparency.title": "уменьшить визуальную прозрачность",

View File

@ -1,6 +1,6 @@
{ {
"name": "@imput/cobalt-web", "name": "@imput/cobalt-web",
"version": "11.2.2", "version": "11.2.3",
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {

View File

@ -75,7 +75,7 @@
</div> </div>
<div class="item-text"> <div class="item-text">
<div class="preview">{`${videoFilePreview}.${youtubeVideoExt}`}</div> <div class="preview">{`${videoFilePreview}.${youtubeVideoExt}`}</div>
<div class="subtext description">video file preview</div> <div class="subtext description">{$t("settings.filename.preview_desc.video")}</div>
</div> </div>
</div> </div>
<div id="filename-preview-audio" class="filename-preview-item"> <div id="filename-preview-audio" class="filename-preview-item">
@ -84,7 +84,7 @@
</div> </div>
<div class="item-text"> <div class="item-text">
<div class="preview">{`${audioFilePreview}.${audioFormat}`}</div> <div class="preview">{`${audioFilePreview}.${audioFormat}`}</div>
<div class="subtext description">audio file preview</div> <div class="subtext description">{$t("settings.filename.preview_desc.audio")}</div>
</div> </div>
</div> </div>
</div> </div>