Merge branch 'current' into disable-metadata

This commit is contained in:
wukko 2023-09-06 20:05:16 +06:00 committed by GitHub
commit bfde9aab39
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 156 additions and 119 deletions

55
.github/workflows/docker.yml vendored Normal file
View 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 }}

View file

@ -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",

View file

@ -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`))

View file

@ -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",

View file

@ -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);

View file

@ -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 ``
}

View file

@ -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>

View file

@ -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":

View file

@ -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 = [

View file

@ -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",