soundcloud and tooltip and some other stuff (3.3)

This commit is contained in:
wukko
2022-08-22 20:10:54 +06:00
parent 08cbc05018
commit 189ecf8fe7
17 changed files with 135 additions and 37 deletions

View File

@@ -18,6 +18,10 @@ export async function getJSON(originalURL, ip, lang, format, quality, audioForma
host = "youtube";
url = `https://youtube.com/watch?v=${url.replace("youtu.be/", "").replace("https://", "")}`;
}
if (host == "goo" && url.substring(0, 30) == "https://soundcloud.app.goo.gl/") {
host = "soundcloud"
url = `https://soundcloud.com/${url.replace("https://soundcloud.app.goo.gl/", "").split('/')[0]}`
}
if (host == "tumblr" && !url.includes("blog/view")) {
if (url.slice(-1) == '/') url = url.slice(0, -1);
url = url.replace(url.split('/')[5], '');

View File

@@ -8,6 +8,7 @@ export const
version = packageJson.version,
streamLifespan = config.streamLifespan,
maxVideoDuration = config.maxVideoDuration,
maxAudioDuration = config.maxAudioDuration,
genericUserAgent = config.genericUserAgent,
repo = packageJson["bugs"]["url"].replace('/issues', ''),
authorInfo = config.authorInfo,

View File

@@ -13,6 +13,7 @@ import douyin from "./services/douyin.js";
import tumblr from "./services/tumblr.js";
import matchActionDecider from "./sub/matchActionDecider.js";
import vimeo from "./services/vimeo.js";
import soundcloud from "./services/soundcloud.js";
export default async function (host, patternMatch, url, ip, lang, format, quality, audioFormat, isAudioOnly, noWatermark) {
try {
@@ -91,6 +92,15 @@ export default async function (host, patternMatch, url, ip, lang, format, qualit
lang: lang
});
break;
case "soundcloud":
isAudioOnly = true;
r = await soundcloud({
author: patternMatch["author"], song: patternMatch["song"], url: url,
shortLink: patternMatch["shortLink"] ? patternMatch["shortLink"] : false,
format: audioFormat,
lang: lang
});
break;
default:
return apiJSON(0, { t: errorUnsupported(lang) });
}

View File

@@ -44,7 +44,7 @@ export function popup(obj) {
${obj.buttonOnly ? obj.emoji : ``}
<div id="popup-header" class="popup-header">
${obj.standalone && !obj.buttonOnly ? `<button id="popup-close" class="button mono" onclick="popup('${obj.name}', 0)" ${obj.header.closeAria ? `aria-label="${obj.header.closeAria}"` : ''}>x</button>` : ''}
${obj.header.aboveTitle ? `<a id="popup-above-title" href="${obj.header.aboveTitle.url}">${obj.header.aboveTitle.text}</a>` : ''}
${obj.header.aboveTitle ? `<a id="popup-above-title" target="_blank" href="${obj.header.aboveTitle.url}">${obj.header.aboveTitle.text}</a>` : ''}
${obj.header.title ? `<div id="popup-title">${obj.header.title}</div>` : ''}
${obj.header.subtitle ? `<div id="popup-subtitle">${obj.header.subtitle}</div>` : ''}
</div>
@@ -52,7 +52,7 @@ export function popup(obj) {
${body}${obj.buttonOnly ? `<button id="close-error" class="switch" onclick="popup('${obj.name}', 0)">${obj.buttonText}</button>` : ''}
</div>
${obj.footer ? `<div id="popup-footer" class="popup-footer">
<a id="popup-bottom" class="popup-footer-content" href="${obj.footer.url}">${obj.footer.text}</a>
<a id="popup-bottom" class="popup-footer-content" target="_blank" href="${obj.footer.url}">${obj.footer.text}</a>
</div>` : ''}
${obj.standalone ? `</div>` : ''}`
}
@@ -69,7 +69,7 @@ export function multiPagePopup(obj) {
<div id="popup-${obj.name}" class="popup center box scrollable" style="visibility: hidden;">
<div id="popup-content">${obj.header ? `<div id="popup-header" class="popup-header">
${obj.header.aboveTitle ? `<a id="popup-above-title" href="${obj.header.aboveTitle.url}">${obj.header.aboveTitle.text}</a>` : ''}
${obj.header.aboveTitle ? `<a id="popup-above-title" target="_blank" href="${obj.header.aboveTitle.url}">${obj.header.aboveTitle.text}</a>` : ''}
${obj.header.title ? `<div id="popup-title">${obj.header.title}</div>` : ''}
${obj.header.subtitle ? `<div id="popup-subtitle">${obj.header.subtitle}</div>` : ''}</div>` : ''}${tabContent}</div>
<div id="popup-tabs" class="switches popup-tabs">${tabs}</div>
@@ -90,8 +90,8 @@ export function settingsCategory(obj) {
export function footerButtons(obj) {
let items = ``
for (let i = 0; i < obj.length; i++) {
let func = `${obj[i]["type"] == "toggle" ? `toggle('${obj[i]["name"]}')` : `popup('${obj[i]["name"]}', 1)`}`
items += `<button id="${obj[i]["name"]}" class="button footer-button" onclick="${func}" aria-label="${obj[i]["aria"]}">${obj[i]["icon"]}</button> `
let func = `${obj[i]["type"] == "toggle" ? `toggle('${obj[i]["name"]}')` : `popup('${obj[i]["name"]}', 1)`}`;
items += `<button id="${obj[i]["name"]}" class="button footer-button" onclick="${func}" aria-label="${obj[i]["aria"]}">${obj[i]["icon"]}</button>`;
}
return `
<div id="footer-buttons">${items}</div>`

View File

@@ -153,7 +153,7 @@ export default function(obj) {
header: {
aboveTitle: {
text: `v.${version} ~ ${obj.hash}`,
url: repo
url: `${repo}/commit/${obj.hash}`
},
title: `${emoji("⚙️", 30)} ${loc(obj.lang, 'TitlePopupSettings')}`
},
@@ -268,7 +268,7 @@ export default function(obj) {
<div id="cobalt-main-box" class="center box" style="visibility: hidden;">
<div id="logo-area">${appName}</div>
<div id="download-area" class="mobile-center">
<input id="url-input-area" class="mono" type="text" autocorrect="off" maxlength="110" autocapitalize="off" placeholder="${loc(obj.lang, 'LinkInput')}" aria-label="${loc(obj.lang, 'AccessibilityInputArea')}" oninput="button()">
<input id="url-input-area" class="mono" type="text" autocorrect="off" maxlength="128" autocapitalize="off" placeholder="${loc(obj.lang, 'LinkInput')}" aria-label="${loc(obj.lang, 'AccessibilityInputArea')}" oninput="button()">
<input id="download-button" class="mono dontRead" onclick="download(document.getElementById('url-input-area').value)" type="submit" value="" disabled=true aria-label="${loc(obj.lang, 'AccessibilityDownloadButton')}">
</div>
</div>
@@ -292,7 +292,13 @@ export default function(obj) {
)}
</footer>
</body>
<script type="text/javascript">const loc = {noInternet:"${loc(obj.lang, 'ErrorNoInternet')}", noURLReturned: "${loc(obj.lang, 'ErrorBadFetch')}", toggleDefault:'${emoji("✨")} ${loc(obj.lang, "ModeToggleDefault")}', toggleAudio:'${emoji("🎶")} ${loc(obj.lang, "SettingsFormatSwitchAudio")}'};</script>
<script type="text/javascript">const loc = {
noInternet: "${loc(obj.lang, 'ErrorNoInternet')}",
noURLReturned: "${loc(obj.lang, 'ErrorBadFetch')}",
toggleDefault: '${emoji("✨")} ${loc(obj.lang, "ModeToggleSmart")} ${loc(obj.lang, "ModeToggle")}',
toggleAudio: '${emoji("🎶")} ${loc(obj.lang, "SettingsAudioTab")} ${loc(obj.lang, "ModeToggle")}',
pressToChange: '<div class="tooltip">▼ ${loc(obj.lang, 'PressToChange')}</div>'
};</script>
<script type="text/javascript" src="cobalt.js"></script>
</html>`;
} catch (err) {

View File

@@ -0,0 +1,42 @@
import got from "got";
import loc from "../../localization/manager.js";
import { genericUserAgent, maxAudioDuration, services } from "../config.js";
export default async function(obj) {
try {
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") {
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}` }
} else return { error: loc(obj.lang, 'ErrorLengthAudioConvert', maxAudioDuration / 60000) }
}
} else return { error: loc(obj.lang, 'ErrorEmptyDownload') }
} else return { error: loc(obj.lang, 'ErrorBrokenLink', 'soundcloud') }
} catch (e) {
console.log(e)
return { error: loc(obj.lang, 'ErrorBadFetch') };
}
}

View File

@@ -43,6 +43,7 @@
"alias": "youtube, youtube music",
"patterns": ["watch?v=:id"],
"quality_match": ["2160", "1440", "1080", "720", "480", "360", "240", "144"],
"bestAudio": "opus",
"quality": {
"1080": "hig",
"720": "mid",
@@ -65,5 +66,11 @@
"vimeo": {
"patterns": [":id"],
"enabled": true
},
"soundcloud": {
"patterns": [":author/:song", ":shortLink"],
"bestAudio": "mp3",
"clientid": "lnFbWHXluNwOkW7TxTYUXrrse0qj1C72",
"enabled": true
}
}

View File

@@ -21,4 +21,7 @@ export let testers = {
(patternMatch["id"] && patternMatch["id"].length < 21 && patternMatch["user"] && patternMatch["user"].length <= 32)),
"vimeo": (patternMatch) => ((patternMatch["id"] && patternMatch["id"].length <= 11)),
"soundcloud": (patternMatch) => ((patternMatch["author"] && patternMatch["song"] && (patternMatch["author"].length + patternMatch["song"].length) <= 96) ||
(patternMatch["shortLink"] && patternMatch["shortLink"].length <= 32))
};

View File

@@ -1,4 +1,4 @@
import { supportedAudio } from "../config.js"
import { services, supportedAudio } from "../config.js"
import { apiJSON } from "./utils.js"
export default function(r, host, ip, audioFormat, isAudioOnly) {
@@ -48,14 +48,12 @@ export default function(r, host, ip, audioFormat, isAudioOnly) {
let type = "render"
let copy = false
if (!supportedAudio.includes(audioFormat)) audioFormat = "best";
if (audioFormat == "best") {
if (host != "youtube") {
audioFormat = "m4a"
copy = true
} else {
audioFormat = "opus"
type = "bridge"
}
if ((audioFormat == "best" && services[host]["bestAudio"]) || services[host]["bestAudio"] && (audioFormat == services[host]["bestAudio"])) {
audioFormat = services[host]["bestAudio"]
type = "bridge"
} else if (audioFormat == "best") {
audioFormat = "m4a"
copy = true
}
if (host == "reddit" && r.typeId == 1 || host == "vk" || host == "vimeo") return apiJSON(0, { t: r.audioFilename });
return apiJSON(2, {