diff --git a/docs/API.md b/docs/API.md index e0a9624a..f45e2c0e 100644 --- a/docs/API.md +++ b/docs/API.md @@ -43,8 +43,8 @@ Content live render streaming endpoint.
### Request Query Variables | key | variables | description | |:----|:-----------------|:-------------------------------------------------------------------------------------------------------------------------------| -| p | ``1`` | Used for checking the rate limit. | -| t | Stream token | Unique stream identificator which is used for retrieving cached stream info data. | +| p | ``1`` | Used for probing the rate limit. | +| t | Stream token | Unique stream ID. Used for retrieving cached stream info data. | | h | HMAC | Hashed combination of: (hashed) ip address, stream token, expiry timestamp, and service name. Used for verification of stream. | | e | Expiry timestamp | | diff --git a/package.json b/package.json index 52745127..cd3dad05 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cobalt", "description": "save what you love", - "version": "5.3.1", + "version": "5.3.2", "author": "wukko", "exports": "./src/cobalt.js", "type": "module", @@ -30,6 +30,7 @@ "express-rate-limit": "^6.3.0", "ffmpeg-static": "^5.1.0", "got": "^12.1.0", + "nanoid": "^4.0.2", "node-cache": "^5.1.2", "url-pattern": "1.0.3", "xml-js": "^1.6.11", diff --git a/src/front/cobalt.css b/src/front/cobalt.css index 640fe82b..a0760c26 100644 --- a/src/front/cobalt.css +++ b/src/front/cobalt.css @@ -147,8 +147,10 @@ button:active, background: var(--accent-press); cursor: pointer; } +.switch.text-backdrop, .switch.text-backdrop:hover, .switch.text-backdrop:active, +.text-to-copy.text-backdrop, .text-to-copy.text-backdrop:hover, .text-to-copy.text-backdrop:active { background: var(--accent); @@ -648,7 +650,13 @@ input[type="checkbox"] { -webkit-user-select: text; } .expanded .collapse-body { - display: block + display: block; +} +#download-switcher .switches { + gap: var(--gap); +} +#pd-share { + display: none; } /* adapt the page according to screen size */ @media screen and (min-width: 2300px) { diff --git a/src/front/cobalt.js b/src/front/cobalt.js index a6ac7fb3..1d1d625e 100644 --- a/src/front/cobalt.js +++ b/src/front/cobalt.js @@ -91,6 +91,9 @@ function copy(id, data) { setTimeout(() => { e.classList.remove("text-backdrop") }, 600); data ? navigator.clipboard.writeText(data) : navigator.clipboard.writeText(e.innerText); } +async function share(url) { + try { await navigator.share({url: url}) } catch (e) {} +} function detectColorScheme() { let theme = "auto"; let localTheme = sGet("theme"); @@ -174,6 +177,8 @@ function popup(type, action, text) { case "download": eid("pd-download").href = text; eid("pd-copy").setAttribute("onClick", `copy('pd-copy', '${text}')`); + eid("pd-share").setAttribute("onClick", `share('${text}')`); + if (navigator.canShare) eid("pd-share").style.display = "flex"; break; case "picker": switch (text.type) { diff --git a/src/localization/languages/en.json b/src/localization/languages/en.json index 9600ae8d..f6b88990 100644 --- a/src/localization/languages/en.json +++ b/src/localization/languages/en.json @@ -52,7 +52,7 @@ "DownloadPopupWayToSave": "pick a way to save", "ClickToCopy": "press to copy", "Download": "download", - "CopyURL": "copy url", + "CopyURL": "copy", "AboutTab": "about", "ChangelogTab": "changelog", "DonationsTab": "donations", @@ -117,6 +117,7 @@ "SettingsDubDefault": "original", "SettingsDubAuto": "auto", "SettingsVimeoPrefer": "vimeo downloads type", - "SettingsVimeoPreferDescription": "progressive: direct file link to vimeo's cdn. max quality is 1080p.\ndash: video and audio are merged by {appName} into one file. max quality is 4k.\n\npick \"progressive\" if you want best editor/player/social media compatibility. if progressive download isn't available, dash is used instead." + "SettingsVimeoPreferDescription": "progressive: direct file link to vimeo's cdn. max quality is 1080p.\ndash: video and audio are merged by {appName} into one file. max quality is 4k.\n\npick \"progressive\" if you want best editor/player/social media compatibility. if progressive download isn't available, dash is used instead.", + "ShareURL": "share" } } diff --git a/src/localization/languages/ru.json b/src/localization/languages/ru.json index abcab52a..3479e755 100644 --- a/src/localization/languages/ru.json +++ b/src/localization/languages/ru.json @@ -52,7 +52,7 @@ "DownloadPopupWayToSave": "выбери, как сохранить", "ClickToCopy": "нажми, чтобы скопировать", "Download": "скачать", - "CopyURL": "скопировать ссылку", + "CopyURL": "скопировать", "AboutTab": "о {appName}", "ChangelogTab": "изменения", "DonationsTab": "донаты", @@ -117,6 +117,7 @@ "SettingsDubDefault": "оригинал", "SettingsDubAuto": "авто", "SettingsVimeoPrefer": "тип загрузок с vimeo", - "SettingsVimeoPreferDescription": "progressive: прямая ссылка на файл с сервера vimeo. максимальное качество: 1080p.\ndash: {appName} совмещает видео и аудио в один файл. максимальное качество: 4k.\n\nвыбирай \"progressive\", если тебе нужна наилучшая совместимость с плеерами/редакторами/соцсетями. если \"progressive\" файл недоступен, {appName} скачает \"dash\"." + "SettingsVimeoPreferDescription": "progressive: прямая ссылка на файл с сервера vimeo. максимальное качество: 1080p.\ndash: {appName} совмещает видео и аудио в один файл. максимальное качество: 4k.\n\nвыбирай \"progressive\", если тебе нужна наилучшая совместимость с плеерами/редакторами/соцсетями. если \"progressive\" файл недоступен, {appName} скачает \"dash\".", + "ShareURL": "поделиться" } } diff --git a/src/modules/pageRender/page.js b/src/modules/pageRender/page.js index 25e785b3..2576de41 100644 --- a/src/modules/pageRender/page.js +++ b/src/modules/pageRender/page.js @@ -333,7 +333,8 @@ export default function(obj) { name: "download", subtitle: t('DownloadPopupWayToSave'), explanation: `${!isIOS ? t('DownloadPopupDescription') : t('DownloadPopupDescriptionIOS')}`, - items: `${t('Download')} + items: `${t('Download')} +
${t('ShareURL')}
${t('CopyURL')}
` }) })} diff --git a/src/modules/stream/manage.js b/src/modules/stream/manage.js index 875a6d98..827af46c 100644 --- a/src/modules/stream/manage.js +++ b/src/modules/stream/manage.js @@ -1,4 +1,5 @@ import NodeCache from "node-cache"; +import { nanoid } from 'nanoid'; import { sha256 } from "../sub/crypto.js"; import { streamLifespan } from "../config.js"; @@ -11,9 +12,9 @@ streamCache.on("expired", (key) => { }); export function createStream(obj) { - let streamID = sha256(`${obj.ip},${obj.service},${obj.filename},${obj.audioFormat},${obj.mute}`, salt), + let streamID = nanoid(), exp = Math.floor(new Date().getTime()) + streamLifespan, - ghmac = sha256(`${streamID},${obj.service},${obj.ip},${exp}`, salt); + ghmac = sha256(`${streamID},${obj.ip},${obj.service},${exp}`, salt); if (!streamCache.has(streamID)) { streamCache.set(streamID, { @@ -42,13 +43,15 @@ export function createStream(obj) { export function verifyStream(ip, id, hmac, exp) { try { - let streamInfo = streamCache.get(id); - if (!streamInfo) return { error: 'this stream token does not exist', status: 400 }; - - let ghmac = sha256(`${id},${streamInfo.service},${ip},${exp}`, salt); - if (String(hmac) === ghmac && String(exp) === String(streamInfo.exp) && ghmac === String(streamInfo.hmac) - && String(ip) === streamInfo.ip && Number(exp) > Math.floor(new Date().getTime())) { - return streamInfo; + if (id.length === 21) { + let streamInfo = streamCache.get(id); + if (!streamInfo) return { error: 'this stream token does not exist', status: 400 }; + + let ghmac = sha256(`${id},${ip},${streamInfo.service},${exp}`, salt); + if (String(hmac) === ghmac && String(exp) === String(streamInfo.exp) && ghmac === String(streamInfo.hmac) + && String(ip) === streamInfo.ip && Number(exp) > Math.floor(new Date().getTime())) { + return streamInfo; + } } return { error: 'Unauthorized', status: 401 }; } catch (e) {