probably the biggest update in history of cobalt
This commit is contained in:
wukko 2022-08-12 19:36:19 +06:00
parent 2fae43d890
commit 54c14232d5
42 changed files with 896 additions and 385 deletions

View file

@ -43,12 +43,14 @@ Take English or Russian localization from [this directory](https://github.com/wu
- You can rephrase sentences as long as they keep the same sense.
- You can add wordplays or puns if it feels natural to do so.
- Even though I love cursing, keep that away from translations.
- Always check if there are issues in UI with your localization.
- There's no need to translate `ChangelogContentTitle` and `ChangelogContent`, because those are very often changed.
- Add "(in english)" to `ChangelogLastCommit` and `ChangelogLastMajor`, because those are almost always kept exclusively in English. Remove that phrase if you do translate major update changelog.
- Be nice.
## TO-DO
### Services
- [x] Tumblr support
- [ ] niconico support
- [ ] Instagram support
- [ ] SoundCloud support
@ -57,11 +59,9 @@ Take English or Russian localization from [this directory](https://github.com/wu
- [ ] Add an option to keep watermark on TikTok videos
### Other
- [ ] Add support for emoji in localization
- [ ] Language picker in settings
- [ ] Make switch buttons in settings selectable with keyboard
- [ ] Option to save audios in formats other than original
- [ ] Make cobalt fully PWA compatible (add a service worker)
- [ ] Make page rendering module more versatile
## Host an instance yourself
Code might be a little messy, but I do my best to improve it with every commit.
@ -93,5 +93,8 @@ Setup script installs all needed `npm` dependencies, but you have to install `No
## Disclaimer
This is my passion project, so update scheduele depends solely on my motivation. Don't expect any consistency in that.
## Third party stuff
[Fluent Emoji](https://github.com/microsoft/fluentui-emoji) by Microsoft.
## License
cobalt is under [AGPL-3.0](https://github.com/wukko/cobalt/blob/current/LICENSE).

View file

@ -1,7 +1,7 @@
{
"name": "cobalt",
"description": "save what you love",
"version": "2.2.9",
"version": "3.0",
"author": "wukko",
"exports": "./src/cobalt.js",
"type": "module",

View file

@ -8,11 +8,11 @@ import rateLimit from "express-rate-limit";
import { shortCommit } from "./modules/sub/currentCommit.js";
import { appName, genericUserAgent, version, internetExplorerRedirect } from "./modules/config.js";
import { getJSON } from "./modules/api.js";
import renderPage from "./modules/pageRender.js";
import renderPage from "./modules/pageRender/page.js";
import { apiJSON, languageCode } from "./modules/sub/utils.js";
import { Bright, Cyan } from "./modules/sub/consoleText.js";
import stream from "./modules/stream/stream.js";
import loc, { loadLoc } from "./localization/manager.js";
import loc from "./localization/manager.js";
import { buildFront } from "./modules/build.js";
const commitHash = shortCommit();
@ -65,8 +65,10 @@ if (fs.existsSync('./.env')) {
req.query.url.trim(),
req.header('x-forwarded-for') ? req.header('x-forwarded-for') : req.ip,
languageCode(req),
req.query.format ? req.query.format.slice(0, 5) : "mp4",
req.query.quality ? req.query.quality.slice(0, 3) : "max"
req.query.format ? req.query.format.slice(0, 5) : "webm",
req.query.quality ? req.query.quality.slice(0, 3) : "max",
req.query.audioFormat ? req.query.audioFormat.slice(0, 4) : false,
req.query.audio ? true : false
)
res.status(j.status).json(j.body);
} else {
@ -79,7 +81,7 @@ if (fs.existsSync('./.env')) {
res.status(200).json({ "status": "continue" });
} else if (req.query.t) {
let ip = req.header('x-forwarded-for') ? req.header('x-forwarded-for') : req.ip
stream(res, ip, req.query.t, req.query.h, req.query.e);
stream(res, ip, req.query.t, req.query.h, req.query.e, languageCode(req));
} else {
let j = apiJSON(0, { t: loc(languageCode(req), 'ErrorNoStreamID') })
res.status(j.status).json(j.body);

View file

@ -13,21 +13,28 @@
"new": "https://vivaldi.com/"
},
"donations": {
"crypto": {
"ethereum": "0x4B4cF23051c78c7A7E0eA09d39099621c46bc302",
"bitcoin": "bc1q64amsn0wd60urem3jkhpywed8q8kqwssw6ta5j",
"litecoin": "ltc1qvp0xhrk2m7pa6p6z844qcslfyxv4p3vf95rhna",
"bitcoin cash": "bitcoincash:qph0d7d02mvg5xxqjwjv5ahjx2254yx5kv0zfg0xsj",
"monero": "4B1SNB6s8Pq1hxjNeKPEe8Qa8EP3zdL16Sqsa7QDoJcUecKQzEj9BMxWnEnTGu12doKLJBKRDUqnn6V9qfSdXpXi3Nw5Uod"
},
"other": {
"boosty": "https://boosty.to/wukko"
}
},
"quality": {
"hig": "1080",
"mid": "720",
"low": "480"
},
"supportedAudio": ["mp3", "ogg", "opus"],
"ffmpegArgs": {
"webm": ["-c:v", "copy", "-c:a", "copy"],
"mp4": ["-c:v", "copy", "-c:a", "copy", "-movflags", "frag_keyframe+empty_moov"],
"bst": ["-c:a", "copy"],
"mp3": ["-ar", "48000", "-ac", "2", "-b:a", "320k"]
"copy": ["-c:a", "copy"],
"audio": ["-ar", "48000", "-ac", "2", "-b:a", "320k"],
"m4a": ["-movflags", "frag_keyframe+empty_moov"]
}
}

View file

@ -8,7 +8,8 @@
@media (prefers-color-scheme: dark) {
:root {
--accent: rgb(225, 225, 225);
--accent-hover: rgb(20, 20, 20);
--accent-hover: rgb(25, 25, 25);
--accent-button-bg: rgb(20, 20, 20);
--accent-press: rgb(10, 10, 10);
--accent-unhover: rgb(100, 100, 100);
--accent-unhover-2: rgb(110, 110, 110);
@ -18,8 +19,9 @@
@media (prefers-color-scheme: light) {
:root {
--accent: rgb(25, 25, 25);
--accent-hover: rgb(230 230 230);
--accent-press: rgb(240 240 240);
--accent-hover: rgb(225, 225, 225);
--accent-button-bg: rgb(230, 230, 230);
--accent-press: rgb(240, 240, 240);
--accent-unhover: rgb(190, 190, 190);
--accent-unhover-2: rgb(110, 110, 110);
--background: rgb(255, 255, 255);
@ -27,7 +29,8 @@
}
[data-theme="dark"] {
--accent: rgb(225, 225, 225);
--accent-hover: rgb(20, 20, 20);
--accent-hover: rgb(25, 25, 25);
--accent-button-bg: rgb(20, 20, 20);
--accent-press: rgb(10, 10, 10);
--accent-unhover: rgb(100, 100, 100);
--accent-unhover-2: rgb(110, 110, 110);
@ -35,8 +38,9 @@
}
[data-theme="light"] {
--accent: rgb(25, 25, 25);
--accent-hover: rgb(230 230 230);
--accent-press: rgb(240 240 240);
--accent-hover: rgb(225, 225, 225);
--accent-button-bg: rgb(230, 230, 230);
--accent-press: rgb(240, 240, 240);
--accent-unhover: rgb(190, 190, 190);
--accent-unhover-2: rgb(110, 110, 110);
--background: rgb(255, 255, 255);
@ -56,6 +60,7 @@ body {
a {
color: var(--accent);
text-decoration: none;
user-select: none;
}
::placeholder {
color: var(--accent-unhover-2);
@ -79,13 +84,13 @@ a {
width: 15px;
height: 15px;
border: var(--border-15);
background-color: var(--background);
background-color: var(--accent-button-bg);
display: block;
z-index: 5;
position: relative;
}
[type="checkbox"]:checked::before {
box-shadow: inset 0 0 0 0.2rem var(--background);
box-shadow: inset 0 0 0 0.2rem var(--accent-button-bg);
background-color: var(--accent);
}
button {
@ -120,6 +125,7 @@ button:active,
.text-to-copy:active {
background: var(--accent-press);
cursor: pointer;
transform: scale(0.95)
}
input[type="checkbox"] {
cursor: pointer;
@ -202,12 +208,19 @@ input[type="checkbox"] {
text-align: center;
width: 90%;
}
#footer-buttons {
display: inline-flex;
align-items: center;
}
.footer-button {
cursor: pointer;
color: var(--accent-unhover-2);
border: 0.15rem var(--accent-unhover-2) solid;
border: 0.15rem solid var(--accent-unhover-2);
padding: 0.4rem 0.8rem 0.5rem;
margin-bottom: 0.5rem;
margin: 0.4rem;
display: flex;
align-content: center;
align-items: center;
}
.footer-button:hover {
color: var(--accent);
@ -252,31 +265,38 @@ input[type="checkbox"] {
.scrollable .bottom-link {
padding-bottom: 2rem;
}
.changelog-subtitle {
font-size: 1.1rem;
padding-bottom: 0.7rem;
}
.nowrap {
white-space: nowrap;
}
.about-padding {
.no-top-padding {
padding-top: 0!important;
}
.desc-padding {
padding-bottom: 1.5rem;
}
.popup-subtitle {
#popup-subtitle {
font-size: 1.1rem;
padding-bottom: 0.5rem;
padding-bottom: 1rem;
}
.little-subtitle {
font-size: 1.05rem;
}
.popup-desc {
#popup-desc,
#desc-error {
width: 100%;
text-align: left;
float: left;
line-height: 1.7rem;
}
.popup-title {
#popup-title {
font-size: 1.5rem;
margin-bottom: 0.5rem;
line-height: 1.85em;
display: flex;
align-items: center;
}
.popup-footer {
#popup-footer {
bottom: 0;
position: fixed;
margin-bottom: 1.5rem;
@ -290,25 +310,25 @@ input[type="checkbox"] {
border-top: 0.05rem solid var(--accent-unhover-2);
padding-top: 0.4rem;
}
.popup-above-title {
#popup-above-title {
color: var(--accent-unhover-2);
font-size: 0.8rem;
}
.popup-content {
#popup-content {
overflow-x: hidden;
overflow-y: auto;
height: var(--without-padding);
scrollbar-width: none;
}
.popup-header {
#popup-header {
position: relative;
background: var(--background);
z-index: 999;
}
.popup-content.with-footer {
#popup-content.with-footer {
margin-bottom: 3rem;
}
#close {
#popup-close {
cursor: pointer;
float: right;
right: 0rem;
@ -317,13 +337,14 @@ input[type="checkbox"] {
.settings-category {
padding-bottom: 1.2rem;
}
.title {
.category-title {
width: 100%;
text-align: left;
line-height: 1.7rem;
color: var(--accent-unhover-2);
border-bottom: 0.05rem solid var(--accent-unhover-2);
padding-bottom: 0.25rem;
margin-bottom: 1rem;
}
.bottom-margin {
margin-bottom: 1rem;
@ -336,9 +357,9 @@ input[type="checkbox"] {
align-content: center;
padding: 0.6rem;
padding-right: 1rem;
border: var(--border-10);
width: auto;
margin: 0 0.5rem 0.5rem 0;
background: var(--accent-button-bg);
}
.checkbox-label {
line-height: 1.3rem;
@ -352,6 +373,8 @@ input[type="checkbox"] {
line-height: 1.7rem;
padding-bottom: 0.4rem;
color: var(--accent);
}
.subtitle.extra {
margin-top: 1rem;
}
.small-padding .subtitle {
@ -362,30 +385,20 @@ input[type="checkbox"] {
width: 100%;
font-size: 0.8rem;
text-align: left;
line-height: 1.3rem;
line-height: 1.3rem!important;
color: var(--accent-unhover-2);
}
.switch {
border-top: var(--border-10);
border-bottom: var(--border-10);
padding: 0.8rem;
width: 100%;
text-align: center;
color: var(--accent);
background: var(--background);
display: grid;
background: var(--accent-button-bg);
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.switch.full {
border: var(--border-10);
}
.switch.left {
border-left: var(--border-10);
}
.switch.right {
border-right: var(--border-10);
}
.switch.space-right {
margin-right: 1rem
}
@ -399,6 +412,13 @@ input[type="checkbox"] {
width: auto;
flex-direction: row;
flex-wrap: nowrap;
overflow-x: scroll;
}
.autowidth {
width: auto;
}
.bottom-space {
margin-bottom: 2rem;
}
.text-to-copy {
user-select: text;
@ -406,6 +426,26 @@ input[type="checkbox"] {
padding: 1rem;
overflow: auto;
}
#close-bottom {
width: 18%;
margin-left: 1rem;
background: var(--background);
border: var(--border-15);
color: var(--accent);
padding: 0.3rem 0.75rem 0.5rem;
}
.popup-tab-content {
display: none;
}
#popup-tabs {
z-index: 999;
bottom: 0;
position: relative;
width: 100%;
}
.emoji {
margin-right: 0.4rem;
}
/* adapt the page according to screen size */
@media screen and (min-width: 2300px) {
html {
@ -430,7 +470,15 @@ input[type="checkbox"] {
width: 40%;
}
}
@media screen and (max-width: 1024px) {
@media screen and (max-width: 1100px) {
#cobalt-main-box {
width: 70%;
}
.popup {
width: 50%;
}
}
@media screen and (max-width: 1025px) {
#cobalt-main-box {
width: 75%;
}
@ -458,7 +506,7 @@ input[type="checkbox"] {
text-align: center;
}
#cobalt-main-box {
width: 80%;
width: 85%;
display: flex;
border: none;
padding: 0;
@ -473,7 +521,13 @@ input[type="checkbox"] {
padding-bottom: 2rem;
}
}
@media screen and (max-width: 524px) {
@media screen and (max-width: 475px) {
.tab {
font-size: 0;
}
.tab .emoji {
margin-right: 0;
}
#logo-area {
padding-right: 0;
padding-top: 0;

View file

@ -1,11 +1,15 @@
let isIOS = navigator.userAgent.toLowerCase().match("iphone os");
let version = 3
let switchers = {
"theme": ["auto", "light", "dark"],
"youtubeFormat": ["webm", "mp4", "audio"],
"quality": ["max", "hig", "mid", "low"]
"ytFormat": ["webm", "mp4"],
"quality": ["max", "hig", "mid", "low"],
"audioFormat": ["best", "mp3", "ogg", "opus"]
}
let exceptions = {
"youtubeFormat": "mp4"
let exceptions = { // fuck you apple
"ytFormat": "mp4",
"audioFormat": "mp3"
}
function eid(id) {
@ -20,6 +24,9 @@ function disable(id) {
function vis(state) {
return (state === 1) ? "visible" : "hidden";
}
function opposite(state) {
return state == "true" ? "false" : "true";
}
function changeDownloadButton(action, text) {
switch (action) {
case 0:
@ -70,13 +77,41 @@ function detectColorScheme() {
}
document.documentElement.setAttribute("data-theme", theme);
}
function changeTab(evnt, tabId, tabClass) {
let tabcontent = document.getElementsByClassName(`tab-content-${tabClass}`);
let tablinks = document.getElementsByClassName(`tab-${tabClass}`);
for (let i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
for (let i = 0; i < tablinks.length; i++) {
tablinks[i].dataset.enabled = "false";
}
eid(tabId).style.display = "block";
evnt.currentTarget.dataset.enabled = "true";
}
function hideAllPopups() {
let filter = document.getElementsByClassName('popup');
for (let i = 0; i < filter.length; i++) {
filter[i].style.visibility = "hidden";
}
eid("popup-backdrop").style.visibility = "hidden";
}
function popup(type, action, text) {
eid("popup-backdrop").style.visibility = vis(action);
switch (type) {
case "about":
let tabId = text ? text : "changelog";
if (tabId == "changelog") {
localStorage.setItem("changelogStatus", version)
}
eid(`tab-button-${type}-${tabId}`).click();
eid("popup-about").style.visibility = vis(action);
if (!localStorage.getItem("seenAbout")) localStorage.setItem("seenAbout", "true");
break;
case "settings":
eid(`tab-button-${type}-video`).click();
eid("popup-settings").style.visibility = vis(action);
break;
case "error":
eid("desc-error").innerHTML = text;
eid("popup-error").style.visibility = vis(action);
@ -93,17 +128,17 @@ function popup(type, action, text) {
break;
}
}
function changeSwitcher(li, b, u) {
if (u) localStorage.setItem(li, b);
function changeSwitcher(li, b) {
if (b) {
localStorage.setItem(li, b);
for (i in switchers[li]) {
(switchers[li][i] == b) ? enable(`${li}-${b}`) : disable(`${li}-${switchers[li][i]}`)
}
if (li == "theme") detectColorScheme();
} else {
let pref = switchers[li][0];
if (isIOS && exceptions[li]) pref = exceptions[li];
localStorage.setItem(li, pref);
if (isIOS && exceptions[li]) pref = exceptions[li];
for (i in switchers[li]) {
(switchers[li][i] == pref) ? enable(`${li}-${pref}`) : disable(`${li}-${switchers[li][i]}`)
}
@ -132,18 +167,49 @@ function loadSettings() {
if (localStorage.getItem("downloadPopup") == "true" && !isIOS) {
eid("downloadPopup").checked = true;
}
changeSwitcher("theme", localStorage.getItem("theme"))
changeSwitcher("youtubeFormat", localStorage.getItem("youtubeFormat"))
changeSwitcher("quality", localStorage.getItem("quality"))
if (!localStorage.getItem("audioMode")) {
toggle("audioMode")
}
updateToggle("audioMode", localStorage.getItem("audioMode"))
for (let i in switchers) {
changeSwitcher(i, localStorage.getItem(i))
}
}
function checkbox(action) {
if (eid(action).checked) {
localStorage.setItem(action, "true");
if (action == "alwaysVisibleButton") button();
} else {
localStorage.setItem(action, "false");
if (action == "alwaysVisibleButton") button();
}
}
function toggle(toggle) {
let state = localStorage.getItem(toggle);
if (state) {
localStorage.setItem(toggle, opposite(state))
} else {
localStorage.setItem(toggle, "false")
}
updateToggle(toggle, localStorage.getItem(toggle))
}
function updateToggle(toggle, state) {
switch(state) {
case "true":
eid(toggle).innerHTML = loc.toggleAudio;
break;
case "false":
eid(toggle).innerHTML = loc.toggleDefault;
break;
}
}
async function download(url) {
changeDownloadButton(2, '...');
eid("url-input-area").disabled = true;
let format = '';
if (url.includes("youtube.com/") || url.includes("/youtu.be/")) {
format = `&format=${localStorage.getItem("youtubeFormat")}`
}
fetch(`/api/json?quality=${localStorage.getItem("quality")}${format}&url=${encodeURIComponent(url)}`).then(async (response) => {
let audioMode = localStorage.getItem("audioMode");
let format = (url.includes("youtube.com/") && audioMode == "false" || url.includes("/youtu.be/") && audioMode == "false") ? `&format=${localStorage.getItem("ytFormat")}` : '';
let mode = (localStorage.getItem("audioMode") == "true") ? `audio=true` : `quality=${localStorage.getItem("quality")}`;
fetch(`/api/json?audioFormat=${localStorage.getItem("audioFormat")}&${mode}${format}&url=${encodeURIComponent(url)}`).then(async (response) => {
let j = await response.json();
if (j.status != "error" && j.status != "rate-limit") {
if (j.url) {
@ -198,11 +264,16 @@ window.onload = function () {
eid("cobalt-main-box").style.visibility = 'visible';
eid("footer").style.visibility = 'visible';
eid("url-input-area").value = "";
if (!localStorage.getItem("seenAbout")) popup('about', 1);
if (!localStorage.getItem("seenAbout")) {
popup('about', 1, "about");
} else if (localStorage.getItem("changelogStatus") != `${version}` && localStorage.getItem("disableChangelog") != "true") {
popup('about', 1, "changelog");
}
if (isIOS) localStorage.setItem("downloadPopup", "true");
}
eid("url-input-area").addEventListener("keyup", (event) => {
if (event.key === 'Enter') {
eid("download-button").click();
}
if (event.key === 'Enter') eid("download-button").click();
})
document.onkeydown = function(event) {
if (event.key === 'Escape') hideAllPopups();
};

View file

@ -0,0 +1,13 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M27.21 30.0201H9.46004C8.54004 30.0201 6.79004 29.2701 6.79004 28.35V16.74L17.8825 14.8293L28.89 16.74V28.35C28.88 29.2701 28.14 30.0201 27.21 30.0201Z" fill="#6B438B"/>
<path d="M11.96 10.02L9.70792 9.00173L8.25 7.01L12.7766 5.81213L16.4261 5.88593L18.4398 4.3135L22.95 3.12L25.565 4.21237L26.66 6.12L22.1403 7.31911H18.4079L16.4811 8.82053L11.96 10.02Z" fill="#A4AEEB"/>
<path d="M24.5301 16.74L26.9913 14.7543L27.3401 12.88H22.6585L18.8457 14.2289L16.8128 12.88H12.1401L9.61252 14.7543L9.33008 16.74H14.0505L17.8925 15.6004L19.8475 16.74H24.5301Z" fill="#A4AEEB"/>
<path d="M26.66 6.12L22.95 3.12L27.16 2L28.15 5.73L26.66 6.12Z" fill="#635994"/>
<path d="M7.75002 11.14L6.22467 9.76244L6.77002 7.41001L8.25002 7.01001L11.96 10.02L7.75002 11.14Z" fill="#635994"/>
<path d="M18.45 4.31006L12.76 5.82006L16.46 8.83006L22.16 7.32006L18.45 4.31006Z" fill="#635994"/>
<path d="M28.88 16.74V12.88H27.34L24.53 16.74H28.88Z" fill="#635994"/>
<path d="M12.14 12.88L9.33004 16.74H6.79004V12.88H12.14Z" fill="#635994"/>
<path d="M22.69 12.88H16.8L13.99 16.74H19.88L22.69 12.88Z" fill="#635994"/>
<path d="M6.77 7.41003L7.75 11.14L4.99 11.87L4 8.14003L5.49 7.75003L6.77 7.41003Z" fill="#321B41"/>
<path d="M7.78993 12.88V28.35C7.78993 29.27 8.53993 30.02 9.45993 30.02H6.59993C5.67993 30.02 4.92993 29.27 4.92993 28.35V12.88H7.78993Z" fill="#321B41"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,11 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.0985 20L11.2876 20.0023C11.163 20.0027 11.039 20.0205 10.9196 20.0553C9.57284 20.4476 9.75254 22.345 11.1502 22.4907L15.3126 22.9244C15.6061 22.955 15.7939 22.6293 15.6127 22.404L14.6445 21.1998C14.0337 20.4401 13.0937 19.9972 12.0985 20Z" fill="#F70A8D"/>
<path d="M9.86911 18.7891L9.82812 18.75H13.2344L12.1306 20.02C11.8606 20.29 11.4306 20.29 11.1606 20.02L10.2561 19.1579L9.41187 20.02C9.14187 20.29 8.71187 20.29 8.44187 20.02L7.54963 19.1104L6.6775 20.02C6.4075 20.29 5.9775 20.29 5.7075 20.02L4.51562 18.8281H7.27269L7.23438 18.7891H9.86911Z" fill="#D3D3D3"/>
<path d="M19.6562 19.0002H19L19.5369 12.0625H29.6719C30.2519 12.0625 30.5469 12.6075 30.5469 13.1875V14.5C30.5469 16.8086 28.7796 18.7047 26.1464 18.9688H29.3263C30.0562 18.9688 30.6562 19.3796 30.6562 20.1096V23.7609C30.6562 27.5159 28.5156 29.9209 26.1875 29.9209H19.6562V19.0002Z" fill="#F70A8D"/>
<path d="M10.551 1.6787C10.037 1.52971 9.49953 1.82562 9.35054 2.33964C9.20154 2.85367 9.49746 3.39115 10.0115 3.54014C10.6835 3.73494 10.9953 3.99416 11.132 4.17663C11.2583 4.34529 11.285 4.5116 11.2547 4.67813C11.1764 5.1085 10.7662 5.42164 10.4687 5.42164C10.0082 5.42164 9.68487 5.27825 9.18246 5.05545C9.01246 4.98007 8.82196 4.89559 8.59871 4.8045C7.74074 4.45443 6.70167 4.20545 5.30247 4.75415C3.38698 5.50532 3.04516 7.3959 3.15013 8.85442C3.25691 10.3381 3.8277 11.8428 4.30202 12.6056C4.58464 13.06 5.18217 13.1994 5.63664 12.9167C6.09112 12.6341 6.23044 12.0366 5.94782 11.5821C5.63587 11.0805 5.16689 9.87831 5.08319 8.7153C4.99768 7.52712 5.31466 6.83113 6.01003 6.55843C6.79885 6.24909 7.29885 6.3673 7.86654 6.59893C7.96477 6.63901 8.07609 6.68952 8.19917 6.74536C8.75588 6.99795 9.55319 7.3597 10.4687 7.3597C11.8252 7.3597 12.9384 6.25096 13.1615 5.02508C13.2819 4.36349 13.1537 3.64307 12.6832 3.01486C12.2231 2.40046 11.4961 1.95264 10.551 1.6787Z" fill="#F9AD3C"/>
<path d="M22.8585 5.00005H27.5433C27.7528 5.00005 27.9051 5.23005 27.8194 5.43005L27.5909 6.00005C27.1148 7.21005 25.9912 8.00005 24.7439 8.00005H22.8585C22.5919 8.00005 22.3444 8.17005 22.2301 8.42005L20.8399 11.65C20.7447 11.86 20.5447 12 20.3257 12H18.3738C17.9738 12 17.6977 11.56 17.8596 11.17L19.4497 7.34005C20.0401 5.92005 21.3827 5.00005 22.8585 5.00005Z" fill="#F9AD3C"/>
<path d="M13.545 7.74869C13.7109 7.46456 13.8246 7.2698 13.8438 7.23444C13.9453 7.04684 14.4842 6.07835 15.4062 6.07819C16.0743 6.07808 16.6307 6.07814 16.9406 6.07817C17.033 6.07818 17.1035 6.07819 17.1484 6.07819C17.7109 6.07819 18.0234 6.64842 18.0234 6.98436C18.0234 7.37498 17.8047 7.99996 17.1484 7.99996H16.375C16.3125 7.98434 16.1687 7.98746 16.0938 8.12496C16.06 8.18681 15.9428 8.5144 15.8028 8.92064H27.6793C27.9293 8.92064 28.0993 9.15064 28.0493 9.39064L27.7193 11.3506C27.4193 12.5506 26.3393 13.3906 25.1093 13.3906H23.3993L23.4593 13.4606C23.9993 14.0606 24.2993 14.8306 24.2993 15.6406L24.2893 23.0606V23.4206L24.2493 24.4606C24.2393 24.6306 24.2193 24.8106 24.1993 24.9806L24.1393 25.5006C24.1256 25.6243 24.1024 25.7432 24.0795 25.8607C24.0691 25.9142 24.0587 25.9674 24.0493 26.0206L23.9493 26.5306C23.9293 26.644 23.9004 26.7573 23.8715 26.8707C23.8571 26.9273 23.8426 26.984 23.8293 27.0406C23.5693 28.0306 23.2093 29.0006 22.7593 29.9206H14.5155L15.4365 26.7122C15.9941 26.1945 16.0208 25.5264 16.0493 24.8137C16.057 24.6214 16.0648 24.4258 16.0832 24.229C16.0946 24.1079 16.0016 24 15.8799 24H7.13932C5.49932 24 4.06932 22.7706 4.07932 21.5206C4.07932 21.1906 4.34618 21.0337 4.67618 21.0337H10.2278C10.7195 21.0337 11.4005 20.7175 11.7505 20.3675L13.0793 18.9206H3.81932C2.85932 18.9206 2.07932 18.1406 2.07932 17.1806V13.3806C2.09932 12.0206 3.20932 10.9206 4.57932 10.9206C5.17932 10.9206 5.91494 11.0606 6.34494 11.4206C6.37301 11.444 6.40148 11.4681 6.43038 11.4925C6.71031 11.7292 7.02924 11.9988 7.39182 11.9988L10.4293 11.8906C10.9293 11.8906 11.3693 11.5906 11.5593 11.1406L11.9993 10.4206C12.0261 10.3744 12.0541 10.3291 12.0831 10.2848C12.5006 9.53792 13.162 8.40493 13.545 7.74869Z" fill="#00D26A"/>
<path d="M14.6163 29.9206C15.3233 29.4798 16.5543 27.5772 17.25 25.8614C17.8125 24.4741 16.849 24.0424 16.2969 24H14.2031L14.1373 24.0416C14.243 24.0583 14.3192 24.1576 14.3038 24.2674C14.1748 25.1883 14.0447 26.0547 13.3367 26.7122C12.4068 27.5889 10.6728 28.1548 9.44602 28.5551C9.35742 28.584 9.27147 28.6121 9.18865 28.6393C9.09412 28.6857 8.99703 28.7294 8.90129 28.7725C8.46026 28.971 8.04803 29.1566 8.04803 29.5299C8.04803 29.8805 8.27447 29.888 8.63616 29.8998C8.74322 29.9034 8.86213 29.9073 8.99054 29.9206H14.6163Z" fill="#F9AD3C"/>
<path d="M4 17C3.45361 17 3 16.5905 3 16.0762V13.9238C3 13.419 3.4433 13 4 13C4.54639 13 5 13.4095 5 13.9238V16.0762C4.98969 16.581 4.54639 17 4 17Z" fill="#008463"/>
<path d="M14 14C14 13.4477 14.4477 13 15 13C15.5523 13 16 13.4477 16 14V16C16 16.5523 15.5523 17 15 17C14.4477 17 14 16.5523 14 16V14Z" fill="#1C1C1C"/>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

5
src/front/emoji/gear.svg Normal file
View file

@ -0,0 +1,5 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.8472 3.83391C12.9336 2.79732 13.8001 2 14.8403 2H17.1597C18.1999 2 19.0664 2.79732 19.1528 3.83391L19.3882 6.6587C19.422 7.06373 19.8992 7.2614 20.2095 6.99887L22.3734 5.16789C23.1674 4.49599 24.3439 4.54493 25.0795 5.28045L26.7196 6.92056C27.4551 7.65608 27.504 8.8326 26.8321 9.62666L25.0012 11.7905C24.7386 12.1008 24.9363 12.578 25.3413 12.6118L28.1661 12.8472C29.2027 12.9336 30 13.8001 30 14.8403V17.1597C30 18.1999 29.2027 19.0664 28.1661 19.1528L25.3413 19.3882C24.9363 19.422 24.7386 19.8992 25.0012 20.2095L26.8321 22.3733C27.504 23.1674 27.4551 24.3439 26.7196 25.0794L25.0795 26.7196C24.3439 27.4551 23.1674 27.504 22.3734 26.8321L20.2095 25.0011C19.8992 24.7386 19.422 24.9363 19.3882 25.3413L19.1528 28.1661C19.0664 29.2027 18.1999 30 17.1597 30H14.8403C13.8001 30 12.9336 29.2027 12.8472 28.1661L12.6118 25.3413C12.578 24.9363 12.1008 24.7386 11.7905 25.0012L9.62666 26.8321C8.8326 27.504 7.65608 27.4551 6.92056 26.7196L5.28045 25.0795C4.54493 24.3439 4.496 23.1674 5.16789 22.3734L6.99888 20.2095C7.26141 19.8992 7.06373 19.422 6.65871 19.3882L3.83391 19.1528C2.79732 19.0664 2 18.1999 2 17.1597V14.8403C2 13.8001 2.79732 12.9336 3.83391 12.8472L6.65871 12.6118C7.06373 12.578 7.2614 12.1008 6.99888 11.7905L5.16789 9.62664C4.496 8.83258 4.54493 7.65606 5.28045 6.92054L6.92056 5.28043C7.65608 4.54491 8.8326 4.49597 9.62666 5.16787L11.7905 6.99884C12.1008 7.26137 12.578 7.06369 12.6118 6.65867L12.8472 3.83391ZM21 16C21 13.2386 18.7614 11 16 11C13.2386 11 11 13.2386 11 16C11 18.7614 13.2386 21 16 21C18.7614 21 21 18.7614 21 16Z" fill="#B4ACBC"/>
<path d="M24 16C24 20.4183 20.4183 24 16 24C11.5817 24 8 20.4183 8 16C8 11.5817 11.5817 8 16 8C20.4183 8 24 11.5817 24 16ZM20.5 16C20.5 13.5147 18.4853 11.5 16 11.5C13.5147 11.5 11.5 13.5147 11.5 16C11.5 18.4853 13.5147 20.5 16 20.5C18.4853 20.5 20.5 18.4853 20.5 16Z" fill="#998EA4"/>
<path d="M10.5 16C10.5 12.9624 12.9624 10.5 16 10.5C19.0376 10.5 21.5 12.9624 21.5 16C21.5 19.0376 19.0376 21.5 16 21.5C12.9624 21.5 10.5 19.0376 10.5 16ZM21 16C21 13.2386 18.7614 11 16 11C13.2386 11 11 13.2386 11 16C11 18.7614 13.2386 21 16 21C18.7614 21 21 18.7614 21 16Z" fill="#CDC4D6"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,5 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 29L3 27L19 11L21 13L5 29Z" fill="#6B438B"/>
<path d="M18 12L20 14L21.6741 12.3327C22.1086 11.8878 22.1086 11.0935 21.6741 10.658L21.3436 10.3266C20.8996 9.89111 20 9.99999 19.5 10.5L18 12ZM2.3375 29.2516L2.7475 29.6628C3.1875 30.114 3.9175 30.114 4.3575 29.6528L6 28.0056L4 26L2.3375 27.6271C1.8875 28.0784 1.8875 28.8004 2.3375 29.2516Z" fill="#D3D3D3"/>
<path d="M27.8709 4.42343C28.0014 4.70653 28.2224 4.93105 28.5136 5.05796L29.7288 5.58511C30.0904 5.75107 30.0904 6.24893 29.7288 6.41488L28.5237 6.94203C28.2324 7.06894 28.0014 7.29347 27.8809 7.57657L26.9268 9.74375C26.7561 10.0854 26.2439 10.0854 26.0732 9.74375L25.1191 7.57657C24.9885 7.29347 24.7676 7.06894 24.4763 6.94203L23.2712 6.41488C22.9096 6.24893 22.9096 5.75107 23.2712 5.58511L24.4763 5.05796C24.7676 4.93105 24.9986 4.70653 25.1191 4.42343L26.0732 2.25625C26.2439 1.91458 26.7561 1.91458 26.9268 2.25625L27.8709 4.42343ZM16.4412 5.60266C16.2331 5.51505 16.077 5.35929 15.9834 5.1646L15.307 3.67522C15.1821 3.44159 14.8179 3.44159 14.693 3.67522L14.0167 5.1646C13.9334 5.35929 13.7669 5.51505 13.5588 5.60266L12.6951 5.96283C12.435 6.07964 12.435 6.42036 12.6951 6.53717L13.5588 6.89735C13.7669 6.98496 13.923 7.14071 14.0167 7.3354L14.693 8.82478C14.8179 9.05841 15.1821 9.05841 15.307 8.82478L15.9834 7.3354C16.0666 7.14071 16.2331 6.98496 16.4412 6.89735L17.3049 6.53717C17.565 6.42036 17.565 6.07964 17.3049 5.96283L16.4412 5.60266ZM25.9507 16.2976C25.7473 16.1996 25.5867 16.0363 25.4904 15.8185L24.8051 14.196C24.6767 13.9347 24.3126 13.9347 24.1949 14.196L23.5096 15.8185C23.424 16.0254 23.2527 16.1996 23.0493 16.2976L22.1927 16.6897C21.9358 16.8203 21.9358 17.1906 22.1927 17.3103L23.0493 17.7024C23.2527 17.8004 23.4133 17.9637 23.5096 18.1815L24.1949 19.804C24.3233 20.0653 24.6874 20.0653 24.8051 19.804L25.4904 18.1815C25.576 17.9746 25.7473 17.8004 25.9507 17.7024L26.8073 17.3103C27.0642 17.1797 27.0642 16.8094 26.8073 16.6897L25.9507 16.2976ZM12 14C12.5523 14 13 13.5523 13 13C13 12.4477 12.5523 12 12 12C11.4477 12 11 12.4477 11 13C11 13.5523 11.4477 14 12 14ZM30 13C30 13.5523 29.5523 14 29 14C28.4477 14 28 13.5523 28 13C28 12.4477 28.4477 12 29 12C29.5523 12 30 12.4477 30 13ZM19 4C19.5523 4 20 3.55229 20 3C20 2.44771 19.5523 2 19 2C18.4477 2 18 2.44771 18 3C18 3.55229 18.4477 4 19 4ZM20 21C20 21.5523 19.5523 22 19 22C18.4477 22 18 21.5523 18 21C18 20.4477 18.4477 20 19 20C19.5523 20 20 20.4477 20 21Z" fill="#F9AD3C"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -0,0 +1,4 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.44 3.385C17.77 3.765 18.33 3.835 18.74 3.545C19.54 2.975 20.58 3.835 20.17 4.715L17.75 7.485H13.88L11.44 4.685C11.03 3.795 12.07 2.935 12.87 3.505L12.94 3.555C13.35 3.845 13.91 3.765 14.23 3.385L15.1 2.345C15.48 1.885 16.19 1.885 16.57 2.345L17.44 3.385ZM4 21.2249C4 14.6849 9.3 9.38489 15.84 9.38489C22.38 9.38489 27.68 14.6849 27.67 21.2249C27.67 26.0549 23.75 29.9749 18.92 29.9749H12.75C7.92 29.9749 4 26.0549 4 21.2249Z" fill="#F9AD3C"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.12 7.46484H17.56C18.17 7.46484 18.67 7.96484 18.67 8.57484C18.67 9.18484 18.17 9.68484 17.56 9.68484H14.12C13.51 9.68484 13.01 9.18484 13.01 8.57484C13.01 7.96484 13.51 7.46484 14.12 7.46484ZM15.8401 19.3449H15.9101C17.4101 19.3849 18.6101 20.6049 18.6101 22.1049C18.6101 23.3849 17.7401 24.4549 16.5601 24.7749V25.8949C16.5601 26.2949 16.2401 26.6149 15.8401 26.6149C15.4401 26.6149 15.1201 26.2949 15.1201 25.8949V24.7749C13.9401 24.4549 13.0701 23.3849 13.0701 22.1049C13.0701 21.7049 13.3901 21.3849 13.7901 21.3849C14.1901 21.3849 14.5101 21.7049 14.5101 22.1049C14.5101 22.8449 15.1001 23.4349 15.8301 23.4349C16.5601 23.4349 17.1601 22.8349 17.1601 22.1049C17.1601 21.3749 16.5601 20.7749 15.8301 20.7749H15.7601C14.2701 20.7349 13.0701 19.5149 13.0701 18.0149C13.0701 16.7349 13.9401 15.6649 15.1201 15.3449V14.2249C15.1201 13.8249 15.4401 13.5049 15.8401 13.5049C16.2401 13.5049 16.5601 13.8249 16.5601 14.2249V15.3449C17.7401 15.6649 18.6101 16.7349 18.6101 18.0149C18.6101 18.4149 18.2901 18.7349 17.8901 18.7349C17.4901 18.7349 17.1701 18.4149 17.1701 18.0149C17.1701 17.2849 16.5701 16.6849 15.8401 16.6849C15.1101 16.6849 14.5101 17.2849 14.5101 18.0149C14.5101 18.7449 15.1101 19.3449 15.8401 19.3449Z" fill="#8C5543"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,12 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.0099 12.44H9.32994V12.41H7.63995C6.64387 12.41 5.83483 11.6337 5.78263 10.651C4.97212 10.4617 4.36831 9.74492 4.35031 8.87977C3.56944 8.65907 3 7.94314 3 7.08999L9.32994 7.08999V7.08997H12.0099C13.4899 7.08997 14.6899 8.28997 14.6899 9.76997C14.6899 11.25 13.4899 12.44 12.0099 12.44Z" fill="#E6E6E6"/>
<path d="M9.49181 7.08997H9.47621V8.60621L5.17369 8.60621C4.10972 8.60621 3.22878 7.95478 3.04164 7.08997H3.00494C3.00494 7.9413 3.57197 8.65599 4.35028 8.87833C4.36765 9.74411 4.97167 10.4616 5.78262 10.651C5.83482 11.6337 6.64386 12.4099 7.63995 12.4099H11.3299V12.0662H7.63995C6.76753 12.0662 6.0386 11.4707 5.83598 10.6626C5.95671 10.6871 6.08175 10.7 6.20989 10.7L9.89989 10.7V10.3562L6.20989 10.3562C5.31491 10.3562 4.57092 9.72955 4.39126 8.88953C4.54241 8.92897 4.70115 8.94996 4.86494 8.94996L9.49181 8.94996V7.08997Z" fill="#D3D3D3"/>
<path d="M29.24 14.4L16.46 27.18C15.78 27.86 14.67 27.86 13.99 27.18L12 25.1899C11.32 24.5099 11.32 23.3999 12 22.7199L24.79 9.94994C25.47 9.26994 26.58 9.26994 27.26 9.94994L29.25 11.94C29.92 12.61 29.92 13.72 29.24 14.4Z" fill="#008463"/>
<path d="M27.3 12.46L14.52 25.24C13.84 25.92 12.73 25.92 12.05 25.24L4.55999 17.75C3.87999 17.07 3.87999 15.96 4.55999 15.28L17.35 2.51C18.03 1.83 19.14 1.83 19.82 2.51L27.3 10C27.98 10.68 27.98 11.78 27.3 12.46Z" fill="#00F397"/>
<path d="M17.8086 4.28861C18.2643 3.83293 19.0136 3.82957 19.4825 4.28977L19.4835 4.29074L25.4214 10.2286C25.877 10.6842 25.8801 11.4327 25.4114 11.9014L13.9914 23.3214C13.5357 23.777 12.7864 23.7804 12.3175 23.3202L12.3165 23.3192L6.37863 17.3814C5.92298 16.9257 5.9196 16.1764 6.37972 15.7075L6.38076 15.7065L17.8085 4.28872L17.8086 4.28861ZM19.9965 3.76923C19.2454 3.03044 18.0355 3.02736 17.2915 3.77125L17.2914 3.77136L5.8615 15.1912L5.86149 15.1912L5.85926 15.1935C5.12043 15.9446 5.11738 17.1546 5.86139 17.8986L11.8014 23.8386L11.8035 23.8407C12.5547 24.5796 13.7646 24.5826 14.5086 23.8386L25.9286 12.4186C26.6799 11.6673 26.683 10.4557 25.9386 9.71136L19.9986 3.77136L19.9986 3.77135L19.9965 3.76923ZM22.13 11.0901C22.13 12.9734 20.6033 14.5001 18.72 14.5001C16.8367 14.5001 15.31 12.9734 15.31 11.0901C15.31 9.20679 16.8367 7.68008 18.72 7.68008C20.6033 7.68008 22.13 9.20679 22.13 11.0901Z" fill="#008463"/>
<path d="M12.6561 7.20054L9.53778 10.3188L21.4241 22.2052L24.5424 19.0869L12.6561 7.20054Z" fill="#FFF478"/>
<path d="M22.6098 17.162L19.4915 20.2803L21.429 22.2177L24.5473 19.0994L22.6098 17.162Z" fill="#FFB02E"/>
<path d="M9.65035 14.6466C9.50488 14.4973 9.26591 14.4942 9.1166 14.6397C8.9673 14.7852 8.9642 15.0241 9.10967 15.1735L9.62679 15.7042C9.19489 16.2737 9.16115 16.8348 9.28184 17.2821C9.34853 17.5293 9.45823 17.7284 9.54926 17.8645C9.5952 17.9331 9.63763 17.9876 9.66976 18.0261C9.7105 18.075 9.75576 18.1202 9.80495 18.1604C9.84287 18.1914 9.89633 18.2321 9.96376 18.2759C10.0975 18.3627 10.2931 18.466 10.5367 18.5255C11.0503 18.6509 11.71 18.5639 12.3669 17.907C12.6063 17.6676 12.8246 17.5599 13.0075 17.5166C13.1918 17.4729 13.3643 17.489 13.5185 17.5338C13.6747 17.5792 13.8079 17.653 13.9036 17.7178C13.9758 17.7666 14.039 17.8273 14.081 17.9036C14.1274 17.9877 14.1798 18.1068 14.2067 18.2483C14.2567 18.5102 14.2314 18.8949 13.7931 19.3332C13.3562 19.7701 12.9702 19.7941 12.7051 19.7422C12.5621 19.7141 12.4415 19.6606 12.356 19.6132C12.3139 19.5898 12.2818 19.5687 12.2618 19.5546C12.2518 19.5476 12.2449 19.5424 12.2413 19.5397L12.2387 19.5377C12.0784 19.4067 11.8421 19.4294 11.7096 19.589C11.5764 19.7494 11.5992 19.9879 11.7596 20.1211C11.76 20.1213 11.7604 20.1217 11.7604 20.1217L11.7623 20.1233L11.7673 20.1273L11.7817 20.1385C11.7933 20.1475 11.8089 20.1591 11.8283 20.1727C11.8671 20.1999 11.9215 20.2354 11.9896 20.2732C12.1248 20.3483 12.3204 20.436 12.5599 20.483C12.9849 20.5663 13.514 20.513 14.0458 20.1125L14.6315 20.7053C14.778 20.8536 15.017 20.855 15.1653 20.7085C15.3136 20.562 15.315 20.323 15.1685 20.1747L14.5787 19.5778C14.9734 19.0506 15.0286 18.5279 14.9483 18.1068C14.9028 17.8683 14.8163 17.6736 14.7421 17.539C14.6386 17.3514 14.5024 17.2116 14.3267 17.0926C14.1865 16.9977 13.9816 16.8822 13.7291 16.8088C13.4745 16.7349 13.1676 16.7028 12.8335 16.782C12.4979 16.8615 12.1588 17.0475 11.8331 17.3732C11.3601 17.8462 10.9697 17.8542 10.7158 17.7921C10.5757 17.7579 10.4582 17.6968 10.3747 17.6426C10.2914 17.5886 10.2321 17.5275 10.1767 17.4447C10.1174 17.3561 10.0502 17.2321 10.0107 17.0855C9.93871 16.8188 9.93987 16.424 10.4069 15.957C10.8737 15.4902 11.2745 15.4853 11.5487 15.5566C11.6985 15.5956 11.8253 15.6623 11.9158 15.7211C11.9604 15.7501 11.9944 15.776 12.016 15.7935C12.0268 15.8022 12.0343 15.8087 12.0384 15.8123L12.0417 15.8153C12.1929 15.9571 12.4304 15.9506 12.5735 15.8002C12.7172 15.6492 12.7106 15.4097 12.5595 15.266L12.5588 15.2653L12.557 15.2636L12.5522 15.2592L12.5379 15.2463C12.5264 15.2361 12.5107 15.2227 12.4911 15.2068C12.4519 15.1751 12.3967 15.1333 12.3271 15.0881C12.1891 14.9985 11.9878 14.8908 11.7388 14.826C11.2912 14.7096 10.7297 14.7439 10.1616 15.1713L9.65035 14.6466Z" fill="white"/>
<path d="M20.08 23.74V21.06C20.08 19.58 21.27 18.38 22.75 18.38C24.23 18.38 25.43 19.58 25.43 21.06V22.95H25.43V30.07C24.5769 30.07 23.8609 29.5005 23.6402 28.7196C22.7751 28.7016 22.0583 28.0978 21.869 27.2873C20.8863 27.2351 20.11 26.4261 20.11 25.43V23.74H20.08Z" fill="#E6E6E6"/>
<path d="M20.4537 25.43C20.4537 26.3024 21.0492 27.0314 21.8573 27.234C21.8328 27.1132 21.82 26.9882 21.82 26.86V23.17H22.1637V26.86C22.1637 27.755 22.7904 28.499 23.6304 28.6787C23.591 28.5275 23.57 28.3688 23.57 28.205V23.5781H25.43V23.5937H23.9137V27.8962C23.9137 28.9602 24.5652 29.8412 25.43 30.0283V30.065C24.5787 30.065 23.864 29.498 23.6416 28.7197C22.7759 28.7023 22.0584 28.0983 21.869 27.2873C20.8862 27.2351 20.11 26.4261 20.11 25.43V21.74H20.4537V25.43Z" fill="#D3D3D3"/>
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

View file

@ -0,0 +1,5 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.3599 3.00421L8.21995 3.80421C7.89995 3.84421 7.65995 4.12421 7.65995 4.44421V12.0642C7.08995 11.8642 6.45995 11.7842 5.79995 11.8542C3.74995 12.0742 2.12995 13.7742 1.99995 15.8342C1.83995 18.3242 3.80995 20.3842 6.26995 20.3842C8.62995 20.3842 10.5499 18.4742 10.5499 16.1042C10.5499 16.0142 10.5499 15.9242 10.5399 15.8342V8.00421C10.5399 7.72421 10.7499 7.48421 11.0299 7.44421L14.4899 6.99421C14.7499 6.96421 14.9499 6.73421 14.9499 6.46421V3.53421C14.9599 3.21421 14.6799 2.96421 14.3599 3.00421Z" fill="#6B438B"/>
<path d="M29.4 5.37423L23.26 6.17423C22.94 6.21423 22.7 6.48423 22.7 6.80423V16.8142C22.13 16.6142 21.5 16.5342 20.84 16.6042C18.79 16.8242 17.17 18.5242 17.04 20.5842C16.88 23.0742 18.85 25.1342 21.31 25.1342C23.67 25.1342 25.59 23.2242 25.59 20.8542C25.59 20.7642 25.59 20.6742 25.58 20.5842V10.3742C25.58 10.0942 25.79 9.85423 26.07 9.81423L29.53 9.36423C29.8 9.32423 30 9.10424 30 8.83424V5.89423C30 5.57423 29.72 5.33423 29.4 5.37423Z" fill="#6B438B"/>
<path d="M13.09 10.6543L19.23 9.85429C19.55 9.80429 19.83 10.0543 19.83 10.3743V13.3043C19.83 13.5743 19.63 13.8043 19.37 13.8343L15.91 14.2843C15.63 14.3243 15.42 14.5643 15.42 14.8443V25.0643C15.43 25.1543 15.43 25.2443 15.43 25.3343C15.43 27.7043 13.51 29.6143 11.15 29.6143C8.68995 29.6143 6.71995 27.5543 6.87995 25.0643C6.99995 23.0043 8.61995 21.3143 10.67 21.0943C11.33 21.0243 11.96 21.1043 12.53 21.3043V11.2943C12.53 10.9643 12.77 10.6943 13.09 10.6543Z" fill="#6B438B"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,15 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.04996 29.9L6.5216 29.5061L6.91647 28.6508L9.46694 28.7178L12.1194 28.0078L12.7168 27.0816L15.1987 27.1836L17.79 26.49C19.59 26.01 20.2 23.75 18.88 22.43L9.55996 13.1C8.23996 11.78 5.97996 12.38 5.49996 14.19L4.72734 17.0766L4.8355 19.4212L3.95471 19.9632L3.16482 22.9143L3.29717 25.1621L2.4337 25.6458L2.08996 26.93C1.59996 28.73 3.24996 30.38 5.04996 29.9Z" fill="#F9AD3C"/>
<path d="M7.80079 25.0526C6.32047 23.6703 5.05958 22.006 3.94623 19.995L4.72357 17.0908C6.00204 19.8887 7.44018 21.98 9.16541 23.591C10.8281 25.1437 12.7976 26.2878 15.2358 27.1736L12.1066 28.0112C10.5047 27.2141 9.07999 26.2472 7.80079 25.0526Z" fill="#3F5FFF"/>
<path d="M4.28706 27.8862C3.57939 27.2244 2.97105 26.4832 2.43201 25.6523L3.17687 22.8694C3.87953 24.3596 4.66657 25.5034 5.65277 26.4256C6.63663 27.3457 7.8612 28.085 9.4909 28.7113L6.5021 29.5113C5.67933 29.0387 4.94637 28.5028 4.28706 27.8862Z" fill="#3F5FFF"/>
<path d="M19.11 25.69C20.51 24.29 18.77 20.29 15.23 16.75C11.69 13.21 7.69997 11.47 6.29997 12.87C4.89997 14.27 6.63997 18.27 10.18 21.81C13.72 25.35 17.72 27.08 19.11 25.69Z" fill="#6D4534"/>
<path d="M11.61 7.27004C12.85 9.59004 13.1 12.25 12.33 14.76C11.68 16.9 10.34 18.71 8.52001 19.96C8.00001 19.31 7.55001 18.66 7.17001 18.03C8.56001 17.08 9.58001 15.7 10.08 14.07C10.67 12.16 10.48 10.14 9.54001 8.38004C9.23001 7.80004 9.45001 7.09004 10.02 6.79004C10.6 6.48004 11.31 6.70004 11.61 7.27004Z" fill="#F70A8D"/>
<path d="M22.1498 16.7126C24.0405 15.219 26.4927 14.9149 28.6037 15.7022C29.1461 15.9045 29.7499 15.6288 29.9522 15.0863C30.1545 14.5439 29.8787 13.9402 29.3363 13.7379C26.5673 12.7052 23.3395 13.1011 20.8502 15.0675C20.3959 15.4263 20.3186 16.0855 20.6775 16.5398C21.0363 16.9941 21.6955 17.0715 22.1498 16.7126Z" fill="#F70A8D"/>
<path d="M6.00998 9.69003C6.65063 9.69003 7.16998 9.17068 7.16998 8.53003C7.16998 7.88938 6.65063 7.37003 6.00998 7.37003C5.36933 7.37003 4.84998 7.88938 4.84998 8.53003C4.84998 9.17068 5.36933 9.69003 6.00998 9.69003Z" fill="#F70A8D"/>
<path d="M26.13 8.53003C26.13 9.17068 25.6106 9.69003 24.97 9.69003C24.3293 9.69003 23.81 9.17068 23.81 8.53003C23.81 7.88938 24.3293 7.37003 24.97 7.37003C25.6106 7.37003 26.13 7.88938 26.13 8.53003Z" fill="#F70A8D"/>
<path d="M12.49 3.96C12.49 4.49019 12.0602 4.92 11.53 4.92C10.9998 4.92 10.57 4.49019 10.57 3.96C10.57 3.42981 10.9998 3 11.53 3C12.0602 3 12.49 3.42981 12.49 3.96Z" fill="#F9AD3C"/>
<path d="M25.48 24.41C25.48 24.9457 25.0457 25.38 24.51 25.38C23.9743 25.38 23.54 24.9457 23.54 24.41C23.54 23.8743 23.9743 23.44 24.51 23.44C25.0457 23.44 25.48 23.8743 25.48 24.41Z" fill="#F9AD3C"/>
<path d="M21.63 4.01999C21.47 3.87999 21.27 3.78999 21.05 3.77999C20.51 3.74999 20.05 4.15999 20.01 4.69999C19.94 5.79999 19.18 7.78999 18.89 8.45999C18.89 8.46499 18.8875 8.46999 18.885 8.47499C18.8825 8.47999 18.88 8.48499 18.88 8.48999C18.8264 8.62205 18.7691 8.75228 18.7081 8.88047C18.6061 8.75272 18.4934 8.63579 18.37 8.53C18.09 8.28 17.75 8.09 17.35 7.96L17.32 7.95C15.97 7.5 14.5 8.23 14.05 9.59C13.83 10.25 13.88 10.95 14.19 11.57C14.5 12.19 15.03 12.65 15.69 12.87C15.7608 12.8936 15.8327 12.9086 15.9044 12.9154C16.4096 13.0519 16.8986 13.0566 17.3693 12.9302C17.3408 13.0675 17.3077 13.2041 17.27 13.34C17.13 13.86 17.44 14.4 17.96 14.54C18.48 14.68 19.02 14.37 19.16 13.85C19.3853 13.0354 19.4829 12.2058 19.4529 11.3677C19.9732 10.7159 20.3888 9.9978 20.7 9.21999L20.7147 9.18435C20.9001 8.73574 21.8729 6.38206 21.97 4.80999C21.98 4.49999 21.85 4.20999 21.63 4.01999ZM16.75 9.81C17.0254 9.9018 17.3077 10.0563 17.4285 10.6861C16.936 11.1435 16.62 11.0933 16.34 11C16.3063 10.9888 16.2724 10.9795 16.2384 10.9721C16.1114 10.9124 16.0092 10.807 15.95 10.68C15.87 10.53 15.86 10.36 15.92 10.2C16.03 9.87 16.39 9.69 16.72 9.8L16.75 9.81Z" fill="#00A6ED"/>
<path d="M18.27 18.16C21.68 17.21 25.25 17.64 28.33 19.38C28.9 19.71 29.1 20.42 28.79 20.98C28.47 21.55 27.75 21.75 27.18 21.43C24.65 20.01 21.71 19.65 18.91 20.43C16.6 21.07 14.62 22.43 13.19 24.31C12.58 23.89 11.95 23.41 11.33 22.87C13.06 20.6 15.47 18.94 18.27 18.16Z" fill="#00A6ED"/>
<path d="M16.624 16.9853C16.624 17.6205 16.1091 18.1353 15.474 18.1353C14.8389 18.1353 14.324 17.6205 14.324 16.9853C14.324 16.3502 14.8389 15.8353 15.474 15.8353C16.1091 15.8353 16.624 16.3502 16.624 16.9853Z" fill="#00A6ED"/>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View file

@ -0,0 +1,16 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25 13.9657C23.369 13.9657 22.3538 15.0363 22.0501 15.5716C21.7494 16.0841 22.6173 17.4099 24.9476 17.4099C27.2779 17.4099 27.7973 16.8905 27.934 16.3165C28.0706 15.7424 27.0388 13.9657 25 13.9657Z" fill="#FF6723"/>
<path d="M28.4302 19C28.4302 17.3316 26.9346 16.526 25.8861 16.526C24.752 16.526 23.9433 17.2151 23.6807 17.5596C24.039 18.6782 24.844 21.1951 25.197 22.3137C25.6383 23.712 26.1541 24.0103 26.6264 24.3024C27.0987 24.5945 27.3846 24.3645 27.3846 23.9544V23.2894C27.3846 21.5804 28.4302 21.3753 28.4302 19Z" fill="#00A6ED"/>
<path d="M10.1 20.911C9.49249 20.911 9 21.4035 9 22.011V28.8244C9 29.432 9.49249 29.9244 10.1 29.9244H12.9C13.5075 29.9244 14 29.432 14 28.8244V22.011C14 21.4035 13.5075 20.911 12.9 20.911H10.1Z" fill="#433B6B"/>
<path d="M19.5981 21.8817C18.9906 21.8817 18.4981 22.3741 18.4981 22.9817V28.8244C18.4981 29.432 18.9906 29.9244 19.5981 29.9244H22.3981C23.0056 29.9244 23.4981 29.432 23.4981 28.8244V22.9817C23.4981 22.3741 23.0056 21.8817 22.3981 21.8817H19.5981Z" fill="#433B6B"/>
<path d="M7.34603 26.7699C7.43479 26.6251 7.48439 26.4727 7.52375 26.2939H11.4844C11.5246 26.3256 11.6392 26.4183 11.7763 26.5351C11.9477 26.6811 12 26.827 12 27.1254V28.7693C12 29.3469 11.6684 29.9244 10.9448 29.9244H8.30444C7.65069 29.9244 7.23178 29.2834 7.23178 28.6423V27.284C7.23178 27.0873 7.22543 26.9667 7.34603 26.7699Z" fill="#8D65C5"/>
<path d="M25.41 27.2899C25.41 27.0135 25.3793 26.996 25.2477 26.8731L24.8133 26.2502H21.3651C21.3651 26.6889 20.9527 27.0442 20.7509 27.0442C20.5491 27.0442 20.5535 27.1758 20.5535 27.4654V28.6674C20.5535 29.4395 20.9966 29.9397 21.8433 29.9397H24.2167C25.2213 29.9397 25.41 29.1675 25.41 28.7639V27.2899Z" fill="#8D65C5"/>
<path d="M10 3.08766C10 2.53538 10.4477 2.08766 11 2.08766H11.5709C12.1232 2.08766 12.5709 2.53538 12.5709 3.08766V5.06575C12.5709 5.61803 12.1232 6.06575 11.5709 6.06575H11C10.4477 6.06575 10 5.61803 10 5.06575V3.08766Z" fill="#608842"/>
<path d="M7.3397 22.7633L7.59051 21.4284L9 21.1786H24.2324C24.4844 21.548 25.0185 22.333 25.1386 22.5177C25.2887 22.7486 25.3695 22.9391 25.3695 23.2507V25.3806C25.3695 25.9071 25.5242 26.315 25.6016 26.4531C25.7712 26.7211 25.7192 26.8516 25.4727 26.9492C25.3274 27.0067 25.0977 27.0859 24.8516 27.0859C24.1875 27.0859 24.0547 26.6992 23.9453 26.582C23.8359 26.4648 23.7148 26.5 23.6562 26.582C23.4141 26.9297 23.1406 27.053 22.8008 27.053C22.4609 27.053 22.207 26.9336 21.957 26.6184C21.7969 26.4297 21.734 26.4384 21.5959 26.6184C21.2841 27.0247 20.9818 27.053 20.7976 27.053C20.6134 27.053 20.5331 26.968 20.5331 26.7271V24.9416C20.5331 24.1056 20.2214 23.8477 19.3617 23.8477H13C12.0469 23.8477 12 24.5078 12 24.9609V26.4258C12 26.5625 12 26.7109 11.875 26.6016C11.75 26.4922 11.6992 26.4844 11.5156 26.6484C11.332 26.8125 11.1094 27.0664 10.6523 27.0664C10.1953 27.0664 10.0078 26.793 9.87891 26.6484L9.87387 26.6428C9.74783 26.5014 9.66557 26.4091 9.48438 26.625C9.30078 26.8438 9.11328 27.0664 8.64844 27.0664C8.18359 27.0664 7.95312 26.7773 7.8125 26.582C7.67188 26.3867 7.5625 26.4688 7.47656 26.625C7.39062 26.7813 7.28906 26.8477 7.16797 26.6914C7.04688 26.5352 7 26.2617 7 26C7 25.5708 7.22643 25.231 7.22643 24.341V23.3378C7.22643 22.9883 7.30194 22.8092 7.3397 22.7633Z" fill="#00A6ED"/>
<path d="M7.22492 19.417C7.22492 19.1157 7.22492 18.948 7.31841 18.7417C7.70831 17.8817 7.82742 17.6026 8.19619 17.112H24.0602C24.1521 17.2309 24.432 17.5992 24.8164 18.1211C25.2969 18.7734 25.3711 19.0352 25.3945 19.6055C25.418 20.1758 25.3711 21.4141 25.3711 21.8281C25.3711 22.2422 25.5 22.4688 25.5938 22.5977C25.6875 22.7266 25.7266 22.8242 25.5938 22.8867C25.4609 22.9492 25.293 23.0273 24.8008 23.0273C24.3086 23.0273 24.0391 22.6445 23.9336 22.5313C23.8281 22.418 23.707 22.4531 23.6445 22.5313C23.4141 22.8203 23.1484 23.0273 22.7461 23.0273C22.3438 23.0273 22.0664 22.7617 21.8984 22.5117C21.8125 22.3945 21.7327 22.4001 21.6484 22.5117C21.5039 22.7031 21.2578 23.0273 20.8359 23.0273C20.4141 23.0273 20.1055 22.8008 19.9336 22.582C19.8008 22.4336 19.7284 22.4197 19.5781 22.582L19.5701 22.5907C19.377 22.7993 19.166 23.0273 18.7383 23.0273C18.3047 23.0273 18.1211 22.793 17.9219 22.582C17.7461 22.3867 17.6883 22.4253 17.5547 22.582C17.3516 22.8203 17.1914 23.0273 16.7148 23.0273C16.2383 23.0273 16.0625 22.8047 15.8711 22.5541C15.7539 22.4063 15.669 22.4118 15.5586 22.5541C15.2461 22.957 15.0117 23 14.6719 23C14.2852 23 14.1688 22.9059 13.8477 22.5541C13.7188 22.4129 13.6583 22.4202 13.5391 22.5541C13.2891 22.8348 13.0742 23 12.6719 23C12.2707 23 12.118 22.8369 11.8925 22.5958L11.8906 22.5938C11.7734 22.4688 11.6759 22.3868 11.5312 22.5541C11.3359 22.7801 11.0898 23 10.6914 23C10.293 23 10.0234 22.7723 9.84788 22.5541C9.76478 22.4606 9.63693 22.4414 9.53105 22.5541C9.37004 22.7255 9.11553 23.0268 8.65327 23.0268C8.191 23.0268 7.96247 22.6892 7.82742 22.5541C7.69238 22.4191 7.5833 22.4399 7.51578 22.5541C7.44826 22.6684 7.44307 22.6892 7.38074 22.7723C7.31841 22.8554 7.31841 22.8294 7.22492 22.7723C7.13143 22.6632 7 22.6216 7 22.2269C7 21.7906 7.22492 21.3959 7.22492 20.8713V19.417Z" fill="#FF6DC6"/>
<path d="M7.21926 15.2947C7.21955 15.3328 7.21986 15.3746 7.21986 15.4212V16.3978C7.21986 17.0007 7.17745 17.1602 7.12098 17.3725C7.10066 17.4489 7.07854 17.5321 7.05591 17.6452C6.97037 18.0729 6.99175 18.4294 7.10581 18.6717C7.21179 18.897 7.30547 18.7836 7.38111 18.6921C7.38687 18.6851 7.39252 18.6783 7.39807 18.6717C7.41107 18.6564 7.42506 18.6357 7.44038 18.6131C7.51745 18.4992 7.62837 18.3353 7.81864 18.5434C8.04675 18.7929 8.23922 19 8.66692 19C9.05724 19 9.31695 18.7325 9.46231 18.5828C9.47623 18.5685 9.4891 18.5553 9.50094 18.5434C9.63638 18.408 9.71902 18.3962 9.84669 18.5434C9.85251 18.5501 9.85857 18.5572 9.86489 18.5645C9.99703 18.7182 10.2394 19 10.6925 19C11.1337 19 11.375 18.7163 11.4966 18.5734C11.5059 18.5625 11.5144 18.5525 11.5223 18.5434C11.634 18.4153 11.7178 18.3922 11.8654 18.5434C11.8831 18.5615 11.9017 18.5818 11.9217 18.6035C12.0689 18.7633 12.2868 19 12.6873 19C13.0952 19 13.3298 18.755 13.4832 18.5948C13.5008 18.5764 13.5174 18.5591 13.5331 18.5434C13.6847 18.3914 13.7684 18.4441 13.8722 18.5434C13.885 18.5557 13.9005 18.5719 13.9188 18.5909C14.048 18.7256 14.3114 19 14.682 19C15.1049 19 15.3842 18.7274 15.5518 18.5434C15.7193 18.3595 15.8071 18.4521 15.8869 18.5434C15.8892 18.5461 15.8918 18.549 15.8945 18.5522C15.9846 18.6559 16.2836 19 16.7367 19C17.1498 19 17.3692 18.7614 17.5165 18.6011C17.5356 18.5804 17.5535 18.5609 17.5705 18.5434C17.7181 18.3914 17.774 18.4042 17.9176 18.5434C17.9398 18.5649 17.9634 18.5907 17.9892 18.6189C18.1307 18.7731 18.3386 19 18.7434 19C19.2222 19 19.4935 18.6467 19.5852 18.5191C19.677 18.3914 19.7648 18.3236 19.9164 18.5191C20.068 18.7146 20.3193 19 20.7941 19C21.2055 19 21.4491 18.7078 21.5639 18.5701C21.5816 18.5489 21.5962 18.5313 21.6079 18.5191C21.6957 18.4273 21.8154 18.3515 21.9471 18.5191C22.0787 18.6866 22.342 19 22.7769 19C23.1572 19 23.4246 18.7206 23.5658 18.5732C23.586 18.552 23.6037 18.5336 23.6187 18.5191C23.7384 18.4034 23.8581 18.4074 23.9698 18.543C23.9821 18.558 23.9952 18.5757 24.0097 18.5952C24.1261 18.7521 24.3321 19.0297 24.9672 19.0297C25.3634 19.0297 25.5683 18.8939 25.5895 18.7584C25.6015 18.682 25.527 18.5988 25.4316 18.4923C25.3913 18.4473 25.3474 18.3982 25.3047 18.3438C25.171 18.1733 25.0584 17.9865 24.9388 17.7878C24.8362 17.6175 24.7284 17.4386 24.5976 17.2539C24.5236 17.1494 24.4444 17.0441 24.3586 16.9396C24.81 16.7037 25.3364 16.526 25.8861 16.526C26.8487 16.526 28.1881 17.205 28.4011 18.6073C28.4953 18.2669 28.5106 17.8695 28.5106 17.4445C28.5106 16.179 27.3008 15.0391 25.8984 15.0391C24.9825 15.0391 24.0967 15.432 23.6303 16.2411C22.9766 15.759 22.0481 15.4022 20.6374 15.4022H17.6743C16.8286 15.4022 16.4175 15.1634 16.0175 14.931C15.974 14.9057 15.9307 14.8805 15.8869 14.8557C15.8161 14.8112 15.7593 14.7709 15.7109 14.7367C15.5307 14.609 15.4685 14.5649 15.2359 14.6931C14.9408 14.8557 14.3749 14.7218 14.3749 14C13.7969 14.0006 9.94749 14.0002 8.09505 14C7.6495 14 7.53442 14.2433 7.34817 14.6513C7.2166 14.9395 7.21719 15.0185 7.21926 15.2947Z" fill="#F8312F"/>
<path d="M4.62137 10.7396V9.72369L4.88679 9.83984H13.3359C13.6002 10.1068 14.1633 10.6758 14.3008 10.8164C14.4726 10.9922 14.5078 10.9883 14.5078 11.2656V12C14.5078 13.75 15.3789 14.1641 15.5625 14.2969C15.7461 14.4297 15.6875 14.4967 15.5625 14.6094C15.4375 14.7221 15.1836 14.9726 14.7422 14.9726C14.3008 14.9726 14.1172 14.7344 13.8828 14.4967C13.7539 14.3594 13.652 14.3586 13.5312 14.4967C13.307 14.7529 13.1011 14.9726 12.6938 14.9726C12.2882 14.9726 12.0823 14.7457 11.8591 14.4997L11.8563 14.4967C11.7419 14.3731 11.6186 14.3795 11.5223 14.4967C11.3118 14.7529 11.0555 14.9497 10.6574 14.9497C10.2592 14.9497 10.0076 14.6523 9.88857 14.5424C9.76959 14.4326 9.71468 14.2679 9.52248 14.4967C9.33028 14.7255 9.0191 14.9726 8.66673 14.9726C8.31436 14.9726 8.02606 14.7438 7.83386 14.5424C7.64166 14.3411 7.52268 14.428 7.45861 14.4967C7.39455 14.5653 7.26641 14.9085 7.08794 14.6065C6.90947 14.3045 7.10624 13.2565 7.15201 12.7486C7.19777 12.2406 7.17031 11.8242 6.28711 11.6869C5.4039 11.5496 5.02865 11.2476 4.88679 11.1606C4.74493 11.0737 4.68086 10.9959 4.75865 10.9593C4.83645 10.9227 4.62137 10.8952 4.62137 10.7396Z" fill="#FF8101"/>
<path d="M9.64627 5.71115H13.2695C13.5417 5.96759 14.1266 6.52578 14.2891 6.70703C14.4922 6.93359 14.5 7.01923 14.5 7.24494V9.82812C14.5 10.2891 14.5781 10.5703 14.6367 10.6758L14.6377 10.6776C14.6965 10.7834 14.7995 10.9687 14.5781 10.9687C14.3555 10.9687 14.0273 10.6992 13.8945 10.5234C13.7617 10.3477 13.6094 10.3828 13.5039 10.5234C13.3984 10.6641 13.2422 10.9492 12.7227 10.9492C12.2031 10.9492 12.043 10.7109 11.9023 10.5469C11.7617 10.3828 11.6211 10.3711 11.4844 10.5234C10.9336 11.0898 10.2891 10.9922 9.85547 10.4844C9.73633 10.3652 9.65625 10.3398 9.52735 10.4612C9.39844 10.5825 9.16016 10.9502 8.66797 10.9502C8.17578 10.9502 7.91797 10.5625 7.83203 10.4612C7.7461 10.3599 7.67578 10.2266 7.51563 10.4612C7.35547 10.6958 7.03906 10.9502 6.63051 10.9502C6.10938 10.9502 5.96045 10.6287 5.81997 10.4612C5.70224 10.3208 5.60262 10.3253 5.49847 10.4612C5.21773 10.8234 4.94151 10.914 4.66529 11C4.38907 11.086 4.29398 11.0046 4.20342 10.7827C4.00418 10.2393 3.61023 9.93592 3.61023 8.70879C3.61023 7.12394 4.66076 6.91564 5.09094 6.91564H7.12408C7.77161 6.91564 8.05688 6.78885 8.5097 6.52622C8.96252 6.26359 9.25685 5.71115 9.64627 5.71115Z" fill="#FCB100"/>
<path d="M8.06646 6.76934C7.93599 6.71561 7.84376 6.5664 8.00001 6.50839C8.12793 6.43017 8.19434 6.32483 8.27092 6.1608C8.35214 5.98684 8.4448 5.74687 8.6344 5.40321C9.00279 4.73549 9.66283 4.50525 10.2691 4.50525H11.4511C11.7581 4.50525 12.0804 4.35943 12.0804 4V3.13145C12.0804 2.35628 12.61 2.08766 13 2.08766H13.6461C14.1066 2.08766 14.4903 2.47141 14.4903 3.13145V5.51833C14.4903 6.12464 14.5441 6.31652 14.6438 6.50839C14.7436 6.70026 14.9508 6.95353 14.6438 6.95353C14.3368 6.95353 14.0404 6.61471 13.8945 6.47656C13.7487 6.33841 13.6998 6.33954 13.531 6.50839C13.3621 6.67724 13.0782 6.89981 12.7021 6.89981C12.326 6.89981 12.0881 6.70794 11.9346 6.50839C11.7811 6.30884 11.6524 6.28125 11.4511 6.50839C11.2747 6.70745 11.0366 6.89981 10.6452 6.89981C10.3321 6.89981 9.96912 6.56797 9.84376 6.4375C9.69794 6.29168 9.59776 6.33095 9.48829 6.46093C8.8743 7.19004 8.19693 6.82306 8.06646 6.76934Z" fill="#96C34A"/>
<path d="M10.0973 8.17216C10.0973 8.49209 9.8379 8.75146 9.51796 8.75146C9.19802 8.75146 8.93866 8.49209 8.93866 8.17216C8.93866 7.85222 9.19802 7.59286 9.51796 7.59286C9.8379 7.59286 10.0973 7.85222 10.0973 8.17216Z" fill="#433B6B"/>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -0,0 +1,4 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.0372 20.8626C13.0372 22.1648 14.1823 23.2221 15.5924 23.2221C17.0025 23.2221 18.1475 22.1648 18.1475 20.8528V19.1506C18.1475 19.0395 18.2212 18.9421 18.3271 18.9086C21.6766 17.8508 24 14.9188 24 11.5616V10.3084C24 6.0691 20.3104 2.53471 15.7726 2.4466C13.4931 2.39764 11.3409 3.19068 9.70813 4.65926C8.08598 6.12784 7.18478 8.10553 7.18478 10.2105C7.18478 11.5224 8.34043 12.5798 9.75054 12.5798C11.1606 12.5798 12.3057 11.5224 12.3057 10.2203C12.3057 9.39788 12.6556 8.62443 13.2917 8.04679C13.9278 7.46915 14.7654 7.15585 15.6666 7.17543C17.4478 7.21459 18.8897 8.62443 18.8897 10.3182V11.5616C18.8897 13.0302 17.7659 14.2932 16.2073 14.5575C14.3731 14.8708 13.0372 16.3492 13.0372 18.0723V20.8626Z" fill="#F8312F"/>
<path d="M15.5 30C16.8807 30 18 28.8807 18 27.5C18 26.1193 16.8807 25 15.5 25C14.1193 25 13 26.1193 13 27.5C13 28.8807 14.1193 30 15.5 30Z" fill="#F8312F"/>
</svg>

After

Width:  |  Height:  |  Size: 992 B

View file

@ -0,0 +1,5 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.5194 7.0517C10.2265 6.93064 9.99626 6.69861 9.88117 6.41614L8.929 4.25725C8.75112 3.91425 8.23842 3.91425 8.071 4.25725L7.11883 6.41614C6.99327 6.69861 6.76308 6.92055 6.48057 7.0517L5.26682 7.57629C4.91106 7.74779 4.91106 8.24212 5.26682 8.41362L6.48057 8.93821C6.77354 9.05927 7.00374 9.2913 7.11883 9.57377L8.071 11.7427C8.24888 12.0858 8.76158 12.0858 8.929 11.7427L9.88117 9.57377C10.0067 9.2913 10.2369 9.06936 10.5194 8.93821L11.7332 8.41362C12.0889 8.24212 12.0889 7.74779 11.7332 7.57629L10.5194 7.0517Z" fill="#F9AD3C"/>
<path d="M25.5744 13.5546C24.7045 13.1673 24.0166 12.4539 23.6525 11.5775L20.7897 4.81023C20.2637 3.72992 18.7363 3.72992 18.2103 4.81023L15.3475 11.5775C14.9733 12.4539 14.2854 13.1673 13.4256 13.5546L9.80419 15.1955C8.73194 15.7254 8.73194 17.2746 9.80419 17.8045L13.4256 19.4454C14.2955 19.8327 14.9834 20.5461 15.3475 21.4225L18.2103 28.1898C18.7363 29.2701 20.2637 29.2701 20.7897 28.1898L23.6525 21.4225C24.0267 20.5461 24.7146 19.8327 25.5744 19.4454L29.1958 17.8045C30.2681 17.2746 30.2681 15.7254 29.1958 15.1955L25.5744 13.5546Z" fill="#F9AD3C"/>
<path d="M8.2811 20.3304C8.44173 20.7222 8.73465 21.0258 9.10315 21.2021L10.6528 21.927C11.1157 22.1621 11.1157 22.8379 10.6528 23.073L9.10315 23.7979C8.73465 23.9742 8.44173 24.2876 8.2811 24.6696L7.05276 27.6474C6.82598 28.1175 6.17402 28.1175 5.94724 27.6474L4.7189 24.6696C4.55827 24.2778 4.26535 23.9742 3.89685 23.7979L2.34724 23.073C1.88425 22.8379 1.88425 22.1621 2.34724 21.927L3.89685 21.2021C4.26535 21.0258 4.55827 20.7124 4.7189 20.3304L5.94724 17.3526C6.17402 16.8825 6.82598 16.8825 7.05276 17.3526L8.2811 20.3304Z" fill="#F9AD3C"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -2,9 +2,11 @@
"name": "english",
"code": "en",
"substrings": {
"ContactLink": "<a class=\"text-backdrop\" href=\"{repo}\">let me know</a>"
"ContactLink": "<a class=\"text-backdrop\" href=\"{repo}\" target=\"_blank\">let me know</a>"
},
"strings": {
"ChangelogContentTitle": "everything what you've been waiting for. welcome to cobalt 3.0 :)",
"ChangelogContent": "follow cobalt's twitter account for polls, updates, and more: <a class=\"text-backdrop\" href=\"https://twitter.com/justusecobalt\" target=\"_blank\">@justusecobalt</a>\n\nstuff that you can notice:\n\n- you can now download audio from any supported service, in any format that you set in settings (+). yes, that includes mp3, which you all have been waiting for :D\n- it's now easier to switch between download modes (just a single toggle on the bottom).\n- your youtube download format has been reset, sorry, but that was required to implement all audio downloads.\n- default download format for youtube videos on all platforms is now webm. except for ios.\n\n- cobalt now has emoji, just to spice up the black and white ui. all of them have been tuned to look the best in both themes. isn't it cool?\n- about, changelog, and donation popups have been merged into just one, for covnenience.\n- changelog got a huge upgrade (as you can see), and now there are both major changes and latest commit info, just so commits can finally go back to being batshit insane.\n- changelog popup appears on every major update, but you can disable it in settings, if you want to.\n- changelog now opens by default when pressing \"?\" button. i don't think anyone reads \"about\" as often.\n- settings (+) have been split into three tabs, also for convenience and ease of use.\n\n- added support for donation links. you can now donate through boosty, not only via crypto :D\n- donate popup has been rearranged and tuned just a tiny bit. \n\n- you can now click away from any popup by pressing the void behind it.\n- you can also press \"escape\" key on keyboard to close any popup.\n\n- switchers and buttons are now way easier on eye. white border is gone from where it's unneeded.\n- buttons are now very satisfying to press.\n- switchers are scrollable if there's not enough space to fit all contents on screen.\n- scaling is now even better than before.\n\ninternal stuff:\n\n- frontend won't send video related stuff if audio mode is on.\n- matching has, yet again, gone through mitosis, and is now probably the cleanest it can get.\n- page rendering is now modular, something like what frameworks have but way lighter. this makes adding new features WAY easier.\n- removed some stuff that didn't make sense (like storing language of stream request).\n- cleaned up insides of cobalt, of course.\n- almost all links now open in new tab, just like they should have from the very beginning.\n\nknown issues:\n- impossible to download audio from vk. i'll try to fix it in the next update.\n- headers are not sticky in tabbed popups. maybe this is a good thing, i'll think about it.\n\nif you ever notice any issues, make sure to report them on github. your report doesn't have to sound professional, just do your best to describe the issue.",
"LinkInput": "paste the link here",
"AboutSummary": "{appName} is your go-to place for social media downloads. zero ads, trackers, or any other creepy bullshit attached. simply paste a share link and you're ready to rock!",
"AboutSupportedServices": "currently supported services:",
@ -35,7 +37,7 @@
"ErrorCorruptedStream": "this download is unfortunately corrupted. try again or try a different format and resolution.",
"ErrorNoInternet": "there's no internet or {appName} api is down. check your connection and try again.",
"ErrorCantConnectToServiceAPI": "i couldn't connect to {s} api. seems like either {s} is down or {appName} server ip got blocked. try again later.",
"ErrorEmptyDownload": "there's no media content to download. try something else!",
"ErrorEmptyDownload": "there's nothing to download. try something else!",
"ErrorLiveVideo": "i can't download a live video. wait for stream to finish and try again.",
"ErrorNoStreamID": "there's no such streamId.",
"ErrorNoType": "there's no such expected response type.",
@ -53,15 +55,15 @@
"SettingsQualitySwitchMedium": "medium\n",
"SettingsQualitySwitchLow": "low\n",
"SettingsQualitySwitchLowest": "lowest",
"SettingsFormatSwitchAudio": "audio only",
"SettingsFormatSwitchAudio": "only audio",
"SettingsKeepDownloadButton": "keep &gt;&gt; visible",
"AccessibilityKeepDownloadButton": "keep the download button always visible",
"SettingsEnableDownloadPopup": "ask for a way to save",
"AccessibilityEnableDownloadPopup": "ask what to do with downloads",
"SettingsFormatDescription": "select webm if you need max quality available. webm videos are usually higher quality but ios devices can't play them natively. all \"audio only\" downloads are max quality.",
"SettingsQualityDescription": "if selected resolution isn't available, closest one gets picked instead. if you want to post a youtube video on twitter, select 720p. twitter likes videos like that way more.",
"DonateSubtitle": "it's hard to pay for hosting right now",
"DonateDescription": "i don't like crypto how it is right now, but it's currently the only way for me to pay for anything abroad. mastercard/visa cards and services similar to paypal are no longer an option.",
"SettingsFormatDescription": "select webm if you need max quality available. webm videos are usually higher quality but ios devices can't play them natively.",
"SettingsQualityDescription": "if selected resolution isn't available, closest one gets picked instead. if you want to post a youtube video on twitter, then select a combination of mp4 and 720p. twitter likes videos like that way more.",
"DonateSubtitle": "help me pay for hosting cobalt",
"DonateDescription": "i don't really like crypto in its current state, but it's the only reliable way for me to receive money and pay for anything abroad.",
"LinkGitHubIssues": "&gt;&gt; report issues and check out the source code on github",
"LinkGitHubChanges": "&gt;&gt; see previous changes and contribute on github",
"LinkDonateContact": "&gt;&gt; let me know if currency you want to donate isn't listed",
@ -71,6 +73,21 @@
"DownloadPopupWayToSave": "pick a way to save",
"ClickToCopy": "click to copy",
"Download": "download",
"CopyURL": "copy url"
"CopyURL": "copy url",
"AboutTab": "about",
"ChangelogTab": "changelog",
"DonationsTab": "donations",
"SettingsVideoTab": "video",
"SettingsAudioTab": "audio",
"SettingsOtherTab": "other",
"ChangelogLastCommit": "last commit",
"ChangelogLastMajor": "last major update",
"ModeToggleDefault": "smart mode",
"AccessibilityModeToggle": "toggle download mode",
"DonateLinksDescription": "donation links open in a new tab. this is the best way to donate money, if you want me to receive it directly.",
"SettingsAudioFormatBest": "best",
"SettingsAudioFormatDescription": "when best format is selected, you get audio in best quality available, because audio is kept in its original format. if you select anything other than that, you'll get a slightly compressed file.",
"Keyphrase": "save what you love",
"SettingsDisableChangelogOnUpdate": "don't show changelog after major updates"
}
}

View file

@ -2,7 +2,7 @@
"name": "español",
"code": "es",
"substrings": {
"ContactLink": "<a class=\"text-backdrop\" href=\"{repo}\">hazme saber</a>"
"ContactLink": "<a class=\"text-backdrop\" href=\"{repo}\" target=\"_blank\">hazme saber</a>"
},
"strings": {
"LinkInput": "pega tu enlace aquí",
@ -58,8 +58,8 @@
"AccessibilityKeepDownloadButton": "mantener el botón de descarga siempre visible",
"SettingsEnableDownloadPopup": "pregunta por la forma de guardar",
"AccessibilityEnableDownloadPopup": "preguntar qué hacer con las descargas",
"SettingsFormatDescription": "selecciona webm si necesitas la máxima calidad disponible. los videos webm suelen ser de mayor calidad, pero los dispositivos ios no pueden reproducirlos de forma nativa. todas las descargas \"solo audio\" son de máxima calidad.",
"SettingsQualityDescription": "si la resolución seleccionada no está disponible, se elige la más cercana en su lugar. Si quieres publicar un video de youtube a twitter, selecciona 720p. A twitter le gustan más los videos así.",
"SettingsFormatDescription": "selecciona webm si necesitas la máxima calidad disponible. los videos webm suelen ser de mayor calidad, pero los dispositivos ios no pueden reproducirlos de forma nativa.",
"SettingsQualityDescription": "si la resolución seleccionada no está disponible, se elige la más cercana en su lugar. Si quieres publicar un video de youtube a twitter, seleccionar combinación de mp4 y 720p. A twitter le gustan más los videos así.",
"DonateSubtitle": "pagar por hosting es un poco complicado ahora mismo",
"DonateDescription": "no me gusta cómo está crypto ahora mismo, pero es la única forma que tengo para pagar cualquier cosa en línea. las tarjetas mastercard/visa y los servicios similares a paypal ya no están disponibles.",
"LinkGitHubIssues": "&gt;&gt; informa sobre problemas y consulta el código fuente en github",

View file

@ -2,7 +2,7 @@
"name": "français",
"code": "fr",
"substrings": {
"ContactLink": "<a class=\"text-backdrop\" href=\"{repo}\">fais-moi signe</a>"
"ContactLink": "<a class=\"text-backdrop\" href=\"{repo}\" target=\"_blank\">fais-moi signe</a>"
},
"strings": {
"LinkInput": "copie ton lien ici",
@ -58,8 +58,8 @@
"AccessibilityKeepDownloadButton": "garder le bouton télécharger visible en tout temps",
"SettingsEnableDownloadPopup": "demander comment sauvegarder",
"AccessibilityEnableDownloadPopup": "demander comment sauvegarder",
"SettingsFormatDescription": "sélectionne webm si tu veux la meilleure qualité possible. les vidéos webm sont habituellement de plus haute qualité, mais les appareils ios ne peuvent pas les jouer par défaut. tous les téléchargements «audio seulement» sont de qualité maximum.",
"SettingsQualityDescription": "si la résolution sélectionnée n'est pas disponible, celle la plus proche est choisie à la place. si tu veux envoyer une vidéo youtube sur twitter, sélectionne 720p. twitter préfère les vidéos dans ce format.",
"SettingsFormatDescription": "sélectionne webm si tu veux la meilleure qualité possible. les vidéos webm sont habituellement de plus haute qualité, mais les appareils ios ne peuvent pas les jouer par défaut.",
"SettingsQualityDescription": "si la résolution sélectionnée n'est pas disponible, celle la plus proche est choisie à la place. si tu veux envoyer une vidéo youtube sur twitter, sélectionne combinaison de mp4 et 720p. twitter préfère les vidéos dans ce format.",
"DonateSubtitle": "parce que c'est difficile de payer pour l'hébergement",
"DonateDescription": "je n'aime pas l'état de la crypto présentement, mais c'est présentement la seule manière pour moi de payer à l'international. les cartes mastercard/visa et services similaires à paypal ne sont présentement plus une option.",
"LinkGitHubIssues": "&gt;&gt; signale des problèmes et examine le code source sur github",

View file

@ -2,7 +2,7 @@
"name": "indonesia",
"code": "id",
"substrings": {
"ContactLink": "<a class=\"text-backdrop\" href=\"{repo}\">beri tau saya</a>"
"ContactLink": "<a class=\"text-backdrop\" href=\"{repo}\" target=\"_blank\">beri tau saya</a>"
},
"strings": {
"LinkInput": "tempel link kamu disini",
@ -58,8 +58,8 @@
"AccessibilityKeepDownloadButton": "biarkan tombol download selalu kelihatan",
"SettingsEnableDownloadPopup": "tanya cara untuk simpan",
"AccessibilityEnableDownloadPopup": "tanyakan apa yang dilakukan dengan file yang baru didownload",
"SettingsFormatDescription": "pilih webm kalo kamu butuh kualitas tertinggi. kualitas video webm biasanya lebih tinggi tapi perangkat ios tidak bisa menjalankannya secara native. semua download \"audio saja\" sudah kualitas tertinggi",
"SettingsQualityDescription": "jika resolusi yang dipilih tidak tersedia, resolusi yang dipilih akan diganti dengan resolusi tersedia yang terdekat. kalo kamu mau kirim video youtube di twitter, pilih 720p saja. twitter lebih suka video yang 720p.",
"SettingsFormatDescription": "pilih webm kalo kamu butuh kualitas tertinggi. kualitas video webm biasanya lebih tinggi tapi perangkat ios tidak bisa menjalankannya secara native.",
"SettingsQualityDescription": "jika resolusi yang dipilih tidak tersedia, resolusi yang dipilih akan diganti dengan resolusi tersedia yang terdekat. kalo kamu mau kirim video youtube di twitter, pilih kombinasi mp4 dan 720p. twitter lebih suka video seperti itu.",
"DonateSubtitle": "sekarang lagi susah untuk bayar hosting bagi saya",
"DonateDescription": "saya tidak suka crypto dengan keadaannya sekarang, tetapi saat ini itu adalah satu-satunya cara bagi saya untuk membayar apa pun yang di luar negeri. kartu mastercard/visa dan layanan yang mirip seperti paypal tidak lagi menjadi pilihan.",
"LinkGitHubIssues": "&gt;&gt; laporkan masalah atau liat source code di github",

View file

@ -2,7 +2,7 @@
"name": "polish",
"code": "pl",
"substrings": {
"ContactLink": "<a class=\"text-backdrop\" href=\"{repo}\">daj mi znać</a>"
"ContactLink": "<a class=\"text-backdrop\" href=\"{repo}\" target=\"_blank\">daj mi znać</a>"
},
"strings": {
"LinkInput": "wklej link tutaj",
@ -58,8 +58,8 @@
"AccessibilityKeepDownloadButton": "pozostaw przycisk pobierania zawsze widoczny",
"SettingsEnableDownloadPopup": "pytaj o sposób zapisu",
"AccessibilityEnableDownloadPopup": "pytaj co zrobić z pobranymi plikami",
"SettingsFormatDescription": "wybierz webm jeżeli potrzebujesz najwyższej możliwej jakości. filmy webm są zwykle wyższej jakości ale urządzenia działające na systemie ios nie odtwarzają ich natywnie. wszystkie pliki \"tylko audio\" są maksymalnej jakości.",
"SettingsQualityDescription": "jeżeli wybrana rozdzielczość nie będzie dostępna, zostanie wybrana najbliższa pasująca. jeżeli chcesz wrzucić film z youtube'a na twittera, wybierz 720p. twitter bardziej lubi takie filmy.",
"SettingsFormatDescription": "wybierz webm jeżeli potrzebujesz najwyższej możliwej jakości. filmy webm są zwykle wyższej jakości ale urządzenia działające na systemie ios nie odtwarzają ich natywnie.",
"SettingsQualityDescription": "jeżeli wybrana rozdzielczość nie będzie dostępna, zostanie wybrana najbliższa pasująca. jeżeli chcesz wrzucić film z youtube'a na twittera, wybierz połączenie mp4 i 720p. twitter bardziej lubi takie filmy.",
"DonateSubtitle": "ciężko się teraz płaci za hosting",
"DonateDescription": "nie podoba mi się stan w jakim są teraz kryptowaluty, ale na razie jest to dla mnie jedyny sposób żeby płacić za coś za granicą. karty mastercard/visa i usługi takie jak paypal nie są już dostępną opcją.",
"LinkGitHubIssues": "&gt;&gt; zgłoś problem lub zobacz kod źródłowy na githubie",

View file

@ -2,7 +2,7 @@
"name": "русский",
"code": "ru",
"substrings": {
"ContactLink": "<a class=\"text-backdrop\" href=\"{repo}\">напиши об этом</a>"
"ContactLink": "<a class=\"text-backdrop\" href=\"{repo}\" target=\"_blank\">напиши об этом</a>"
},
"strings": {
"LinkInput": "вставь ссылку сюда",
@ -58,12 +58,12 @@
"AccessibilityKeepDownloadButton": "оставлять кнопку скачивания на экране",
"SettingsEnableDownloadPopup": "спрашивать, как сохранять",
"AccessibilityEnableDownloadPopup": "спрашивать, что делать с загрузками",
"SettingsFormatDescription": "выбирай webm, если хочешь максимальное качество. webm обычно лучше по качеству, но устройства на ios не могут проигрывать их без сторонних приложений. все загрузки \"только аудио\" всегда максимального качества.",
"SettingsQualityDescription": "если выбранное разрешение недоступно, то выбирается ближайшее к нему. если ты хочешь твитнуть загруженное видео, то выбирай 720p. такие видео твиттер обычно воспринимает намного лучше.",
"DonateSubtitle": "сейчас намного сложнее оплачивать хостинг",
"DonateDescription": "я ненавижу крипто, но у меня нет возможности платить любым другим способом.",
"SettingsFormatDescription": "выбирай webm, если хочешь максимальное качество. webm обычно лучше по качеству, но устройства на ios не могут проигрывать их без сторонних приложений.",
"SettingsQualityDescription": "если выбранное разрешение недоступно, то выбирается ближайшее к нему. если ты хочешь твитнуть загруженное видео, то выбирай комбинацию из mp4 и 720p. такие видео твиттер обычно воспринимает намного лучше.",
"DonateSubtitle": "помоги мне платить за хостинг",
"DonateDescription": "я не люблю крипто в его текущем состоянии, но у меня нет другого надёжного способа оплаты хостинга.",
"LinkGitHubIssues": "&gt;&gt; сообщай о проблемах и смотри исходный код на github",
"LinkGitHubChanges": "&gt;&gt; посмотреть предыдущие изменения на github",
"LinkGitHubChanges": "&gt;&gt; смотри предыдущие изменения на github",
"LinkDonateContact": "&gt;&gt; напиши мне, если в этом списке нет подходящей валюты",
"NoScriptMessage": "{appName} использует javascript для обработки ссылок и интерактивного интерфейса. ты должен разрешить использование javascript, чтобы пользоваться сайтом. тут нет никаких трекеров или рекламы, обещаю.",
"DownloadPopupDescriptionIOS": "так как у тебя устройство на ios, тебе нужно зажать кнопку \"скачать\" и выбрать что-то похожее на \"сохранить в галерею\" в появившемся окне.",
@ -71,6 +71,21 @@
"DownloadPopupWayToSave": "выбери, как сохранить",
"ClickToCopy": "нажми, чтобы скопировать",
"Download": "скачать",
"CopyURL": "скопировать ссылку"
"CopyURL": "скопировать ссылку",
"AboutTab": "о cobalt",
"ChangelogTab": "изменения",
"DonationsTab": "донаты",
"SettingsVideoTab": "видео",
"SettingsAudioTab": "аудио",
"SettingsOtherTab": "другое",
"ChangelogLastCommit": "последний коммит (на английском)",
"ChangelogLastMajor": "последнее обновление (на английском)",
"ModeToggleDefault": "умный режим",
"AccessibilityModeToggle": "переключить режим скачивания",
"DonateLinksDescription": "ссылки на донаты открываются в новой вкладке. это лучший метод пожертвовать деньги, если ты хочешь, чтобы я получил их лично, а не в виде крипто.",
"SettingsAudioFormatBest": "лучший",
"SettingsAudioFormatDescription": "когда выбран \"лучший\" формат, ты получишь аудио максимально возможного качества, так как оно останется в оригинальном формате. если же выбрано что-то другое, то аудио будет немного сжато.",
"Keyphrase": "сохраняй то, что любишь",
"SettingsDisableChangelogOnUpdate": "не показывать изменения после обновлений"
}
}

View file

@ -7,7 +7,7 @@ import { errorUnsupported } from "./sub/errors.js";
import loc from "../localization/manager.js";
import match from "./match.js";
export async function getJSON(originalURL, ip, lang, format, quality) {
export async function getJSON(originalURL, ip, lang, format, quality, audioFormat, isAudioOnly) {
try {
let url = decodeURI(originalURL);
if (!url.includes('http://')) {
@ -28,7 +28,7 @@ export async function getJSON(originalURL, ip, lang, format, quality) {
if (patternMatch) break;
}
if (patternMatch) {
return await match(host, patternMatch, url, ip, lang, format, quality);
return await match(host, patternMatch, url, ip, lang, format, quality, audioFormat, isAudioOnly);
} return apiJSON(0, { t: errorUnsupported(lang) } )
} return apiJSON(0, { t: errorUnsupported(lang) } )
} else {

View file

@ -15,4 +15,5 @@ supportedLanguages = config.supportedLanguages,
quality = config.quality,
internetExplorerRedirect = config.internetExplorerRedirect,
donations = config.donations,
ffmpegArgs = config.ffmpegArgs
ffmpegArgs = config.ffmpegArgs,
supportedAudio = config.supportedAudio

24
src/modules/emoji.js Normal file
View file

@ -0,0 +1,24 @@
const names = {
"🎶": "musical_notes",
"🎬": "clapper_board",
"💰": "money_bag",
"🎉": "party_popper",
"❓": "red_question_mark",
"✨": "sparkles",
"🪅": "pinata",
"🪄": "magic_wand",
"🐲": "dragon_face",
"💸": "money_with_wings",
"⚙️": "gear"
}
let sizing = {
22: 0.4,
30: 0.7
}
export default function(emoji, size, disablePadding) {
if (!size) size = 22;
let padding = size != 22 ? `margin-right:${sizing[size] ? sizing[size] : "0.4"}rem;`: ``;
if (disablePadding) padding = 'margin-right:0!important;';
if (!names[emoji]) emoji = "❓";
return `<img class="emoji" height="${size}" width="${size}" style="${padding}" alt="${emoji}" src="emoji/${names[emoji]}.svg">`
}

View file

@ -11,8 +11,9 @@ import vk from "./services/vk.js";
import tiktok from "./services/tiktok.js";
import douyin from "./services/douyin.js";
import tumblr from "./services/tumblr.js";
import matchActionDecider from "./sub/matchActionDecider.js";
export default async function (host, patternMatch, url, ip, lang, format, quality) {
export default async function (host, patternMatch, url, ip, lang, format, quality, audioFormat, isAudioOnly) {
try {
if (!testers[host]) return apiJSON(0, { t: errorUnsupported(lang) });
if (!(testers[host](patternMatch))) throw Error();
@ -24,99 +25,69 @@ export default async function (host, patternMatch, url, ip, lang, format, qualit
id: patternMatch["id"],
lang: lang
});
return (!r.error) ? apiJSON(1, { u: r }) : apiJSON(0, { t: r.error });
break;
case "vk":
r = await vk({
userId: patternMatch["userId"],
videoId: patternMatch["videoId"],
lang: lang, quality: quality
});
return (!r.error) ? apiJSON(2, { type: "bridge", lang: lang, u: r.url, filename: r.filename,
service: host, ip: ip, salt: process.env.streamSalt }) : apiJSON(0, { t: r.error });
break;
case "bilibili":
r = await bilibili({
id: patternMatch["id"].slice(0, 12),
lang: lang
});
return (!r.error) ? apiJSON(2, {
type: "render", u: r.urls, lang: lang,
service: host, ip: ip,
filename: r.filename,
salt: process.env.streamSalt, time: r.time
}) : apiJSON(0, { t: r.error });
break;
case "youtube":
let fetchInfo = {
id: patternMatch["id"].slice(0,11),
lang: lang, quality: quality,
format: "mp4"
format: "webm"
};
if (url.match('music.youtube.com')) {
format = "audio"
}
if (url.match('music.youtube.com') || isAudioOnly == true) format = "audio";
switch (format) {
case "webm":
fetchInfo["format"] = "webm";
case "mp4":
fetchInfo["format"] = "mp4";
break;
case "audio":
fetchInfo["format"] = "webm";
fetchInfo["isAudioOnly"] = true;
fetchInfo["quality"] = "max";
isAudioOnly = true;
break;
}
r = await youtube(fetchInfo);
return (!r.error) ? apiJSON(2, {
type: r.type, u: r.urls, lang: lang, service: host, ip: ip,
filename: r.filename, salt: process.env.streamSalt,
isAudioOnly: fetchInfo["isAudioOnly"] ? fetchInfo["isAudioOnly"] : false,
time: r.time,
}) : apiJSON(0, { t: r.error });
break;
case "reddit":
r = await reddit({
sub: patternMatch["sub"],
id: patternMatch["id"],
title: patternMatch["title"], lang: lang,
});
return (!r.error) ? apiJSON(r.typeId, {
type: r.type, u: r.urls, lang: lang,
service: host, ip: ip,
filename: r.filename, salt: process.env.streamSalt
}) : apiJSON(0, { t: r.error });
break;
case "tiktok":
r = await tiktok({
postId: patternMatch["postId"],
id: patternMatch["id"], lang: lang,
});
return (!r.error) ? apiJSON(2, {
type: "bridge", u: r.urls, lang: lang,
service: host, ip: ip,
filename: r.filename, salt: process.env.streamSalt
}) : apiJSON(0, { t: r.error });
break;
case "douyin":
r = await douyin({
postId: patternMatch["postId"],
id: patternMatch["id"], lang: lang,
});
return (!r.error) ? apiJSON(2, {
type: "bridge", u: r.urls, lang: lang,
service: host, ip: ip,
filename: r.filename, salt: process.env.streamSalt
}) : apiJSON(0, { t: r.error });
break;
case "tumblr":
r = await tumblr({
id: patternMatch["id"], url: url, user: patternMatch["user"] ? patternMatch["user"] : false,
lang: lang
});
return (!r.error) ? apiJSON(1, { u: r.split('?')[0] }) : apiJSON(0, { t: r.error });
break;
default:
return apiJSON(0, { t: errorUnsupported(lang) });
}
return matchActionDecider(r, host, ip, audioFormat, isAudioOnly)
} catch (e) {
return apiJSON(0, { t: genericError(lang, host) })
}

View file

@ -1,205 +0,0 @@
import { services, appName, authorInfo, version, quality, repo, donations } from "./config.js";
import { getCommitInfo } from "./sub/currentCommit.js";
import loc from "../localization/manager.js";
let s = services;
let com = getCommitInfo();
let enabledServices = Object.keys(s).filter((p) => {
if (s[p].enabled) {
return true
}
}).sort().map((p) => {
if (s[p].alias) {
return s[p].alias
} else {
return p
}
}).join(', ')
let donate = ``
for (let i in donations) {
donate += `<div class="subtitle">${i} (REPLACEME)</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 {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=${isIOS ? `1` : `5`}" />
<title>${appName}</title>
<meta property="og:url" content="${process.env.selfURL}" />
<meta property="og:title" content="${appName}" />
<meta property="og:description" content="${loc(obj.lang, 'EmbedBriefDescription')}" />
<meta property="og:image" content="${process.env.selfURL}icons/generic.png" />
<meta name="title" content="${appName}" />
<meta name="description" content="${loc(obj.lang, 'AboutSummary')}" />
<meta name="theme-color" content="#000000" />
<meta name="twitter:card" content="summary" />
<link rel="icon" type="image/x-icon" href="icons/favicon.ico" />
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png" />
<link rel="apple-touch-icon" sizes="180x180" href="icons/apple-touch-icon.png" />
<link rel="manifest" href="manifest.webmanifest" />
<link rel="stylesheet" href="cobalt.css" />
<link rel="stylesheet" href="fonts/notosansmono.css" />
<noscript><div style="margin: 2rem;">${loc(obj.lang, 'NoScriptMessage')}</div></noscript>
</head>
<body id="cobalt-body" data-nosnippet>
<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, 'AccessibilityClosePopup')}">x</button>
<div id="title" class="popup-title">${loc(obj.lang, 'TitlePopupAbout')}</div>
</div>
<div id="content" class="popup-content with-footer">
<div id="desc" class="popup-desc about-padding">${loc(obj.lang, 'AboutSummary')}</div>
<div id="desc" class="popup-desc about-padding">${loc(obj.lang, 'AboutSupportedServices')} ${enabledServices}.</div>
<div id="desc" class="popup-desc bottom-link"><a class="text-backdrop" href="${repo}">${loc(obj.lang, 'LinkGitHubIssues')}</a></div>
</div>
<div id="popup-footer" class="popup-footer">
<a id="popup-bottom" class="popup-footer-content" href="${authorInfo.link}">${loc(obj.lang, 'MadeWithLove')}</a>
</div>
</div>
<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, 'AccessibilityClosePopup')}">x</button>
<div id="title" class="popup-title">${loc(obj.lang, 'TitlePopupChangelog')}</div>
<div id="desc" class="popup-subtitle">${com[0]} (${obj.hash})</div>
</div>
<div id="content" class="popup-content">
<div id="desc" class="popup-desc about-padding">${com[1]}</div>
<div id="desc" class="popup-desc bottom-link"><a class="text-backdrop" href="${repo}/commits">${loc(obj.lang, 'LinkGitHubChanges')}</a></div>
</div>
</div>
<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, 'AccessibilityClosePopup')}">x</button>
<div id="title" class="popup-title">${loc(obj.lang, 'TitlePopupDonate')}</div>
<div id="desc" class="little-subtitle">${loc(obj.lang, 'DonateSubtitle')}</div>
</div>
<div id="content" class="popup-content">
${donate.replace(/REPLACEME/g, loc(obj.lang, 'ClickToCopy').trim())}
<div id="desc" class="explanation about-padding">${loc(obj.lang, 'DonateDescription')}</div>
<div id="desc" class="popup-desc bottom-link"><a class="text-backdrop" href="${authorInfo.contact}">${loc(obj.lang, 'LinkDonateContact')}</a></div>
</div>
</div>
<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, 'AccessibilityClosePopup')}">x</button>
<div id="version" class="popup-above-title">v.${version} ~ ${obj.hash}</div>
<div id="title" class="popup-title">${loc(obj.lang, 'TitlePopupSettings')}</div>
</div>
<div id="content" class="popup-content">
<div id="settings-appearance" class="settings-category">
<div class="title">${loc(obj.lang, 'SettingsAppearanceSubtitle')}</div>
<div class="settings-category-content">
<div id="theme-switcher" class="switch-container">
<div class="subtitle">${loc(obj.lang, 'SettingsThemeSubtitle')}</div>
<div class="switches">
<div id="theme-auto" class="switch full" onclick="changeSwitcher('theme', 'auto', 1)">${loc(obj.lang, 'SettingsThemeAuto')}</div>
<div id="theme-dark" class="switch" onclick="changeSwitcher('theme', 'dark', 1)">${loc(obj.lang, 'SettingsThemeDark')}</div>
<div id="theme-light" class="switch full" onclick="changeSwitcher('theme', 'light', 1)">${loc(obj.lang, 'SettingsThemeLight')}</div>
</div>
</div>
</div>
</div>
<div id="settings-downloads" class="settings-category">
<div class="title">${loc(obj.lang, 'SettingsDownloadsSubtitle')}</div>
<div class="settings-category-content">
<div id="quality-switcher" class="switch-container">
<div class="subtitle">${loc(obj.lang, 'SettingsQualitySubtitle')}</div>
<div class="switches">
<div id="quality-max" class="switch full" onclick="changeSwitcher('quality', 'max', 1)">${loc(obj.lang, 'SettingsQualitySwitchMax')}</div>
<div id="quality-hig" class="switch" onclick="changeSwitcher('quality', 'hig', 1)">${loc(obj.lang, 'SettingsQualitySwitchHigh')}(${quality.hig}p)</div>
<div id="quality-mid" class="switch full" onclick="changeSwitcher('quality', 'mid', 1)">${loc(obj.lang, 'SettingsQualitySwitchMedium')}(${quality.mid}p)</div>
<div id="quality-low" class="switch right" onclick="changeSwitcher('quality', 'low', 1)">${loc(obj.lang, 'SettingsQualitySwitchLow')}(${quality.low}p)</div>
</div>
<div class="explanation">${loc(obj.lang, 'SettingsQualityDescription')}</div>
</div>
</div>
</div>
<div id="settings-youtube" class="settings-category">
<div class="title">youtube</div>
<div class="settings-category-content">
<div id="youtube-switcher" class="switch-container">
<div class="subtitle">${loc(obj.lang, 'SettingsFormatSubtitle')}</div>
<div class="switches">
<div id="youtubeFormat-mp4" class="switch full" onclick="changeSwitcher('youtubeFormat', 'mp4', 1)">mp4</div>
<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)">${loc(obj.lang, 'SettingsFormatSwitchAudio')}</div>
</div>
<div class="explanation">${loc(obj.lang, 'SettingsFormatDescription')}</div>
</div>
</div>
</div>
<div id="settings-misc" class="settings-category">
<div class="title bottom-margin">${loc(obj.lang, 'SettingsMiscSubtitle')}</div>
<div class="settings-category-content">
<label class="checkbox">
<input id="alwaysVisibleButton" type="checkbox" aria-label="${loc(obj.lang, 'AccessibilityKeepDownloadButton')}" onclick="checkbox('alwaysVisibleButton', 'always-visible-button')">
<span>${loc(obj.lang, 'SettingsKeepDownloadButton')}</span>
</label>${!isIOS ? `
<label class="checkbox">
<input id="downloadPopup" type="checkbox" aria-label="${loc(obj.lang, 'AccessibilityEnableDownloadPopup')}" onclick="checkbox('downloadPopup', 'always-visible-button')">
<span>${loc(obj.lang, 'SettingsEnableDownloadPopup')}</span>
</label>` : ``}
</div>
</div>
</div>
</div>
<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, 'AccessibilityClosePopup')}">x</button>
<div id="title" class="popup-subtitle">${loc(obj.lang, 'TitlePopupDownload')}</div>
</div>
<div id="content" class="popup-content">
<div id="theme-switcher" class="switch-container small-padding">
<div class="subtitle">${loc(obj.lang, 'DownloadPopupDescription')}</div>
<div class="switches">
<a id="pd-download" class="switch full space-right" target="_blank" href="/">${loc(obj.lang, 'Download')}</a>
<div id="pd-copy" class="switch full">${loc(obj.lang, 'CopyURL')}</div>
</div>
</div>
<div id="desc" class="explanation about-padding">${isIOS ? loc(obj.lang, 'DownloadPopupDescriptionIOS') : loc(obj.lang, 'DownloadPopupDescription')}</div>
</div>
</div>
<div id="popup-error" class="popup center box" style="visibility: hidden;">
<div id="popup-header" class="popup-header">
<button id="close" class="button mono" onclick="popup('error', 0)" aria-label="${loc(obj.lang, 'AccessibilityClosePopup')}">x</button>
<div id="title" class="popup-title">${loc(obj.lang, 'TitlePopupError')}</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;">
<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="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>
<footer id="footer" style="visibility: hidden;">
<div id="footer-buttons">
<button id="about-open" class="button footer-button" onclick="popup('about', 1)" aria-label="${loc(obj.lang, 'AccessibilityOpenAbout')}">?</button>
<button id="changelog-open" class="button footer-button" onclick="popup('changelog', 1)" aria-label="${loc(obj.lang, 'AccessibilityOpenChangelog')}">!</button>
<button id="donate-open" class="button footer-button" onclick="popup('donate', 1)" aria-label="${loc(obj.lang, 'AccessibilityOpenDonate')}">$</button>
<button id="settings-open" class="button footer-button" onclick="popup('settings', 1)" aria-label="${loc(obj.lang, 'AccessibilityOpenSettings')}">+</button>
</div>
</footer>
</body>
<script type="text/javascript">const loc = {noInternet:"${loc(obj.lang, 'ErrorNoInternet')}", noURLReturned: "${loc(obj.lang, 'ErrorBadFetch')}"};</script>
<script type="text/javascript" src="cobalt.js"></script>
</html>`;
} catch (err) {
return `${loc(obj.lang, 'ErrorPageRenderFail', obj.hash)}`;
}
}

View file

@ -0,0 +1,95 @@
export function switcher(obj) {
let items = ``;
switch(obj.name) {
case "download":
items = obj.items;
break;
default:
for (let i = 0; i < obj.items.length; i++) {
let classes = obj.items[i]["classes"] ? obj.items[i]["classes"] : []
items += `<button id="${obj.name}-${obj.items[i]["action"]}" class="switch${classes.length > 0 ? ' ' + classes.join(' ') : ''}" onclick="changeSwitcher('${obj.name}', '${obj.items[i]["action"]}')">${obj.items[i]["text"] ? obj.items[i]["text"] : obj.items[i]["action"]}</button>`
}
break;
}
return `
<div id="${obj.name}-switcher" class="switch-container">
${obj.subtitle ? `<div class="subtitle">${obj.subtitle}</div>` : ``}
<div class="switches">${items}</div>
${obj.explanation ? `<div class="explanation">${obj.explanation}</div>` : ``}
</div>`
}
export function checkbox(action, text, aria) {
return `<label class="checkbox">
<input id="${action}" type="checkbox" ${aria ? `aria-label="${aria}"` : ''} onclick="checkbox('${action}')">
<span>${text}</span>
</label>`
}
export function popup(obj) {
let classes = obj.classes ? obj.classes : []
let body = obj.body;
if (Array.isArray(obj.body)) {
body = ``
for (let i = 0; i < obj.body.length; i++) {
let classes = obj.body[i]["classes"] ? obj.body[i]["classes"] : []
if (i != obj.body.length - 1 && !obj.body[i]["nopadding"]) {
classes.push("desc-padding")
}
body += obj.body[i]["raw"] ? obj.body[i]["text"] : `<div id="popup-desc" class="${classes.length > 0 ? classes.join(' ') : ''}">${obj.body[i]["text"]}</div>`
}
}
return `
${!obj.embed ? `<div id="popup-${obj.name}" class="popup center box${classes.length > 0 ? ' ' + classes.join(' ') : ''}" style="visibility: hidden;">`: ''}
<div id="popup-header" class="popup-header">
${!obj.embed ? `<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.title ? `<div id="popup-title">${obj.header.title}</div>` : ''}
${obj.header.subtitle ? `<div id="popup-subtitle">${obj.header.subtitle}</div>` : ''}
</div>
<div id="popup-content"${obj.footer ? ' class="with-footer"' : ''}>${body}</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>
</div>` : ''}
${!obj.embed ? `</div>`: ''}`
}
export function multiPagePopup(obj) {
let tabs = ``
let tabContent = ``
for (let i = 0; i < obj.tabs.length; i++) {
tabs += `<button id="tab-button-${obj.name}-${obj.tabs[i]["name"]}" class="switch tab tab-${obj.name}" onclick="changeTab(event, 'tab-${obj.name}-${obj.tabs[i]["name"]}', '${obj.name}')">${obj.tabs[i]["title"]}</button>`
tabContent += `<div id="tab-${obj.name}-${obj.tabs[i]["name"]}" class="popup-tab-content tab-content-${obj.name}">${obj.tabs[i]["content"]}</div>`
}
tabs += `<button id="close-bottom" class="switch tab-${obj.name}" onclick="popup('${obj.name}', 0)" ${obj.closeAria ? `aria-label="${obj.closeAria}"` : ''}>x</button>`
return `
<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.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>
</div>`
}
export function backdropLink(link, text) {
return `<a class="text-backdrop" href="${link}" target="_blank">${text}</a>`
}
export function settingsCategory(obj) {
return `<div id="settings-${obj.name}" class="settings-category">
<div class="category-title">${obj.title ? obj.title : obj.name}</div>
<div class="settings-category-content">${obj.body}</div>
</div>`
}
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> `
}
return `
<div id="footer-buttons">${items}</div>`
}

