Merge branch 'current' into disable-metadata
This commit is contained in:
commit
bfde9aab39
10 changed files with 156 additions and 119 deletions
55
.github/workflows/docker.yml
vendored
Normal file
55
.github/workflows/docker.yml
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
name: Build Docker image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build-and-push-image:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get version from package.json
|
||||
id: package-version
|
||||
uses: martinbeentjes/npm-get-version-action@v1.3.1
|
||||
- name: Get short commit hash
|
||||
id: commit-hash
|
||||
run: echo "commit_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
tags: |
|
||||
type=raw,value=latest
|
||||
type=raw,value=${{ steps.package-version.outputs.current-version }}
|
||||
type=raw,value=${{ steps.package-version.outputs.current-version }}-${{ steps.commit-hash.outputs.commit_short }}
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "cobalt",
|
||||
"description": "save what you love",
|
||||
"version": "7.1.2",
|
||||
"version": "7.2",
|
||||
"author": "wukko",
|
||||
"exports": "./src/cobalt.js",
|
||||
"type": "module",
|
||||
|
|
|
@ -9,9 +9,6 @@ import { loadLoc } from "./localization/manager.js";
|
|||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
import { runWeb } from "./core/web.js";
|
||||
import { runAPI } from "./core/api.js";
|
||||
|
||||
const app = express();
|
||||
|
||||
const gitCommit = shortCommit();
|
||||
|
@ -28,8 +25,10 @@ const apiMode = process.env.apiURL && process.env.apiPort && !((process.env.webU
|
|||
const webMode = process.env.webURL && process.env.webPort && !((process.env.apiURL && process.env.apiPort) || (process.env.selfURL && process.env.port));
|
||||
|
||||
if (apiMode) {
|
||||
const { runAPI } = await import('./core/api.js');
|
||||
runAPI(express, app, gitCommit, gitBranch, __dirname)
|
||||
} else if (webMode) {
|
||||
const { runWeb } = await import('./core/web.js');
|
||||
await runWeb(express, app, gitCommit, gitBranch, __dirname)
|
||||
} else {
|
||||
console.log(Red(`cobalt wasn't configured yet or configuration is invalid.\n`) + Bright(`please run the setup script to fix this: `) + Green(`npm run setup`))
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"streamLifespan": 20000,
|
||||
"maxVideoDuration": 10800000,
|
||||
"maxVideoDuration": 18000000,
|
||||
"genericUserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
|
||||
"authorInfo": {
|
||||
"name": "wukko",
|
||||
|
|
|
@ -175,6 +175,24 @@ input[type="text"],
|
|||
backdrop-filter: blur(7px);
|
||||
-webkit-backdrop-filter: blur(7px);
|
||||
}
|
||||
.glass-bkg.alone {
|
||||
z-index: -1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
}
|
||||
.glass-bkg.small {
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: -1;
|
||||
position: absolute;
|
||||
border: var(--accent-highlight) solid 0.15rem;
|
||||
border-radius: 8px/9px;
|
||||
}
|
||||
.desktop button:hover,
|
||||
.desktop .switch:hover,
|
||||
.desktop .checkbox:hover,
|
||||
|
@ -198,7 +216,7 @@ button:active,
|
|||
.popup.small .switch {
|
||||
background: var(--accent-button-elevated);
|
||||
}
|
||||
.popup.small .switch:hover {
|
||||
.desktop .popup.small .switch:hover {
|
||||
background: var(--accent-hover-elevated);
|
||||
}
|
||||
.switch.text-backdrop,
|
||||
|
@ -267,7 +285,6 @@ button:active,
|
|||
}
|
||||
.box {
|
||||
background: var(--background);
|
||||
border: var(--glass) solid .2rem;
|
||||
color: var(--accent);
|
||||
}
|
||||
#url-input-area {
|
||||
|
@ -375,7 +392,8 @@ button:active,
|
|||
max-height: 95%;
|
||||
opacity: 0;
|
||||
transform: translate(-50%,-48%)scale(.95);
|
||||
box-shadow: 0 0 20px 0 var(--accent-hover-transparent);
|
||||
box-shadow: 0 0 0 0.2rem var(--glass) inset,
|
||||
0 0 20px 0 var(--accent-hover-transparent);
|
||||
}
|
||||
.popup.visible {
|
||||
visibility: visible;
|
||||
|
@ -404,7 +422,6 @@ button:active,
|
|||
.popup.small {
|
||||
width: 20%;
|
||||
box-shadow: 0px 0px 60px 0px var(--accent-hover);
|
||||
border: var(--accent-highlight) solid 0.15rem;
|
||||
padding: 1.7rem;
|
||||
transform: translate(-50%,-50%)scale(.95);
|
||||
pointer-events: all;
|
||||
|
@ -530,7 +547,6 @@ button:active,
|
|||
z-index: 999;
|
||||
padding-top: calc(env(safe-area-inset-top)/2 + 1.7rem);
|
||||
width: 100%;
|
||||
border-bottom: var(--accent-highlight) solid 0.1rem;
|
||||
}
|
||||
.settings-category {
|
||||
padding-bottom: 1rem;
|
||||
|
@ -629,7 +645,6 @@ button:active,
|
|||
width: auto;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
overflow-x: scroll;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.switches .switch {
|
||||
|
@ -672,7 +687,6 @@ button:active,
|
|||
width: 100%;
|
||||
padding-top: 0.2rem;
|
||||
padding-bottom: 1.7rem;
|
||||
border-top: var(--accent-highlight) solid 0.1rem;
|
||||
}
|
||||
.popup-tabs-child {
|
||||
width: 100%;
|
||||
|
@ -797,12 +811,16 @@ button:active,
|
|||
width: 100%;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding-top: calc(env(safe-area-inset-top) + 1rem);
|
||||
}
|
||||
.urgent-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.no-transparency .glass-bkg,
|
||||
.no-transparency #popup-backdrop {
|
||||
backdrop-filter: none;
|
||||
|
@ -903,13 +921,17 @@ button:active,
|
|||
.scrollable #popup-content {
|
||||
border-radius: 8px / 9px;
|
||||
}
|
||||
#popup-header {
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
#popup-header .glass-bkg {
|
||||
border-top-left-radius: 8px 9px;
|
||||
border-top-right-radius: 8px 9px;
|
||||
border-bottom: var(--accent-highlight) solid 0.1rem;
|
||||
top: -1px;
|
||||
}
|
||||
#popup-tabs {
|
||||
border-bottom-left-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
#popup-tabs .glass-bkg {
|
||||
border-bottom-left-radius: 8px 9px;
|
||||
border-bottom-right-radius: 8px 9px;
|
||||
border-top: var(--accent-highlight) solid 0.1rem;
|
||||
bottom: -1px;
|
||||
}
|
||||
.switches .first {
|
||||
border-top-left-radius: 5px 6px;
|
||||
|
@ -1005,87 +1027,6 @@ button:active,
|
|||
width: calc(100% - 1.3rem);
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 320px) {
|
||||
:root {
|
||||
--gap: 0.38rem;
|
||||
--gap-no-icon: 0.38rem;
|
||||
--line-height: 1.2rem;
|
||||
}
|
||||
#popup-title {
|
||||
font-size: 1.07rem;
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
.checkbox {
|
||||
width: calc(100% - 1rem);
|
||||
}
|
||||
.footer-button,
|
||||
#audioMode-false,
|
||||
#audioMode-true,
|
||||
#paste {
|
||||
font-size: 0!important;
|
||||
}
|
||||
.footer-button .emoji,
|
||||
#audioMode-false .emoji,
|
||||
#audioMode-true .emoji,
|
||||
#paste .emoji {
|
||||
margin-right: 0;
|
||||
}
|
||||
.switch,
|
||||
.checkbox,
|
||||
.category-title,
|
||||
.subtitle,
|
||||
#popup-desc,
|
||||
.collapse-title {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
.collapse-header {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
#popup-above-title,
|
||||
#url-input-area {
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
.explanation {
|
||||
font-size: 0.6rem;
|
||||
margin-top: 0.5rem;
|
||||
line-height: 1rem!important;
|
||||
}
|
||||
#popup-desc {
|
||||
line-height: 1.2rem;
|
||||
font-size: 0.64rem;
|
||||
}
|
||||
.changelog-subtitle, #popup-subtitle {
|
||||
font-size: 0.8rem!important;
|
||||
}
|
||||
.category-title {
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
.emoji {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
}
|
||||
.desc-padding {
|
||||
padding-bottom: 0.8rem;
|
||||
}
|
||||
#logo {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.popup,
|
||||
.popup.scrollable,
|
||||
.popup.small {
|
||||
height: 98%;
|
||||
}
|
||||
[type=checkbox] {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border: 0.12rem solid var(--accent);
|
||||
}
|
||||
[type=checkbox]:before {
|
||||
transform: scaleY(.8)scaleX(.7)rotate(45deg);
|
||||
left: 3.4px;
|
||||
top: -2px;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 720px) {
|
||||
#cobalt-main-box {
|
||||
width: calc(100% - (0.7rem * 2));
|
||||
|
@ -1124,10 +1065,20 @@ button:active,
|
|||
padding-top: calc(env(safe-area-inset-bottom)/2 + 1rem);
|
||||
}
|
||||
.popup,
|
||||
#popup-header,
|
||||
#popup-tabs {
|
||||
#popup-header .glass-bkg,
|
||||
#popup-tabs .glass-bkg,
|
||||
.glass-bkg.small {
|
||||
border-radius: 0;
|
||||
}
|
||||
#popup-tabs .glass-bkg {
|
||||
bottom: 0;
|
||||
}
|
||||
.switches {
|
||||
overflow-x: scroll;
|
||||
}
|
||||
.checkbox {
|
||||
margin-right: 0;
|
||||
}
|
||||
.popup.center {
|
||||
top: unset;
|
||||
left: unset;
|
||||
|
@ -1141,11 +1092,13 @@ button:active,
|
|||
left: 0;
|
||||
transform: none;
|
||||
position: absolute;
|
||||
border: none;
|
||||
border-top: var(--accent-highlight) solid 0.15rem;
|
||||
padding-bottom: calc(env(safe-area-inset-bottom)/2 + 1.7rem);
|
||||
transform: translateY(30rem);
|
||||
}
|
||||
.glass-bkg.small {
|
||||
border: none;
|
||||
border-top: var(--accent-highlight) solid 0.15rem;
|
||||
}
|
||||
.popup.small.visible {
|
||||
transform: none;
|
||||
transition: transform 200ms cubic-bezier(0.075, 0.82, 0.165, 1), opacity 130ms ease-in-out;
|
||||
|
@ -1173,6 +1126,7 @@ button:active,
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
box-shadow: none;
|
||||
}
|
||||
#popup-tabs {
|
||||
padding-bottom: calc(env(safe-area-inset-bottom)/2 + 1.5rem);
|
||||
|
|
|
@ -68,17 +68,19 @@ export function popup(obj) {
|
|||
}
|
||||
return `
|
||||
${obj.standalone ? `<div id="popup-${obj.name}" class="popup center${!obj.buttonOnly ? " box": ''}${classes.length > 0 ? ' ' + classes.join(' ') : ''}">` : ''}
|
||||
<div id="popup-header" class="popup-header${!obj.buttonOnly ? " glass-bkg": ''}">
|
||||
<div id="popup-header" class="popup-header">
|
||||
<div id="popup-header-contents">
|
||||
${obj.buttonOnly ? obj.header.emoji : ``}
|
||||
${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>
|
||||
${!obj.buttonOnly ? `<div class="glass-bkg alone"></div>`: ''}
|
||||
</div>
|
||||
<div id="popup-content" class="popup-content-inner">
|
||||
${body}${obj.buttonOnly ? `<button id="close-error" class="switch" onclick="popup('${obj.name}', 0)">${obj.buttonText}</button>` : ''}
|
||||
</div>
|
||||
${classes.includes("small") ? `<div class="glass-bkg small"></div>`: ''}
|
||||
${obj.standalone ? `</div>` : ''}`
|
||||
}
|
||||
|
||||
|
@ -97,14 +99,18 @@ export function multiPagePopup(obj) {
|
|||
return `
|
||||
<div id="popup-${obj.name}" class="popup center box scrollable">
|
||||
<div id="popup-content">
|
||||
${obj.header ? `<div id="popup-header" class="popup-header glass-bkg">
|
||||
${obj.header ? `<div id="popup-header" class="popup-header">
|
||||
<div id="popup-header-contents">
|
||||
${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>
|
||||
<div class="glass-bkg alone"></div>
|
||||
</div>` : ''}${tabContent}</div>
|
||||
<div id="popup-tabs" class="switches popup-tabs glass-bkg"><div class="switches popup-tabs-child">${tabs}</div></div>
|
||||
<div id="popup-tabs" class="switches popup-tabs">
|
||||
<div class="switches popup-tabs-child">${tabs}</div>
|
||||
<div class="glass-bkg alone"></div>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
export function collapsibleList(arr) {
|
||||
|
@ -136,15 +142,19 @@ export function popupWithBottomButtons(obj) {
|
|||
return `
|
||||
<div id="popup-${obj.name}" class="popup center box scrollable">
|
||||
<div id="popup-content">
|
||||
${obj.header ? `<div id="popup-header" class="popup-header glass-bkg">
|
||||
${obj.header ? `<div id="popup-header" class="popup-header">
|
||||
<div id="popup-header-contents">
|
||||
${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>` : ''}
|
||||
${obj.header.explanation ? `<div class="explanation">${obj.header.explanation}</div>` : ''}
|
||||
</div>
|
||||
<div class="glass-bkg alone"></div>
|
||||
</div>` : ''}${obj.content}</div>
|
||||
<div id="popup-tabs" class="switches popup-tabs glass-bkg"><div id="picker-buttons" class="switches popup-tabs-child">${tabs}</div></div>
|
||||
<div id="popup-tabs" class="switches popup-tabs">
|
||||
<div id="picker-buttons" class="switches popup-tabs-child">${tabs}</div>
|
||||
<div class="glass-bkg alone"></div>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
export function socialLink(emji, name, handle, url) {
|
||||
|
@ -205,7 +215,9 @@ export function celebrationsEmoji() {
|
|||
}
|
||||
export function urgentNotice(obj) {
|
||||
if (obj.visible) {
|
||||
return `<div id="urgent-notice" class="urgent-notice explanation" onclick="${obj.action}">${emoji(obj.emoji, 18)} ${obj.text}</div>`
|
||||
return `<div id="urgent-notice" class="urgent-notice explanation">` +
|
||||
`<span class="urgent-text" onclick="${obj.action}">${emoji(obj.emoji, 18)} ${obj.text}</span>` +
|
||||
`</div>`
|
||||
}
|
||||
return ``
|
||||
}
|
||||
|
|
|
@ -469,7 +469,7 @@ export default function(obj) {
|
|||
name: "download",
|
||||
standalone: true,
|
||||
buttonOnly: true,
|
||||
classes: ["small", "glass-bkg"],
|
||||
classes: ["small"],
|
||||
header: {
|
||||
closeAria: t('AccessibilityGoBack'),
|
||||
emoji: emoji("🐱", 78, 1, 1),
|
||||
|
@ -490,7 +490,7 @@ export default function(obj) {
|
|||
name: "error",
|
||||
standalone: true,
|
||||
buttonOnly: true,
|
||||
classes: ["small", "glass-bkg"],
|
||||
classes: ["small"],
|
||||
header: {
|
||||
closeAria: t('AccessibilityGoBack'),
|
||||
title: t('TitlePopupError'),
|
||||
|
@ -503,10 +503,10 @@ export default function(obj) {
|
|||
<div id="popup-backdrop" onclick="hideAllPopups()"></div>
|
||||
<div id="home" style="visibility:hidden">
|
||||
${urgentNotice({
|
||||
emoji: "🔗",
|
||||
text: t("UrgentFeatureUpdate71"),
|
||||
emoji: "💖",
|
||||
text: t("UrgentThanks"),
|
||||
visible: true,
|
||||
action: "popup('about', 1, 'changelog')"
|
||||
action: "popup('about', 1, 'donate')"
|
||||
})}
|
||||
<div id="cobalt-main-box" class="center">
|
||||
<div id="logo">${t("AppTitleCobalt")}</div>
|
||||
|
|
|
@ -3,10 +3,16 @@ export default function (inHost, inURL) {
|
|||
let url = String(inURL);
|
||||
|
||||
switch(host) {
|
||||
case "youtube":
|
||||
if (url.startsWith("https://youtube.com/live/") || url.startsWith("https://www.youtube.com/live/")) {
|
||||
url = url.split("?")[0].replace("www.", "");
|
||||
url = `https://youtube.com/watch?v=${url.replace("https://youtube.com/live/", "")}`
|
||||
}
|
||||
break;
|
||||
case "youtu":
|
||||
if (url.startsWith("https://youtu.be/")) {
|
||||
host = "youtube";
|
||||
url = `https://youtube.com/watch?v=${url.replace("https://youtu.be/", "")}`;
|
||||
url = `https://youtube.com/watch?v=${url.replace("https://youtu.be/", "")}`
|
||||
}
|
||||
break;
|
||||
case "vxtwitter":
|
||||
|
|
|
@ -16,7 +16,8 @@ export async function streamDefault(streamInfo, res) {
|
|||
res.setHeader('Content-disposition', `attachment; filename="${streamInfo.isAudioOnly ? `${streamInfo.filename}.${streamInfo.audioFormat}` : regFilename}"`);
|
||||
|
||||
const { body: stream, headers } = await request(streamInfo.urls, {
|
||||
headers: { 'user-agent': genericUserAgent }
|
||||
headers: { 'user-agent': genericUserAgent },
|
||||
maxRedirections: 16
|
||||
});
|
||||
|
||||
res.setHeader('content-type', headers['content-type']);
|
||||
|
@ -33,7 +34,9 @@ export async function streamLiveRender(streamInfo, res) {
|
|||
try {
|
||||
if (streamInfo.urls.length !== 2) return fail(res);
|
||||
|
||||
let { body: audio } = await request(streamInfo.urls[1]);
|
||||
let { body: audio } = await request(streamInfo.urls[1], {
|
||||
maxRedirections: 16
|
||||
});
|
||||
|
||||
let format = streamInfo.filename.split('.')[streamInfo.filename.split('.').length - 1],
|
||||
args = [
|
||||
|
|
|
@ -97,7 +97,7 @@
|
|||
}
|
||||
}, {
|
||||
"name": "retweeted video",
|
||||
"url": "https://twitter.com/hugekiwinuts/status/1618671150829309953?s=46&t=gItGzgwGQQJJaJrO6qc1Pg",
|
||||
"url": "https://twitter.com/uwukko/status/1696901469633421344",
|
||||
"params": {},
|
||||
"expected": {
|
||||
"code": 200,
|
||||
|
@ -465,6 +465,14 @@
|
|||
"code": 200,
|
||||
"status": "stream"
|
||||
}
|
||||
}, {
|
||||
"name": "live link, defaults",
|
||||
"url": "https://www.youtube.com/live/ENxZS6PUDuI?feature=shared",
|
||||
"params": {},
|
||||
"expected": {
|
||||
"code": 200,
|
||||
"status": "stream"
|
||||
}
|
||||
}, {
|
||||
"name": "inexistent video",
|
||||
"url": "https://youtube.com/watch?v=gnjuHYWGEW",
|
||||
|
|
Loading…
Reference in a new issue