beginning of 2.2

- added download popup to solve the issue with downloads on ios
- merged big and small popups into one
- made buttons in donation menu act like buttons
- began to clean up localisation
- added ability to embed repo url into localisation strings
- moved ffmpeg args to config for more flexibility (and hopefully future changes)
- removed error response in stream that could result in a crash
- removed notice for ios users from about cause it's no longer relevant
- made error popup look and act like the rest
- a tiny bit of clean up
- ill do better changelog tomorrow i think
This commit is contained in:
wukko
2022-07-14 02:32:00 +06:00
parent 78176be045
commit 137184d469
17 changed files with 201 additions and 200 deletions

View File

@@ -14,5 +14,6 @@ let supportedLanguages = config.supportedLanguages
let quality = config.quality
let internetExplorerRedirect = config.internetExplorerRedirect
let donations = config.donations
let ffmpegArgs = config.ffmpegArgs
export {appName, version, streamLifespan, maxVideoDuration, genericUserAgent, repo, authorInfo, services, supportedLanguages, quality, internetExplorerRedirect, donations}
export {appName, version, streamLifespan, maxVideoDuration, genericUserAgent, repo, authorInfo, services, supportedLanguages, quality, internetExplorerRedirect, donations, ffmpegArgs}

View File

@@ -13,10 +13,12 @@ let enabledServices = Object.keys(s).filter((p) => {
return p
}
}).join(', ')
let donate = ``
for (let i in donations) {
donate += `<div class="subtitle">${i} (${loc("en", 'desc', 'clicktocopy').trim()})</div><div id="don-${i}" class="text-to-copy" onClick="copy('don-${i}')">${donations[i]}</div>`
donate += `<div class="subtitle">${i} (${loc("en", 'desc', 'clickToCopy').trim()})</div><div id="don-${i}" class="text-to-copy" onClick="copy('don-${i}')">${donations[i]}</div>`
}
export default function(obj) {
let isIOS = obj.useragent.toLowerCase().match("iphone os")
try {
@@ -49,33 +51,48 @@ export default function(obj) {
<noscript><div style="margin: 2rem;">${loc(obj.lang, 'desc', 'noScript')}</div></noscript>
</head>
<body id="cobalt-body">
<div id="popup-about" class="popup-narrow center box" style="visibility: hidden;">
<div id="popup-download" class="popup center box" style="visibility: hidden;">
<div id="popup-header" class="popup-header">
<button id="close" class="button mono" onclick="popup('download', 0)" aria-label="${loc(obj.lang, 'accessibility', 'close')}">x</button>
<div id="title" class="popup-subtitle">${loc(obj.lang, 'title', 'download')}</div>
</div>
<div id="content" class="popup-content">
<div id="theme-switcher" class="switch-container small-padding">
<div class="subtitle">${loc(obj.lang, 'title', 'pickDownload')}</div>
<div class="switches">
<a id="pd-download" class="switch full space-right" target="_blank"">${loc(obj.lang, 'desc', 'download')}</a>
<div id="pd-copy" class="switch full">${loc(obj.lang, 'desc', 'copy')}</div>
</div>
</div>
<div id="desc" class="explanation about-padding">${isIOS ? loc(obj.lang, 'desc', 'iosDownload') : loc(obj.lang, 'desc', 'normalDownload')}</div>
</div>
</div>
<div id="popup-about" class="popup center box" style="visibility: hidden;">
<div id="popup-header" class="popup-header">
<button id="close" class="button mono" onclick="popup('about', 0)" aria-label="${loc(obj.lang, 'accessibility', 'close')}">x</button>
<div id="title" class="popup-title">${loc(obj.lang, 'title', 'about')}</div>
</div>
<div id="content" class="popup-content with-footer">
<div id="desc" class="popup-desc about-padding">${loc(obj.lang, 'desc', 'about')}</div>
<div id="desc" class="popup-desc about-padding">${loc(obj.lang, 'desc', 'support_1')} ${enabledServices}.</div>
${isIOS ? `<div id="desc" class="popup-subtitle popup-desc"><span class="text-backdrop">${loc(obj.lang, 'desc', 'iosTitle')}</span></div><div id="desc" class="popup-desc about-padding">${loc(obj.lang, 'desc', 'ios')}</div>`: ``}
<div id="desc" class="popup-desc"><a class="text-backdrop" href="${repo}">${loc(obj.lang, 'desc', 'sourcecode')}</a></div>
<div id="desc" class="popup-desc about-padding">${loc(obj.lang, 'desc', 'aboutSummary')}</div>
<div id="desc" class="popup-desc about-padding">${loc(obj.lang, 'desc', 'supportedServices')} ${enabledServices}.</div>
<div id="desc" class="popup-desc"><a class="text-backdrop" href="${repo}">${loc(obj.lang, 'desc', 'sourceCode')}</a></div>
</div>
<div id="popup-footer" class="popup-footer">
<a id="popup-bottom" class="popup-footer-content" href="${authorInfo.link}">${loc(obj.lang, 'desc', 'popupBottom')}</a>
</div>
</div>
<div id="popup-changelog" class="popup-narrow center box" style="visibility: hidden;">
<div id="popup-changelog" class="popup center box" style="visibility: hidden;">
<div id="popup-header" class="popup-header">
<button id="close" class="button mono" onclick="popup('changelog', 0)" aria-label="${loc(obj.lang, 'accessibility', 'close')}">x</button>
<div id="title" class="popup-title">${loc(obj.lang, 'title', 'changelog')}</div>
<div id="desc" class="popup-subtitle">${loc(obj.lang, 'changelog', 'subtitle')}</div>
<div id="desc" class="popup-subtitle">${loc(obj.lang, 'changelog', 'subtitle')}(${obj.hash})</div>
</div>
<div id="content" class="popup-content">
<div id="desc" class="popup-desc about-padding">${loc(obj.lang, 'changelog', 'text')}</div>
<div id="desc" class="popup-desc"><a class="text-backdrop" href="${repo}">${loc(obj.lang, 'changelog', 'github')}</a></div>
<div id="desc" class="popup-desc"><a class="text-backdrop" href="${repo}/commits">${loc(obj.lang, 'changelog', 'github')}</a></div>
</div>
</div>
<div id="popup-donate" class="popup-narrow scrollable center box" style="visibility: hidden;">
<div id="popup-donate" class="popup scrollable center box" style="visibility: hidden;">
<div id="popup-header" class="popup-header">
<button id="close" class="button mono" onclick="popup('donate', 0)" aria-label="${loc(obj.lang, 'accessibility', 'close')}">x</button>
<div id="title" class="popup-title">${loc(obj.lang, 'title', 'donate')}</div>
@@ -87,7 +104,7 @@ export default function(obj) {
<div id="desc" class="popup-desc"><a class="text-backdrop" href="${authorInfo.contact}">${loc(obj.lang, 'desc', 'donateDm')}</a></div>
</div>
</div>
<div id="popup-settings" class="popup-narrow scrollable center box" style="visibility: hidden;">
<div id="popup-settings" class="popup scrollable center box" style="visibility: hidden;">
<div id="popup-header" class="popup-header">
<button id="close" class="button mono" onclick="popup('settings', 0)" aria-label="${loc(obj.lang, 'accessibility', 'close')}">x</button>
<div id="version" class="popup-above-title">v.${version} ~ ${obj.hash}</div>
@@ -95,35 +112,41 @@ export default function(obj) {
</div>
<div id="content" class="popup-content">
<div id="settings-appearance" class="settings-category">
<div class="title">${loc(obj.lang, 'settings', 'category-appearance')}</div>
<div class="title">${loc(obj.lang, 'settings', 'appearance')}</div>
<div class="settings-category-content">
<div id="theme-switcher" class="switch-container">
<div class="subtitle">${loc(obj.lang, 'settings', 'theme')}</div>
<div class="switches">
<div id="theme-auto" class="switch full" onclick="changeSwitcher('theme', 'auto', 1)">${loc(obj.lang, 'settings', 'theme-auto')}</div>
<div id="theme-dark" class="switch" onclick="changeSwitcher('theme', 'dark', 1)">${loc(obj.lang, 'settings', 'theme-dark')}</div>
<div id="theme-light" class="switch full" onclick="changeSwitcher('theme', 'light', 1)">${loc(obj.lang, 'settings', 'theme-light')}</div>
<div id="theme-auto" class="switch full" onclick="changeSwitcher('theme', 'auto', 1)">${loc(obj.lang, 'settings', 'themeAuto')}</div>
<div id="theme-dark" class="switch" onclick="changeSwitcher('theme', 'dark', 1)">${loc(obj.lang, 'settings', 'themeDark')}</div>
<div id="theme-light" class="switch full" onclick="changeSwitcher('theme', 'light', 1)">${loc(obj.lang, 'settings', 'themeLight')}</div>
</div>
</div>
<div class="subtitle">${loc(obj.lang, 'settings', 'misc')}</div>
<label class="checkbox">
<input id="always-visible-button" type="checkbox" aria-label="${loc(obj.lang, 'accessibility', 'alwaysVisibleButton')}" onclick="checkbox('alwaysVisibleButton', 'always-visible-button')">${loc(obj.lang, 'settings', 'always-visible')}
<input id="alwaysVisibleButton" type="checkbox" aria-label="${loc(obj.lang, 'accessibility', 'alwaysVisibleButton')}" onclick="checkbox('alwaysVisibleButton', 'always-visible-button')">
<span>${loc(obj.lang, 'settings', 'alwaysVisibleButton')}</span>
</label>
</div>
</div>
<div id="settings-quality" class="settings-category">
<div id="settings-downloads" class="settings-category">
<div class="title">${loc(obj.lang, 'settings', 'general')}</div>
<div class="settings-category-content">
<div id="quality-switcher" class="switch-container">
<div class="subtitle">${loc(obj.lang, 'settings', 'quality')}</div>
<div class="switches">
<div id="quality-max" class="switch full" onclick="changeSwitcher('quality', 'max', 1)">${loc(obj.lang, 'settings', 'q-max')}</div>
<div id="quality-hig" class="switch" onclick="changeSwitcher('quality', 'hig', 1)">${loc(obj.lang, 'settings', 'q-hig')}(${quality.hig}p)</div>
<div id="quality-mid" class="switch full" onclick="changeSwitcher('quality', 'mid', 1)">${loc(obj.lang, 'settings', 'q-mid')}(${quality.mid}p)</div>
<div id="quality-low" class="switch right" onclick="changeSwitcher('quality', 'low', 1)">${loc(obj.lang, 'settings', 'q-low')}(${quality.low}p)</div>
<div id="quality-max" class="switch full" onclick="changeSwitcher('quality', 'max', 1)">${loc(obj.lang, 'settings', 'qmax')}</div>
<div id="quality-hig" class="switch" onclick="changeSwitcher('quality', 'hig', 1)">${loc(obj.lang, 'settings', 'qhig')}(${quality.hig}p)</div>
<div id="quality-mid" class="switch full" onclick="changeSwitcher('quality', 'mid', 1)">${loc(obj.lang, 'settings', 'qmid')}(${quality.mid}p)</div>
<div id="quality-low" class="switch right" onclick="changeSwitcher('quality', 'low', 1)">${loc(obj.lang, 'settings', 'qlow')}(${quality.low}p)</div>
</div>
<div class="explanation">${loc(obj.lang, 'settings', 'q-desc')}</div>
<div class="explanation">${loc(obj.lang, 'settings', 'qualityDesc')}</div>
</div>
${!isIOS ? `<div class="subtitle">${loc(obj.lang, 'settings', 'extra')}</div>
<label class="checkbox">
<input id="downloadPopup" type="checkbox" aria-label="${loc(obj.lang, 'accessibility', 'downloadPopup')}" onclick="checkbox('downloadPopup', 'always-visible-button')">
<span>${loc(obj.lang, 'settings', 'downloadPopupButton')}</span>
</label>` : ``}
</div>
</div>
<div id="settings-youtube" class="settings-category">
@@ -136,16 +159,20 @@ export default function(obj) {
<div id="youtubeFormat-webm" class="switch" onclick="changeSwitcher('youtubeFormat', 'webm', 1)">webm</div>
<div id="youtubeFormat-audio" class="switch full" onclick="changeSwitcher('youtubeFormat', 'audio', 1)">audio only</div>
</div>
<div class="explanation">${loc(obj.lang, 'settings', 'format-info')}</div>
<div class="explanation">${loc(obj.lang, 'settings', 'formatInfo')}</div>
</div>
</div>
</div>
</div>
</div>
<div id="popup-error" class="popup center box" style="visibility: hidden;">
<button id="close" class="button mono" onclick="popup('error', 0)" aria-label="${loc(obj.lang, 'accessibility', 'close')}">x</button>
<div id="title" class="popup-title">${loc(obj.lang, 'title', 'error')}</div>
<div id="desc-error" class="popup-desc"></div>
<div id="popup-header" class="popup-header">
<button id="close" class="button mono" onclick="popup('error', 0)" aria-label="${loc(obj.lang, 'accessibility', 'close')}">x</button>
<div id="title" class="popup-title">${loc(obj.lang, 'title', 'error')}</div>
</div>
<div id="content" class="popup-content">
<div id="desc-error" class="popup-desc"></div>
</div>
</div>
<div id="popup-backdrop" style="visibility: hidden;"></div>
<div id="cobalt-main-box" class="center box" style="visibility: hidden;">

View File

@@ -1,7 +1,7 @@
import { spawn } from "child_process";
import ffmpeg from "ffmpeg-static";
import got from "got";
import { genericUserAgent } from "../config.js";
import { ffmpegArgs, genericUserAgent } from "../config.js";
import { msToTime } from "../sub/api-helper.js";
import { internalError } from "../sub/errors.js";
import loc from "../sub/loc.js";
@@ -16,11 +16,9 @@ export async function streamDefault(streamInfo, res) {
isStream: true
});
stream.pipe(res).on('error', (err) => {
internalError(res);
throw Error("File stream pipe error.");
});
stream.on('error', (err) => {
internalError(res);
throw Error("File stream error.")
});
} catch (e) {
@@ -42,18 +40,9 @@ export async function streamLiveRender(streamInfo, res) {
'-i', 'pipe:4',
'-map', '0:v',
'-map', '1:a',
'-c:v', 'copy',
'-c:a', 'copy',
];
if (format == 'mp4') {
args.push('-movflags', 'frag_keyframe+empty_moov');
if (streamInfo.service == "youtube") {
args.push('-t', msToTime(streamInfo.time));
}
} else if (format == 'webm') {
args.push('-t', msToTime(streamInfo.time));
}
args.push('-f', format, 'pipe:5');
args = args.concat(ffmpegArgs[format])
args.push('-t', msToTime(streamInfo.time), '-f', format, 'pipe:5');
const ffmpegProcess = spawn(ffmpeg, args, {
windowsHide: true,
stdio: [
@@ -63,25 +52,20 @@ export async function streamLiveRender(streamInfo, res) {
});
ffmpegProcess.on('error', (err) => {
ffmpegProcess.kill();
internalError(res);
});
audio.on('error', (err) => {
ffmpegProcess.kill();
internalError(res);
});
video.on('error', (err) => {
ffmpegProcess.kill();
internalError(res);
});
res.setHeader('Content-Disposition', `attachment; filename="${streamInfo.filename}"`);
ffmpegProcess.stdio[5].pipe(res);
video.pipe(ffmpegProcess.stdio[3]).on('error', (err) => {
ffmpegProcess.kill();
internalError(res);
});
audio.pipe(ffmpegProcess.stdio[4]).on('error', (err) => {
ffmpegProcess.kill();
internalError(res);
});
} else {
res.status(400).json({ status: "error", text: loc('en', 'apiError', 'corruptedVideo') });
@@ -113,17 +97,14 @@ export async function streamAudioOnly(streamInfo, res) {
});
ffmpegProcess.on('error', (err) => {
ffmpegProcess.kill();
internalError(res);
});
audio.on('error', (err) => {
ffmpegProcess.kill();
internalError(res);
});
res.setHeader('Content-Disposition', `attachment; filename="${streamInfo.filename}"`);
ffmpegProcess.stdio[4].pipe(res);
audio.pipe(ffmpegProcess.stdio[3]).on('error', (err) => {
ffmpegProcess.kill();
internalError(res);
});
} catch (e) {
internalError(res);

View File

@@ -1,5 +1,8 @@
import { execSync } from "child_process";
export default function() {
export function shortCommit() {
return execSync('git rev-parse --short HEAD').toString().trim()
}
export function getCommitInfo() {
return execSync(`git show -s --format='%s;;;%B'`).toString().trim().slice(1,-1).replace(/[\r\n]/gm, '\n').split(';;;')
}

View File

@@ -1,4 +1,4 @@
import { supportedLanguages, appName } from "../config.js";
import { supportedLanguages, appName, repo } from "../config.js";
import loadJson from "./load-json.js";
export default function(lang, cat, string, replacement) {
@@ -8,7 +8,7 @@ export default function(lang, cat, string, replacement) {
try {
let str = loadJson(`./strings/${lang}/${cat}.json`);
if (str && str[string]) {
let s = str[string].replace(/\n/g, '<br/>').replace(/{appName}/g, appName)
let s = str[string].replace(/\n/g, '<br/>').replace(/{appName}/g, appName).replace(/{repo}/g, repo)
if (replacement) {
s = s.replace(/{s}/g, replacement)
}