View file

@ -0,0 +1,291 @@
import { services, appName, authorInfo, version, quality, repo, donations, supportedAudio } from "../config.js";
import { getCommitInfo } from "../sub/currentCommit.js";
import loc from "../../localization/manager.js";
import { backdropLink, checkbox, footerButtons, multiPagePopup, popup, settingsCategory, switcher } from "./elements.js";
import emoji from "../emoji.js";
let s = services;
let com = getCommitInfo();
let enabledServices = Object.keys(s).filter((p) => {
if (s[p].enabled) return true;
}).sort().map((p) => {
return s[p].alias ? s[p].alias : p
}).join(', ')
let donate = ``
let donateLinks = ``
let audioFormats = supportedAudio.map((p) => {
return {"action": p}
})
audioFormats.unshift({ "action": "best" })
for (let i in donations["other"]) {
donateLinks += `<a id="don-${i}" class="switch autowidth" href="${donations["other"][i]}" target="_blank">${i}</a>`
}
let extr = ''
for (let i in donations["crypto"]) {
donate += `<div class="subtitle${extr}">${i} (REPLACEME)</div><div id="don-${i}" class="text-to-copy" onClick="copy('don-${i}')">${donations["crypto"][i]}</div>`
extr = ' extra'
}
export default function(obj) {
audioFormats[0]["text"] = loc(obj.lang, 'SettingsAudioFormatBest')
let isIOS = obj.useragent.toLowerCase().match("iphone os")
try {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=${isIOS ? `1` : `5`}" />
<title>${appName}</title>
<meta property="og:url" content="${process.env.selfURL}" />
<meta property="og:title" content="${appName}" />
<meta property="og:description" content="${loc(obj.lang, 'EmbedBriefDescription')}" />
<meta property="og:image" content="${process.env.selfURL}icons/generic.png" />
<meta name="title" content="${appName}" />
<meta name="description" content="${loc(obj.lang, 'AboutSummary')}" />
<meta name="theme-color" content="#000000" />
<meta name="twitter:card" content="summary" />
<link rel="icon" type="image/x-icon" href="icons/favicon.ico" />
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png" />
<link rel="apple-touch-icon" sizes="180x180" href="icons/apple-touch-icon.png" />
<link rel="manifest" href="manifest.webmanifest" />
<link rel="stylesheet" href="cobalt.css" />
<link rel="stylesheet" href="fonts/notosansmono.css" />
<noscript><div style="margin: 2rem;">${loc(obj.lang, 'NoScriptMessage')}</div></noscript>
</head>
<body id="cobalt-body" data-nosnippet>
${multiPagePopup({
name: "about",
closeAria: loc(obj.lang, 'AccessibilityClosePopup'),
tabs: [{
name: "about",
title: `${emoji("🐲")} ${loc(obj.lang, 'AboutTab')}`,
content: popup({
embed: true,
name: "about",
header: {
aboveTitle: {
text: loc(obj.lang, 'MadeWithLove'),
url: authorInfo.link
},
closeAria: loc(obj.lang, 'AccessibilityClosePopup'),
title: loc(obj.lang, 'TitlePopupAbout')
},
body: [{
text: loc(obj.lang, 'AboutSummary')
}, {
text: `${loc(obj.lang, 'AboutSupportedServices')} ${enabledServices}.`
}, {
text: backdropLink(repo, loc(obj.lang, 'LinkGitHubIssues')),
classes: ["bottom-link"]
}]
})
}, {
name: "changelog",
title: `${emoji("🎉")} ${loc(obj.lang, 'ChangelogTab')}`,
content: popup({
embed: true,
name: "changelog",
header: {
closeAria: loc(obj.lang, 'AccessibilityClosePopup'),
title: `${emoji("🪄", 30)} ${loc(obj.lang, 'TitlePopupChangelog')}`
},
body: [{
text: `<div class="category-title">${loc(obj.lang, 'ChangelogLastMajor')}</div>`,
raw: true
}, {
text: loc('en', 'ChangelogContentTitle'),
classes: ["changelog-subtitle"],
nopadding: true
}, {
text: loc('en', 'ChangelogContent')
}, {
text: `<div class="category-title">${loc(obj.lang, 'ChangelogLastCommit')}</div>`,
raw: true
}, {
text: `${com[0]} (${obj.hash})`,
classes: ["changelog-subtitle"],
nopadding: true
}, {
text: com[1]
}, {
text: backdropLink(`${repo}/commits`, loc(obj.lang, 'LinkGitHubChanges')),
classes: ["bottom-link"]
}]
})
}, {
name: "donate",
title: `${emoji("💰")} ${loc(obj.lang, 'DonationsTab')}`,
content: popup({
embed: true,
name: "donate",
header: {
closeAria: loc(obj.lang, 'AccessibilityClosePopup'),
title: emoji("💸", 30) + loc(obj.lang, 'TitlePopupDonate'),
subtitle: loc(obj.lang, 'DonateSubtitle')
},
body: [{
text: donateLinks,
raw: true
}, {
text: loc(obj.lang, 'DonateLinksDescription'),
classes: ["explanation"]
}, {
text: donate.replace(/REPLACEME/g, loc(obj.lang, 'ClickToCopy'))
}, {
text: loc(obj.lang, 'DonateDescription'),
classes: ["explanation", "no-top-padding"]
}, {
text: backdropLink(authorInfo.contact, loc(obj.lang, 'LinkDonateContact')),
classes: ["bottom-link"]
}]
})
}],
})}
${multiPagePopup({
name: "settings",
closeAria: loc(obj.lang, 'AccessibilityClosePopup'),
header: {
aboveTitle: {
text: `v.${version} ~ ${obj.hash}`,
url: repo
},
title: `${emoji("⚙️", 30)} ${loc(obj.lang, 'TitlePopupSettings')}`
},
tabs: [{
name: "video",
title: `${emoji("🎬")} ${loc(obj.lang, 'SettingsVideoTab')}`,
content: settingsCategory({
name: "downloads",
title: loc(obj.lang, 'SettingsDownloadsSubtitle'),
body: switcher({
name: "quality",
subtitle: loc(obj.lang, 'SettingsQualitySubtitle'),
explanation: loc(obj.lang, 'SettingsQualityDescription'),
items: [{
"action": "max",
"text": loc(obj.lang, 'SettingsQualitySwitchMax')
}, {
"action": "hig",
"text": `${loc(obj.lang, 'SettingsQualitySwitchHigh')}(${quality.hig}p)`
}, {
"action": "mid",
"text": `${loc(obj.lang, 'SettingsQualitySwitchMedium')}(${quality.mid}p)`
}, {
"action": "low",
"text": `${loc(obj.lang, 'SettingsQualitySwitchLow')}(${quality.low}p)`
}]
})
}) + `${!isIOS ? checkbox("downloadPopup", loc(obj.lang, 'SettingsEnableDownloadPopup'), loc(obj.lang, 'AccessibilityEnableDownloadPopup')) : ''}`
+ settingsCategory({
name: "youtube",
body: switcher({
name: "ytFormat",
subtitle: loc(obj.lang, 'SettingsFormatSubtitle'),
explanation: loc(obj.lang, 'SettingsFormatDescription'),
items: [{
"action": "mp4"
}, {
"action": "webm"
}]
})
})
}, {
name: "audio",
title: `${emoji("🎶")} ${loc(obj.lang, 'SettingsAudioTab')}`,
content: settingsCategory({
name: "general",
title: loc(obj.lang, 'SettingsAudioTab'),
body: switcher({
name: "audioFormat",
subtitle: loc(obj.lang, 'SettingsFormatSubtitle'),
explanation: loc(obj.lang, 'SettingsAudioFormatDescription'),
items: audioFormats
})
})
}, {
name: "other",
title: `${emoji("🪅")} ${loc(obj.lang, 'SettingsOtherTab')}`,
content: settingsCategory({
name: "appearance",
title: loc(obj.lang, 'SettingsAppearanceSubtitle'),
body: switcher({
name: "theme",
subtitle: loc(obj.lang, 'SettingsThemeSubtitle'),
items: [{
"action": "auto",
"text": loc(obj.lang, 'SettingsThemeAuto')
},{
"action": "dark",
"text": loc(obj.lang, 'SettingsThemeDark')
},{
"action": "light",
"text": loc(obj.lang, 'SettingsThemeLight')
}]
})
}) + checkbox("alwaysVisibleButton", loc(obj.lang, 'SettingsKeepDownloadButton'), loc(obj.lang, 'AccessibilityKeepDownloadButton')) + checkbox("disableChangelog", loc(obj.lang, 'SettingsDisableChangelogOnUpdate'), loc(obj.lang, 'SettingsDisableChangelogOnUpdate'))
}],
})}
${popup({
name: "download",
header: {
closeAria: loc(obj.lang, 'AccessibilityClosePopup'),
subtitle: loc(obj.lang, 'TitlePopupDownload')
},
body: switcher({
name: "download",
subtitle: loc(obj.lang, 'DownloadPopupWayToSave'),
explanation: `${!isIOS ? loc(obj.lang, 'DownloadPopupDescription') : loc(obj.lang, 'DownloadPopupDescriptionIOS')}`,
items: `<a id="pd-download" class="switch full space-right" target="_blank" href="/">${loc(obj.lang, 'Download')}</a>
<div id="pd-copy" class="switch full">${loc(obj.lang, 'CopyURL')}</div>`
})
})}
${popup({
name: "error",
header: {
closeAria: loc(obj.lang, 'AccessibilityClosePopup'),
title: loc(obj.lang, 'TitlePopupError')
},
body: `<div id="desc-error"></div>`
})}
<div id="popup-backdrop" style="visibility: hidden;" onclick="hideAllPopups()"></div>
<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="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>
<footer id="footer" style="visibility: hidden;">
${footerButtons([{
name: "about",
type: "popup",
icon: "?",
aria: loc(obj.lang, 'AccessibilityOpenAbout')
}, {
name: "settings",
type: "popup",
icon: "+",
aria: loc(obj.lang, 'AccessibilityOpenSettings')
}, {
name: "audioMode",
type: "toggle",
icon: emoji("✨", 22, 1),
aria: loc(obj.lang, 'AccessibilityModeToggle')
}]
)}
</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" src="cobalt.js"></script>
</html>`;
} catch (err) {
return `${loc(obj.lang, 'ErrorPageRenderFail', obj.hash)}`;
}
}

View file

@ -20,7 +20,7 @@ export default async function(obj) {
let audio = streamData["data"]["dash"]["audio"].filter((a) => {
if (!a["baseUrl"].includes("https://upos-sz-mirrorcosov.bilivideo.com/")) return true;
}).sort((a, b) => Number(b.bandwidth) - Number(a.bandwidth));
return { urls: [video[0]["baseUrl"], audio[0]["baseUrl"]], time: streamData.data.timelength, filename: `bilibili_${obj.id}_${video[0]["width"]}x${video[0]["height"]}.mp4` };
return { urls: [video[0]["baseUrl"], audio[0]["baseUrl"]], time: streamData.data.timelength, audioFilename: `bilibili_${obj.id}_audio`, filename: `bilibili_${obj.id}_${video[0]["width"]}x${video[0]["height"]}.mp4` };
} else {
return { error: loc(obj.lang, 'ErrorLengthLimit', maxVideoDuration / 60000) };
}

View file

@ -28,7 +28,7 @@ export default async function(obj) {
});
iteminfo = JSON.parse(iteminfo.body);
if (iteminfo['item_list'][0]['video']['play_addr']['url_list'][0]) {
return { urls: iteminfo['item_list'][0]['video']['play_addr']['url_list'][0].replace("playwm", "play"), filename: `douyin_${obj.postId}.mp4` };
return { urls: iteminfo['item_list'][0]['video']['play_addr']['url_list'][0].replace("playwm", "play"), audioFilename: `douyin_${obj.postId}_audio`, filename: `douyin_${obj.postId}.mp4` };
} else {
return { error: loc(obj.lang, 'ErrorEmptyDownload') };
}

View file

@ -14,10 +14,11 @@ export default async function(obj) {
} catch (err) {
audio = ''
}
let id = data["secure_media"]["reddit_video"]["fallback_url"].split('/')[3]
if (audio.length > 0) {
return { typeId: 2, type: "render", urls: [video, audio], filename: `reddit_${data["secure_media"]["reddit_video"]["fallback_url"].split('/')[3]}.mp4` };
return { typeId: 2, type: "render", urls: [video, audio], audioFilename: `reddit_${id}_audio`, filename: `reddit_${id}.mp4` };
} else {
return { typeId: 1, urls: video};
return { typeId: 1, urls: video, audioFilename: loc(obj.lang, 'ErrorEmptyDownload')};
}
} else {
return { error: loc(obj.lang, 'ErrorEmptyDownload') };

View file

@ -23,7 +23,7 @@ export default async function(obj) {
});
html = html.body;
if (html.includes(',"preloadList":[{"url":"')) {
return { urls: unicodeDecode(html.split(',"preloadList":[{"url":"')[1].split('","id":"')[0].trim()), filename: `tiktok_${obj.postId}.mp4` };
return { urls: unicodeDecode(html.split(',"preloadList":[{"url":"')[1].split('","id":"')[0].trim()), audioFilename: `tiktok_${obj.postId}_audio`, filename: `tiktok_${obj.postId}.mp4` };
} else {
return { error: loc(obj.lang, 'ErrorEmptyDownload') };
}

View file

@ -14,7 +14,7 @@ export default async function(obj) {
if (html.includes('<!-- GOOGLE CAROUSEL --><script type="application/ld+json">')) {
let json = JSON.parse(html.split('<!-- GOOGLE CAROUSEL --><script type="application/ld+json">')[1].split('</script>')[0])
if (json["video"] && json["video"]["contentUrl"]) {
return json["video"]["contentUrl"]
return { urls: json["video"]["contentUrl"], audioFilename: `tumblr_${obj.id}_audio` }
} else return { error: loc(obj.lang, 'ErrorEmptyDownload') }
} else return { error: loc(obj.lang, 'ErrorBrokenLink', 'tumblr') }
} else return { error: loc(obj.lang, 'ErrorBrokenLink', 'tumblr') }

View file

@ -39,7 +39,7 @@ export default async function (obj) {
if (parsbod.hasOwnProperty("extended_entities") && parsbod["extended_entities"].hasOwnProperty("media")) {
if (parsbod["extended_entities"]["media"][0]["type"] === "video" || parsbod["extended_entities"]["media"][0]["type"] === "animated_gif") {
let variants = parsbod["extended_entities"]["media"][0]["video_info"]["variants"]
return variants.filter((v) => { if (v["content_type"] == "video/mp4") return true; }).sort((a, b) => Number(b.bitrate) - Number(a.bitrate))[0]["url"].split('?')[0];
return { urls: variants.filter((v) => { if (v["content_type"] == "video/mp4") return true; }).sort((a, b) => Number(b.bitrate) - Number(a.bitrate))[0]["url"].split('?')[0], audioFilename: `twitter_${obj.id}_audio` }
} else {
return nothing
}

View file

@ -26,9 +26,9 @@ export default async function(obj) {
let maxQuality = js["player"]["params"][0][selectedQuality].split('type=')[1].slice(0, 1)
let userQuality = selectQuality('vk', obj.quality, Object.entries(services.vk.quality_match).reduce((r, [k, v]) => { r[v] = k; return r;})[maxQuality])
let id = js["player"]["params"][0][selectedQuality].split("id=")[1]
if (selectedQuality in js["player"]["params"][0]) {
return { url: js["player"]["params"][0][selectedQuality].replace(`type=${maxQuality}`, `type=${services.vk.quality_match[userQuality]}`), filename: `vk_${js["player"]["params"][0][selectedQuality].split("id=")[1]}_${attr['width']}x${attr['height']}.mp4` };
return { urls: js["player"]["params"][0][selectedQuality].replace(`type=${maxQuality}`, `type=${services.vk.quality_match[userQuality]}`), filename: `vk_${js["player"]["params"][0][selectedQuality].split("id=")[1]}_${attr['width']}x${attr['height']}.mp4`, audioFilename: loc(obj.lang, 'ErrorEmptyDownload') };
} else {
return { error: loc(obj.lang, 'ErrorEmptyDownload') };
}

View file

@ -54,7 +54,7 @@ export default async function (obj) {
return { type: "render", urls: [video[0]["url"], audio[0]["url"]], time: video[0]["approxDurationMs"],
filename: `youtube_${obj.id}_${video[0]["width"]}x${video[0]["height"]}.${video[0]["container"]}` };
} else if (audio.length > 0) {
return { type: "bridge", isAudioOnly: true, urls: audio[0]["url"], filename: `youtube_${obj.id}_${audio[0]["audioBitrate"]}kbps.${audio[0]["container"] == "webm" ? "opus" : "m4a"}` };
return { type: "bridge", isAudioOnly: true, urls: audio[0]["url"], audioFilename: `youtube_${obj.id}_audio` };
} else {
return { error: loc(obj.lang, 'ErrorBadFetch') };
}

View file

@ -21,8 +21,9 @@ export function createStream(obj) {
ip: iphmac,
exp: exp,
isAudioOnly: obj.isAudioOnly ? true : false,
audioFormat: obj.audioFormat,
time: obj.time,
lang: obj.lang
copy: obj.copy
});
return `${process.env.selfURL}api/stream?t=${streamUUID}&e=${exp}&h=${ghmac}`;
}

View file

@ -2,19 +2,19 @@ import { apiJSON } from "../sub/utils.js";
import { verifyStream } from "./manage.js";
import { streamAudioOnly, streamDefault, streamLiveRender } from "./types.js";
export default function(res, ip, id, hmac, exp) {
export default function(res, ip, id, hmac, exp, lang) {
try {
let streamInfo = verifyStream(ip, id, hmac, exp, process.env.streamSalt);
if (!streamInfo.error) {
if (streamInfo.isAudioOnly && streamInfo.type == "render") {
if (streamInfo.isAudioOnly && streamInfo.type != "bridge") {
streamAudioOnly(streamInfo, res);
} else {
switch (streamInfo.type) {
case "render":
streamLiveRender(streamInfo, res);
streamLiveRender(streamInfo, res, lang);
break;
default:
streamDefault(streamInfo, res);
streamDefault(streamInfo, res, lang);
break;
}
}

View file

@ -8,7 +8,7 @@ import loc from "../../localization/manager.js";
export async function streamDefault(streamInfo, res) {
try {
res.setHeader('Content-disposition', `attachment; filename="${streamInfo.filename}"`);
res.setHeader('Content-disposition', `attachment; filename="${streamInfo.isAudioOnly ? `${streamInfo.filename}.${streamInfo.audioFormat}` : streamInfo.filename}"`);
const stream = got.get(streamInfo.urls, {
headers: {
"user-agent": genericUserAgent
@ -25,7 +25,7 @@ export async function streamDefault(streamInfo, res) {
internalError(res);
}
}
export async function streamLiveRender(streamInfo, res) {
export async function streamLiveRender(streamInfo, res, lang) {
try {
if (streamInfo.urls.length == 2) {
let headers = {};
@ -69,7 +69,7 @@ export async function streamLiveRender(streamInfo, res) {
ffmpegProcess.kill();
});
} else {
res.status(400).json({ status: "error", text: loc(streamInfo.lang, 'ErrorCorruptedStream') });
res.status(400).json({ status: "error", text: loc(lang, 'ErrorCorruptedStream') });
}
} catch (e) {
internalError(res);
@ -82,15 +82,15 @@ export async function streamAudioOnly(streamInfo, res) {
headers = { "user-agent": genericUserAgent };
}
const audio = got.get(streamInfo.urls, { isStream: true, headers: headers });
let format = streamInfo.filename.split('.')[streamInfo.filename.split('.').length - 1], args = [
let args = [
'-loglevel', '-8',
'-i', 'pipe:3',
'-vn',
];
args = args.concat(ffmpegArgs[format])
if (streamInfo.time) args.push('-t', msToTime(streamInfo.time));
args.push('-f', format, 'pipe:4');
'-vn'
]
let arg = streamInfo.copy ? ffmpegArgs["copy"] : ffmpegArgs["audio"]
args = args.concat(arg)
if (ffmpegArgs[streamInfo.audioFormat]) args = args.concat(ffmpegArgs[streamInfo.audioFormat]);
args.push('-f', streamInfo.audioFormat == "m4a" ? "ipod" : streamInfo.audioFormat, 'pipe:4');
const ffmpegProcess = spawn(ffmpeg, args, {
windowsHide: true,
stdio: [
@ -104,7 +104,7 @@ export async function streamAudioOnly(streamInfo, res) {
audio.on('error', (err) => {
ffmpegProcess.kill();
});
res.setHeader('Content-Disposition', `attachment; filename="${streamInfo.filename}"`);
res.setHeader('Content-Disposition', `attachment; filename="${streamInfo.filename}.${streamInfo.audioFormat}"`);
ffmpegProcess.stdio[4].pipe(res);
audio.pipe(ffmpegProcess.stdio[3]).on('error', (err) => {
ffmpegProcess.kill();

View file

@ -0,0 +1,68 @@
import { supportedAudio } from "../config.js"
import { apiJSON } from "./utils.js"
export default function(r, host, ip, audioFormat, isAudioOnly) {
if (!r.error) {
if (!isAudioOnly) {
switch (host) {
case "twitter":
return apiJSON(1, { u: r.urls })
case "vk":
return apiJSON(2, {
type: "bridge", u: r.urls, service: host, ip: ip,
filename: r.filename, salt: process.env.streamSalt
})
case "bilibili":
return apiJSON(2, {
type: "render", u: r.urls, service: host, ip: ip,
filename: r.filename, salt: process.env.streamSalt,
time: r.time
})
case "youtube":
return apiJSON(2, {
type: r.type, u: r.urls, service: host, ip: ip,
filename: r.filename, salt: process.env.streamSalt,
time: r.time,
})
case "reddit":
return apiJSON(r.typeId, {
type: r.type, u: r.urls, service: host, ip: ip,
filename: r.filename, salt: process.env.streamSalt
})
case "tiktok":
return apiJSON(2, {
type: "bridge", u: r.urls, service: host, ip: ip,
filename: r.filename, salt: process.env.streamSalt
})
case "douyin":
return apiJSON(2, {
type: "bridge", u: r.urls, service: host, ip: ip,
filename: r.filename, salt: process.env.streamSalt
})
case "tumblr":
return apiJSON(1, { u: r.urls })
}
} else {
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 (host == "reddit" && r.typeId == 1 || host == "vk") return apiJSON(0, { t: r.audioFilename });
return apiJSON(2, {
type: type,
u: Array.isArray(r.urls) ? r.urls[1] : r.urls, service: host, ip: ip,
filename: r.audioFilename, salt: process.env.streamSalt, isAudioOnly: true, audioFormat: audioFormat, copy: copy
})
}
} else {
return apiJSON(0, { t: r.error });
}
}