4.8: new about popup and other visual improvements

This commit is contained in:
wukko 2023-01-30 00:17:33 +06:00
parent b557ffb0cf
commit 6b87c7babd
19 changed files with 295 additions and 160 deletions

View file

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

View file

@ -6,7 +6,17 @@
"authorInfo": { "authorInfo": {
"name": "wukko", "name": "wukko",
"link": "https://wukko.me/", "link": "https://wukko.me/",
"contact": "https://wukko.me/contacts" "contact": "https://wukko.me/contacts",
"support": {
"twitter": {
"url": "https://twitter.com/justusecobalt",
"handle": "@justusecobalt"
},
"mastodon": {
"url": "https://wetdry.world/@cobalt",
"handle": "@cobalt@wetdry.world"
}
}
}, },
"internetExplorerRedirect": { "internetExplorerRedirect": {
"newNT": ["6.1", "6.2", "6.3", "10.0"], "newNT": ["6.1", "6.2", "6.3", "10.0"],

View file

@ -5,6 +5,8 @@
--border-10: 0.1rem solid var(--accent); --border-10: 0.1rem solid var(--accent);
--font-mono: 'Noto Sans Mono', 'Consolas', 'SF Mono', monospace; --font-mono: 'Noto Sans Mono', 'Consolas', 'SF Mono', monospace;
--red: rgb(255, 0, 61); --red: rgb(255, 0, 61);
--padding-1: 0.75rem;
--line-height: 1.65rem;
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
:root { :root {
@ -75,10 +77,11 @@ a {
[type="checkbox"] { [type="checkbox"] {
-webkit-appearance: none; -webkit-appearance: none;
appearance: none; appearance: none;
margin-right: 1rem; margin-right: var(--padding-1);
z-index: 0; z-index: 0;
border: 0; border: 0;
height: 15px; height: 15px;
width: 15px;
} }
[type="checkbox"]::before { [type="checkbox"]::before {
content: ""; content: "";
@ -96,6 +99,7 @@ a {
} }
.checkbox span { .checkbox span {
margin-top: 0.21rem; margin-top: 0.21rem;
margin-left: 0.4rem;
} }
button { button {
background: none; background: none;
@ -112,17 +116,11 @@ input[type="text"],
button:hover, button:hover,
.switch:hover, .switch:hover,
.checkbox:hover, .checkbox:hover,
.text-to-copy:hover { .text-to-copy:hover,
.collapse-header:hover {
background: var(--accent-hover); background: var(--accent-hover);
cursor: pointer; cursor: pointer;
} }
.switch.text-backdrop:hover,
.switch.text-backdrop:active,
.text-to-copy.text-backdrop:hover,
.text-to-copy.text-backdrop:active {
background: var(--accent);
color: var(--background);
}
button:active, button:active,
.switch:active, .switch:active,
.checkbox:active, .checkbox:active,
@ -131,6 +129,17 @@ button:active,
cursor: pointer; cursor: pointer;
transform: scale(0.95) transform: scale(0.95)
} }
.collapse-header:active {
background: var(--accent-press);
cursor: pointer;
}
.switch.text-backdrop:hover,
.switch.text-backdrop:active,
.text-to-copy.text-backdrop:hover,
.text-to-copy.text-backdrop:active {
background: var(--accent);
color: var(--background);
}
.picker-image:active { .picker-image:active {
cursor: pointer; cursor: pointer;
transform: scale(0.95) transform: scale(0.95)
@ -242,7 +251,7 @@ input[type="checkbox"] {
#cobalt-main-box #bottom, #cobalt-main-box #bottom,
#footer-buttons, #footer-buttons,
#footer-buttons, .footer-pair { #footer-buttons, .footer-pair {
gap: 0.8rem; gap: 0.6rem;
} }
#footer-buttons, .footer-pair { #footer-buttons, .footer-pair {
display: flex; display: flex;
@ -265,7 +274,17 @@ input[type="checkbox"] {
.text-backdrop { .text-backdrop {
background: var(--accent); background: var(--accent);
color: var(--background); color: var(--background);
padding: 0 0.1rem; }
.italic {
font-style: italic;
}
.social-link {
display: flex;
flex-direction: row;
justify-content: flex-start;
gap: 0.3rem;
margin-top: 0.5rem;
user-select: none;
} }
::-moz-selection { ::-moz-selection {
background-color: var(--accent); background-color: var(--accent);
@ -312,7 +331,7 @@ input[type="checkbox"] {
width: 100%; width: 100%;
background-color: var(--accent-button-bg); background-color: var(--accent-button-bg);
max-height: 300px; max-height: 300px;
margin-bottom: 2rem; margin-bottom: 1.65rem;
float: left; float: left;
} }
.changelog-img { .changelog-img {
@ -332,7 +351,7 @@ input[type="checkbox"] {
} }
#popup-subtitle { #popup-subtitle {
font-size: 1.1rem; font-size: 1.1rem;
padding-bottom: 1rem; padding-bottom: var(--padding-1);
} }
#popup-desc, #popup-desc,
#desc-error, #desc-error,
@ -340,7 +359,7 @@ input[type="checkbox"] {
width: 100%; width: 100%;
text-align: left; text-align: left;
float: left; float: left;
line-height: 1.7rem; line-height: var(--line-height);
user-select: text; user-select: text;
} }
#popup-title { #popup-title {
@ -359,7 +378,7 @@ input[type="checkbox"] {
} }
.popup-footer-content { .popup-footer-content {
font-size: 0.8rem; font-size: 0.8rem;
line-height: 1.7rem; line-height: var(--line-height);
color: var(--accent-unhover-2); color: var(--accent-unhover-2);
border-top: 0.05rem solid var(--accent-unhover-2); border-top: 0.05rem solid var(--accent-unhover-2);
padding-top: 0.4rem; padding-top: 0.4rem;
@ -386,14 +405,8 @@ input[type="checkbox"] {
#popup-content.with-footer { #popup-content.with-footer {
margin-bottom: 3rem; margin-bottom: 3rem;
} }
#popup-close {
cursor: pointer;
float: right;
right: 0;
position: absolute;
}
.settings-category { .settings-category {
padding-bottom: 1.2rem; padding-bottom: 1rem;
} }
.separator { .separator {
float: left; float: left;
@ -408,13 +421,17 @@ input[type="checkbox"] {
} }
.category-title { .category-title {
text-align: left; text-align: left;
line-height: 1.7rem; line-height: var(--line-height);
} }
.bottom-margin { .bottom-margin {
margin-bottom: 1rem!important; margin-bottom: var(--padding-1)!important;
} }
.top-margin { .top-margin {
margin-top: 1rem!important; margin-top: var(--padding-1)!important;
}
.top-margin-only {
margin-top: var(--padding-1)!important;
margin-bottom: 0!important;
} }
.no-margin { .no-margin {
margin: 0!important; margin: 0!important;
@ -424,10 +441,10 @@ input[type="checkbox"] {
align-items: center; align-items: center;
flex-direction: row; flex-direction: row;
flex-wrap: nowrap; flex-wrap: nowrap;
align-content: center;
padding: 0.55rem 1rem 0.8rem 0.7rem; padding: 0.55rem 1rem 0.8rem 0.7rem;
width: auto; width: auto;
margin: 0 0.5rem 0.5rem 0; margin-right: var(--padding-1);
margin-bottom: var(--padding-1);
background: var(--accent-button-bg); background: var(--accent-button-bg);
} }
.checkbox-label { .checkbox-label {
@ -439,7 +456,7 @@ input[type="checkbox"] {
.subtitle { .subtitle {
width: 100%; width: 100%;
text-align: left; text-align: left;
line-height: 1.7rem; line-height: var(--line-height);
padding-bottom: 0.4rem; padding-bottom: 0.4rem;
color: var(--accent); color: var(--accent);
} }
@ -469,7 +486,7 @@ input[type="checkbox"] {
cursor: pointer; cursor: pointer;
} }
.switch.space-right { .switch.space-right {
margin-right: 1rem margin-right: var(--padding-1);
} }
.switch[data-enabled="true"] { .switch[data-enabled="true"] {
color: var(--background); color: var(--background);
@ -494,17 +511,23 @@ input[type="checkbox"] {
.text-to-copy { .text-to-copy {
user-select: text; user-select: text;
border: var(--border-15); border: var(--border-15);
padding: 1rem; padding: var(--padding-1);
overflow: auto; overflow: auto;
} }
#close-bottom { #close-button {
max-width: 2.8rem; max-width: 2.8rem;
margin-left: 1rem; margin-left: var(--padding-1);
background: var(--background); background: var(--background);
border: var(--border-15); border: var(--border-15);
color: var(--accent); color: var(--accent);
padding: 0.3rem 0.75rem 0.5rem; padding: 0.3rem 0.75rem 0.5rem;
} }
#close-button.up {
float: right;
position: absolute;
right: 0;
height: 2.8rem;
}
.popup-tab-content { .popup-tab-content {
display: none; display: none;
} }
@ -519,18 +542,7 @@ input[type="checkbox"] {
} }
.emoji { .emoji {
margin-right: 0.4rem; margin-right: 0.4rem;
} user-select: none;
.tooltip {
position: absolute;
margin-top: -6rem;
margin-left: -0.5rem;
line-height: 1.2;
text-align: left;
pointer-events: none;
color: var(--accent-unhover-2)!important;
}
.button:active .tooltip {
display: none;
} }
.picker-image { .picker-image {
object-fit: cover; object-fit: cover;
@ -540,13 +552,13 @@ input[type="checkbox"] {
.picker-image-container { .picker-image-container {
width: 8rem; width: 8rem;
height: 8rem; height: 8rem;
margin-bottom: 1rem; margin-bottom: var(--padding-1);
background-color: var(--accent-button-bg); background-color: var(--accent-button-bg);
} }
.picker-various-container { .picker-various-container {
height: 20rem; height: 20rem;
width: 25rem; width: 25rem;
margin-bottom: 1rem; margin-bottom: var(--padding-1);
background-color: var(--accent-button-bg); background-color: var(--accent-button-bg);
position: relative; position: relative;
} }
@ -579,7 +591,41 @@ input[type="checkbox"] {
} }
#popup-picker .explanation { #popup-picker .explanation {
margin-top: 0!important; margin-top: 0!important;
margin-bottom: 1rem; margin-bottom: var(--padding-1);
}
.collapse-list {
background: var(--accent-press);
user-select: none;
}
.collapse-header {
padding: var(--padding-1);
font-size: 1rem;
display: flex;
flex-direction: row;
align-items: center;
cursor: pointer;
background: var(--accent-button-bg);
}
.collapse-indicator {
transform: rotate(180deg);
}
.expanded .collapse-indicator {
transform: none;
}
.collapse-title {
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
gap: 0.8rem;
}
.collapse-body {
display: none;
padding: var(--padding-1);
user-select: text;
}
.expanded .collapse-body {
display: block
} }
/* adapt the page according to screen size */ /* adapt the page according to screen size */
@media screen and (min-width: 2300px) { @media screen and (min-width: 2300px) {
@ -655,9 +701,9 @@ input[type="checkbox"] {
} }
} }
/* mobile page */ /* mobile page */
@media screen and (max-width: 949px) { @media screen and (max-width: 720px) {
#cobalt-main-box, #footer { #cobalt-main-box, #footer {
width: 85%; width: 90%;
} }
} }
@media screen and (max-width: 475px) { @media screen and (max-width: 475px) {
@ -699,6 +745,39 @@ input[type="checkbox"] {
margin-right: 0; margin-right: 0;
} }
} }
@media screen and (max-width: 720px) {
#cobalt-main-box #bottom {
flex-direction: column;
}
#cobalt-main-box #bottom button {
width: 100%!important;
}
#footer {
bottom: 4%;
transform: translate(-50%, 0%);
}
#footer-buttons {
flex-direction: column;
align-items: stretch;
}
.footer-pair .footer-button {
width: 100%!important;
}
#logo-area {
padding-right: 0;
padding-top: 0;
position: fixed;
line-height: 0;
margin-top: -2rem;
width: 100%;
text-align: center;
}
#cobalt-main-box {
display: flex;
border: none;
padding: 0;
}
}
@media screen and (max-width: 949px) { @media screen and (max-width: 949px) {
#picker-holder::-webkit-scrollbar { #picker-holder::-webkit-scrollbar {
display: none; display: none;
@ -715,23 +794,6 @@ input[type="checkbox"] {
height: 20rem; height: 20rem;
max-width: 100%; max-width: 100%;
} }
#cobalt-main-box #bottom {
flex-direction: column;
}
#cobalt-main-box #bottom button {
width: 100%!important;
}
#footer {
bottom: 1.7rem;
transform: translate(-50%, 0%);
}
#footer-buttons {
flex-direction: column;
align-items: stretch;
}
.footer-pair .footer-button {
width: 100%!important;
}
#popup-header { #popup-header {
padding-top: 1.2rem; padding-top: 1.2rem;
} }
@ -744,24 +806,10 @@ input[type="checkbox"] {
line-height: 7rem; line-height: 7rem;
} }
#close-error { #close-error {
bottom: 5%; bottom: 3rem;
position: absolute; position: absolute;
width: var(--without-padding); width: var(--without-padding);
} }
#logo-area {
padding-right: 0;
padding-top: 0;
position: fixed;
line-height: 0;
margin-top: -2rem;
width: 100%;
text-align: center;
}
#cobalt-main-box {
display: flex;
border: none;
padding: 0;
}
.popup, .popup.scrollable, .popup.small { .popup, .popup.scrollable, .popup.small {
border: none; border: none;
width: 90%; width: 90%;

View file

@ -1,7 +1,7 @@
let ua = navigator.userAgent.toLowerCase(); let ua = navigator.userAgent.toLowerCase();
let isIOS = ua.match("iphone os"); let isIOS = ua.match("iphone os");
let isMobile = ua.match("android") || ua.match("iphone os"); let isMobile = ua.match("android") || ua.match("iphone os");
let version = 21; let version = 22;
let regex = new RegExp(/https:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/); let regex = new RegExp(/https:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/);
let notification = `<div class="notification-dot"></div>` let notification = `<div class="notification-dot"></div>`
@ -85,8 +85,8 @@ function clearInput() {
function copy(id, data) { function copy(id, data) {
let e = document.getElementById(id); let e = document.getElementById(id);
e.classList.add("text-backdrop"); e.classList.add("text-backdrop");
data ? navigator.clipboard.writeText(data) : navigator.clipboard.writeText(e.innerText);
setTimeout(() => { e.classList.remove("text-backdrop") }, 600); setTimeout(() => { e.classList.remove("text-backdrop") }, 600);
data ? navigator.clipboard.writeText(data) : navigator.clipboard.writeText(e.innerText);
} }
function detectColorScheme() { function detectColorScheme() {
let theme = "auto"; let theme = "auto";
@ -112,6 +112,11 @@ function changeTab(evnt, tabId, tabClass) {
if (tabId === "tab-about-changelog" && sGet("changelogStatus") !== `${version}`) notificationCheck("changelog"); if (tabId === "tab-about-changelog" && sGet("changelogStatus") !== `${version}`) notificationCheck("changelog");
if (tabId === "tab-about-about" && !sGet("seenAbout")) notificationCheck("about"); if (tabId === "tab-about-about" && !sGet("seenAbout")) notificationCheck("about");
} }
function expandCollapsible(evnt) {
let classlist = evnt.currentTarget.parentNode.classList;
let c = "expanded";
!classlist.contains(c) ? classlist.add(c) : classlist.remove(c);
}
function notificationCheck(type) { function notificationCheck(type) {
let changed = true; let changed = true;
switch (type) { switch (type) {
@ -123,7 +128,6 @@ function notificationCheck(type) {
break; break;
default: default:
changed = false; changed = false;
break;
} }
if (changed && sGet("changelogStatus") === `${version}` || type === "disable") { if (changed && sGet("changelogStatus") === `${version}` || type === "disable") {
setTimeout(() => { setTimeout(() => {

6
src/front/emoji/bird.svg Normal file
View file

@ -0,0 +1,6 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.38815 7.21997L3.31815 8.82997C2.95815 9.01997 2.88815 9.50997 3.18815 9.79997L5.79815 12.27L6.38815 7.21997Z" fill="#F9C23C"/>
<path d="M18.5582 28.5H16.7782L17.9782 22.5H16.4782L15.2782 28.5H11.7781L12.9781 22.5H11.4781L10.2781 28.5H8.47812C7.74812 28.5 7.14812 29.02 7.00812 29.71C6.96812 29.86 7.09812 30 7.24812 30H19.7782C19.9382 30 20.0582 29.86 20.0282 29.71C19.8882 29.02 19.2782 28.5 18.5582 28.5Z" fill="#F9C23C"/>
<path d="M17.5681 6.22C17.4381 5.8 17.2681 5.4 17.0481 5.03H17.6681C18.9581 5.03 19.9981 3.99 19.9981 2.7C19.9981 2.32 19.6781 2 19.2981 2H11.8381C8.65813 2 6.05813 4.48 5.84813 7.61L4.55813 18.79C4.17813 22.1 6.75813 25 10.0881 25H23.8381L23.8348 24.99H29.1181C29.5181 24.99 29.8381 24.67 29.8381 24.27V15.12C29.8381 14.6 29.2881 14.25 28.8081 14.47L21.4662 17.8893L19.1682 11H19.164L17.5681 6.22Z" fill="#00A6ED"/>
<path d="M10 10C10.5523 10 11 9.55228 11 9C11 8.44772 10.5523 8 10 8C9.44772 8 9 8.44772 9 9C9 9.55228 9.44772 10 10 10Z" fill="#212121"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,7 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 27C22.6274 27 28 21.6274 28 15C28 8.37258 22.6274 3 16 3C9.37257 3 4 8.37258 4 15C4 21.6274 9.37257 27 16 27Z" fill="#533566"/>
<path d="M24 24H8L7.07853 28.1805C6.7458 29.0769 7.51208 30 8.59093 30H23.4125C24.4913 30 25.2475 29.0769 24.9249 28.1805L24 24Z" fill="#B4ACBC"/>
<path d="M14.205 6.26449C14.085 6.21411 13.995 6.11335 13.945 6.00252L13.565 5.10579C13.495 4.96474 13.295 4.96474 13.225 5.10579L12.845 6.00252C12.795 6.12343 12.705 6.21411 12.585 6.26449L12.105 6.48615C11.965 6.55668 11.965 6.75819 12.105 6.82871L12.585 7.05038C12.705 7.10076 12.795 7.20151 12.845 7.31235L13.225 8.20907C13.295 8.35013 13.495 8.35013 13.565 8.20907L13.945 7.31235C13.995 7.19144 14.085 7.10076 14.205 7.05038L14.685 6.82871C14.825 6.75819 14.825 6.55668 14.685 6.48615L14.205 6.26449Z" fill="#FCD53F"/>
<path d="M24.12 10.8035C23.96 10.733 23.83 10.5919 23.76 10.4307L23.22 9.15113C23.12 8.94962 22.83 8.94962 22.73 9.15113L22.19 10.4307C22.12 10.5919 21.99 10.733 21.83 10.8035L21.15 11.1159C20.95 11.2166 20.95 11.5088 21.15 11.6096L21.83 11.9219C21.99 11.9924 22.12 12.1335 22.19 12.2947L22.73 13.5743C22.83 13.7758 23.12 13.7758 23.22 13.5743L23.76 12.2947C23.83 12.1335 23.96 11.9924 24.12 11.9219L24.8 11.6096C25 11.5088 25 11.2166 24.8 11.1159L24.12 10.8035Z" fill="#FCD53F"/>
<path d="M12.5861 14.0303C12.7249 14.3822 12.9838 14.6657 13.3168 14.8221L14.6948 15.477C15.1017 15.6921 15.1017 16.3079 14.6948 16.523L13.3168 17.1779C12.9931 17.3343 12.7249 17.6178 12.5861 17.9697L11.4948 20.6774C11.2913 21.1075 10.7087 21.1075 10.5052 20.6774L9.41387 17.9697C9.27515 17.6178 9.01618 17.3343 8.68323 17.1779L7.3052 16.523C6.89827 16.3079 6.89827 15.6921 7.3052 15.477L8.68323 14.8221C9.00693 14.6657 9.27515 14.3822 9.41387 14.0303L10.5052 11.3226C10.7087 10.8925 11.2913 10.8925 11.4948 11.3226L12.5861 14.0303Z" fill="#FCD53F"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1,8 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 24.563C14 23.3163 13.02 22.3052 11.75 22.3052L19 16.7V27.7926C19 28.4601 18.43 29 17.75 29H15.25C14.57 29 14 28.4601 14 27.7926V24.563Z" fill="#636363"/>
<path d="M22.51 22.25L22 22H29V28.06C29 28.58 28.58 29 28.06 29H24.94C24.42 29 24 28.58 24 28.06V24.67C24 23.64 23.43 22.71 22.51 22.25Z" fill="#636363"/>
<path d="M19.35 6C25.23 6 30 10.723 29.99 16.5673V27.7796C29.99 28.4543 29.44 29 28.76 29H26.2339C25.5539 29 25.0039 28.4543 25.0039 27.7796V24.5151C25.0039 23.255 23.9739 22 22.7039 22H17.5C16.49 22 15.79 22.9573 15.32 23.7908C15.2356 23.9407 15.1457 24.1025 15.0509 24.273C14.0231 26.1221 12.4234 29 11.05 29H8.31C8.28361 29 8.26 28.9972 8.23771 28.9946C8.21777 28.9923 8.19889 28.9901 8.18 28.9901C7.35 28.9107 6.96 27.9284 7.45 27.2636L9.16 24.9318C9.88 23.9594 10.07 22.6795 9.59 21.5781C8.84797 19.8763 7.16017 19.1422 5 18.8549V22.5C5 22.7783 5.07227 22.8945 5.08948 22.9152C5.09336 22.9199 5.1032 22.9318 5.13954 22.9472C5.18248 22.9654 5.29076 23 5.5 23C6.32843 23 7 23.6716 7 24.5C7 25.3285 6.32843 26 5.5 26C4.38888 26 3.43171 25.6126 2.7841 24.8349C2.17679 24.1055 2 23.2217 2 22.5V10.9909C2 8.23253 4.25 6 7.03 6H19.35Z" fill="#9B9B9B"/>
<path d="M5.5 12.5C5.77614 12.5 6 12.7239 6 13V14C6 14.2761 5.77614 14.5 5.5 14.5C5.22386 14.5 5 14.2761 5 14V13C5 12.7239 5.22386 12.5 5.5 12.5Z" fill="#1C1C1C"/>
<path d="M10 6C14.4741 6 18.2026 9.18153 18.9773 13.3758C19.1323 14.2261 18.4737 15 17.6022 15H11.3945C10.6295 15 10 14.3885 10 13.6242V6Z" fill="#D3D3D3"/>
<path d="M3.46002 19.7C3.46002 20.2 3.86002 20.6 4.36002 20.6C6.36002 20.6 7.98002 19 7.99002 17H6.19002C6.19002 18.01 5.37002 18.8 4.36002 18.8C3.86002 18.8 3.46002 19.2 3.46002 19.7Z" fill="#D3D3D3"/>
</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="M7.31133 17.6539C7.42658 18.0654 7.50535 18.3466 6.71874 18.25C6.27534 18.1955 5.881 18.1308 5.56463 18.0788C5.24553 18.0264 5.00576 17.987 4.87499 17.9844C4.70832 17.9896 4.46249 18.2844 4.74999 18.9844C5.03749 19.6844 5.38541 20.3958 5.57811 20.7031L9.17187 19.8125L7.56249 17.3594L7.26561 17.4844C7.28021 17.5428 7.29609 17.5995 7.31133 17.6539Z" fill="#580A7C"/>
<path d="M24.7125 17.6539C24.5973 18.0654 24.5185 18.3466 25.3051 18.25C25.7485 18.1955 26.1428 18.1308 26.4592 18.0788C26.7783 18.0264 27.0181 17.987 27.1488 17.9844C27.3155 17.9896 27.5613 18.2844 27.2738 18.9844C26.9863 19.6844 26.6384 20.3958 26.4457 20.7031L22.852 19.8125L24.4613 17.3594L24.7582 17.4844C24.7436 17.5428 24.7277 17.5995 24.7125 17.6539Z" fill="#580A7C"/>
<path d="M8.17722 18.841C8.33838 19.1786 8.54222 19.6056 7.375 19.875L2.76562 20.9219C2.00783 21.1249 2.11991 21.4306 2.24244 21.765C2.2614 21.8167 2.2806 21.8691 2.29687 21.9219C2.74007 23.3599 6 25.8125 8.15625 25.5L12.3125 21.7812L8.42188 18.5938L8.15625 18.7969C8.16307 18.8114 8.17011 18.8261 8.17722 18.841Z" fill="#7D0AB3"/>
<path d="M23.977 18.841C23.8159 19.1786 23.612 19.6056 24.7792 19.875L29.3886 20.9219C30.1464 21.1249 30.0343 21.4306 29.9118 21.765C29.8928 21.8167 29.8736 21.8691 29.8574 21.9219C29.4142 23.3599 26.1542 25.8125 23.998 25.5L19.8417 21.7812L23.7324 18.5938L23.998 18.7969C23.9912 18.8114 23.9841 18.8261 23.977 18.841Z" fill="#7D0AB3"/>
<path d="M20.9627 20.7275L26.988 22.1259C27.1456 22.1176 27.4162 22.2503 27.238 22.8476C27.0152 23.5942 26.4704 25.1452 22.9577 25.1347C20.1475 25.1264 19.7181 22.5055 19.8547 21.196L20.9627 20.7275Z" fill="#9B2ECE"/>
<path d="M11.25 21.2812L5.12499 22.0937C4.96874 22.0728 4.68749 22.1749 4.81249 22.7499C4.96874 23.4687 5.37499 24.9687 8.87499 25.2499C11.675 25.4749 12.3333 23.052 12.3125 21.8124L11.25 21.2812Z" fill="#9B2ECE"/>
<path d="M10.9531 21.3594C11.1198 21.5521 11.3 22.0656 10.6875 22.5781C10.075 23.0906 6.94271 25.8542 5.45313 27.1719C5.16476 27.427 5.28134 27.5969 5.49493 27.9083C5.5117 27.9328 5.52906 27.9581 5.54688 27.9844C6.54939 29.4643 11.6719 31.9219 15.6875 26.7656C15.8022 26.6183 15.8926 26.5183 16 26.5163V26.5281C16.0118 26.524 16.0233 26.521 16.0345 26.519C16.0458 26.521 16.0573 26.524 16.0691 26.5281V26.5163C16.1765 26.5183 16.2669 26.6183 16.3816 26.7656C20.3972 31.9219 25.5197 29.4643 26.5222 27.9844C26.54 27.9581 26.5574 27.9328 26.5741 27.9083C26.7877 27.5969 26.9043 27.427 26.6159 27.1719C25.1264 25.8542 21.9941 23.0906 21.3816 22.5781C20.7691 22.0656 20.9493 21.5521 21.1159 21.3594L20.0144 20.9766L16.0345 22.2623L12.0547 20.9766L10.9531 21.3594Z" fill="#7D0AB3"/>
<path d="M6.375 12C6.375 7.02944 10.4044 3 15.375 3H17C21.9706 3 26 7.02944 26 12V13.625C26 18.5956 21.9706 22.625 17 22.625H15.375C10.4044 22.625 6.375 18.5956 6.375 13.625V12Z" fill="#952CC6"/>
<path d="M8.71875 15.25C9.66799 15.25 10.4375 14.4805 10.4375 13.5312C10.4375 12.582 9.66799 11.8125 8.71875 11.8125C7.76951 11.8125 7 12.582 7 13.5312C7 14.4805 7.76951 15.25 8.71875 15.25Z" fill="#1C1C1C"/>
<path d="M23.7187 15.25C24.6679 15.25 25.4374 14.4805 25.4374 13.5312C25.4374 12.582 24.6679 11.8125 23.7187 11.8125C22.7695 11.8125 22 12.582 22 13.5312C22 14.4805 22.7695 15.25 23.7187 15.25Z" fill="#1C1C1C"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -1,4 +1,4 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> <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="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="#6B438B"/>
<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"/> <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="#6B438B"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 992 B

After

Width:  |  Height:  |  Size: 992 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

View file

@ -1,12 +1,11 @@
{ {
"name": "english", "name": "english",
"substrings": { "substrings": {
"ContactLink": "<a class=\"text-backdrop\" href=\"{repo}\" target=\"_blank\">file an issue on github</a>" "ContactLink": "<a class=\"text-backdrop italic\" href=\"{repo}\" target=\"_blank\">file an issue on github</a>"
}, },
"strings": { "strings": {
"LinkInput": "paste the link here", "LinkInput": "paste the link here",
"AboutSummary": "{appName} is your go-to place for social media downloads. zero ads, trackers, or any other creepy bullshit. simply paste a share link and you're ready to rock!", "AboutSummary": "{appName} is your go-to place for social media downloads. zero ads, trackers, or any other creepy bullshit. simply paste a share link and you're ready to rock!",
"AboutSupportedServices": "currently supported services:",
"EmbedBriefDescription": "save content from social media without annoyances", "EmbedBriefDescription": "save content from social media without annoyances",
"MadeWithLove": "made with <3 by wukko", "MadeWithLove": "made with <3 by wukko",
"AccessibilityInputArea": "link input area", "AccessibilityInputArea": "link input area",
@ -53,7 +52,6 @@
"AccessibilityEnableDownloadPopup": "ask what to do with downloads", "AccessibilityEnableDownloadPopup": "ask what to do with downloads",
"SettingsFormatDescription": "select webm if you want max quality available. webm videos are usually higher bitrate, but ios devices can't play them natively.", "SettingsFormatDescription": "select webm if you want max quality available. webm videos are usually higher bitrate, but ios devices can't play them natively.",
"SettingsQualityDescription": "if selected quality isn't available, closest one gets picked instead.\nif you want to post a youtube video on social media, select a combination of mp4 and 720p.", "SettingsQualityDescription": "if selected quality isn't available, closest one gets picked instead.\nif you want to post a youtube video on social media, select a combination of mp4 and 720p.",
"LinkGitHubIssues": "&gt;&gt; report issues and check out the source code on github",
"LinkGitHubChanges": "&gt;&gt; see previous commits and contribute on github", "LinkGitHubChanges": "&gt;&gt; see previous commits and contribute on github",
"NoScriptMessage": "{appName} uses javascript for api requests and interactive interface. you have to allow javascript to use this site. i don't have any ads or trackers, pinky promise.", "NoScriptMessage": "{appName} uses javascript for api requests and interactive interface. you have to allow javascript to use this site. i don't have any ads or trackers, pinky promise.",
"DownloadPopupDescriptionIOS": "press and hold the download button, hide the video preview, and then select \"download linked file\" to save.", "DownloadPopupDescriptionIOS": "press and hold the download button, hide the video preview, and then select \"download linked file\" to save.",
@ -88,7 +86,6 @@
"ErrorNoUrlReturned": "server didn't return a download link. this should never happen. reload the page and try again, but if it doesn't help, {ContactLink}.", "ErrorNoUrlReturned": "server didn't return a download link. this should never happen. reload the page and try again, but if it doesn't help, {ContactLink}.",
"ErrorUnknownStatus": "i received a response i can't process. most likely something with status is wrong. this should never happen. reload the page and try again, but if it doesn't help, {ContactLink}.", "ErrorUnknownStatus": "i received a response i can't process. most likely something with status is wrong. this should never happen. reload the page and try again, but if it doesn't help, {ContactLink}.",
"PasteFromClipboard": "paste from clipboard", "PasteFromClipboard": "paste from clipboard",
"FollowTwitter": "follow {appName}'s twitter account for polls, updates, and more: <a class=\"text-backdrop\" href=\"https://twitter.com/justusecobalt\" target=\"_blank\">@justusecobalt</a>",
"ChangelogOlder": "previous versions", "ChangelogOlder": "previous versions",
"ChangelogPressToExpand": "press to expand", "ChangelogPressToExpand": "press to expand",
"Miscellaneous": "miscellaneous", "Miscellaneous": "miscellaneous",
@ -104,12 +101,20 @@
"ChangelogPressToHide": "press to collapse", "ChangelogPressToHide": "press to collapse",
"Donate": "donate", "Donate": "donate",
"DonateSub": "help me keep it up", "DonateSub": "help me keep it up",
"DonateExplanation": "{appName} does not (and will never) serve ads or sell your data, therefore it's <span class=\"text-backdrop\">completely free to use</span>. but hey! turns out keeping up a web service used by hundreds of thousands of people is somewhat costly.\n\nif you ever found {appName} useful and want to keep it online, or simply want to thank the developer, consider chipping in! each and every cent helps and is VERY appreciated.", "DonateExplanation": "{appName} does not (and will never) serve ads or sell your data, therefore it's <span class=\"text-backdrop\">completely free to use</span>. but turns out keeping up a web service used by thousands of people is somewhat costly.\n\nif you ever found {appName} useful and want to keep it online, or simply want to thank the developer, consider chipping in! each and every cent helps and is VERY appreciated.",
"DonateVia": "donate via", "DonateVia": "donate via",
"DonateHireMe": "or, as an alternative, you can <a class=\"text-backdrop\" href=\"{s}\" target=\"_blank\">hire me</a>.", "DonateHireMe": "or, as an alternative, you can <a class=\"text-backdrop italic\" href=\"{s}\" target=\"_blank\">hire me</a>.",
"SettingsVideoMute": "mute audio", "SettingsVideoMute": "mute audio",
"SettingsVideoMuteExplanation": "disables audio in downloaded video when possible. ignored when audio mode is on or service only supports audio.", "SettingsVideoMuteExplanation": "disables audio in downloaded video when possible. ignored when audio mode is on or service only supports audio.",
"SettingsVideoGeneral": "general", "SettingsVideoGeneral": "general",
"ErrorSoundCloudNoClientId": "couldn't find client_id that is required to fetch audio data from soundcloud. try again, and if issue persists, {ContactLink}." "ErrorSoundCloudNoClientId": "couldn't find client_id that is required to fetch audio data from soundcloud. try again, and if issue persists, {ContactLink}.",
"CollapseServices": "supported services",
"CollapseSupport": "support & source code",
"CollapsePrivacy": "privacy policy",
"ServicesNote": "this list is not final and keeps expanding over time, make sure to check it once in a while!",
"FollowSupport": "follow {appName} on mastodon or twitter for support, polls, news, and more:",
"SupportNote": "please note that questions and issues may take a while to respond to, there's only one person managing everything.",
"SourceCode": "report issues, explore source code, star or fork the repo:",
"PrivacyPolicy": "{appName}'s privacy policy is simple: no data about you is collected or stored. zero, zilch, nada, nothing.\nwhat you download is your business, not mine.\n\nsome non-backtraceable data does get temporarily stored when requested download requires live render. it's necessary for that feature to function.\n\nin that case, <span class=\"text-backdrop\">salted sha256 hash of your ip address</span> and information about requested stream are temporarily stored in server's RAM for <span class=\"text-backdrop\">2 minutes</span>. after 2 minutes all previously stored information is permanently removed. hash of your ip address is <span class=\"text-backdrop\">used for limiting stream access only to you</span>.\nno one (even me) has access to this data, because official cobalt codebase doesn't provide a way to read it outside of processing functions in the first place.\n\nyou can check {appName}'s <a class=\"text-backdrop italic\" href=\"{repo}\" target=\"_blank\">github repo</a> for yourself and see that indeed nothing is stored permanently."
} }
} }

View file

@ -1,12 +1,11 @@
{ {
"name": "русский", "name": "русский",
"substrings": { "substrings": {
"ContactLink": "<a class=\"text-backdrop\" href=\"{repo}\" target=\"_blank\">напиши об этом на github</a>" "ContactLink": "<a class=\"text-backdrop italic\" href=\"{repo}\" target=\"_blank\">напиши об этом на github</a>"
}, },
"strings": { "strings": {
"LinkInput": "вставь ссылку сюда", "LinkInput": "вставь ссылку сюда",
"AboutSummary": "{appName} — твой друг при скачивании контента из соц. сетей. никакой рекламы или трекеров. вставляешь ссылку и получаешь файл. ничего лишнего.", "AboutSummary": "{appName} — твой друг при скачивании контента из соц. сетей. никакой рекламы или трекеров. вставляешь ссылку и получаешь файл. ничего лишнего.",
"AboutSupportedServices": "что поддерживается:",
"EmbedBriefDescription": "сохраняй что хочешь, без мороки и вторжения в личное пространство", "EmbedBriefDescription": "сохраняй что хочешь, без мороки и вторжения в личное пространство",
"MadeWithLove": "сделано с <3 wukko", "MadeWithLove": "сделано с <3 wukko",
"AccessibilityInputArea": "зона вставки ссылки", "AccessibilityInputArea": "зона вставки ссылки",
@ -53,7 +52,6 @@
"AccessibilityEnableDownloadPopup": "спрашивать, что делать с загрузками", "AccessibilityEnableDownloadPopup": "спрашивать, что делать с загрузками",
"SettingsFormatDescription": "выбирай webm, если хочешь максимальное качество. у webm видео битрейт обычно выше, но устройства на ios не могут проигрывать их без сторонних приложений.", "SettingsFormatDescription": "выбирай webm, если хочешь максимальное качество. у webm видео битрейт обычно выше, но устройства на ios не могут проигрывать их без сторонних приложений.",
"SettingsQualityDescription": "если выбранное качество недоступно, то выбирается ближайшее к нему.\nесли ты хочешь опубликовать видео с youtube где-то в соц. сетях, то выбирай комбинацию из mp4 и 720p.", "SettingsQualityDescription": "если выбранное качество недоступно, то выбирается ближайшее к нему.\nесли ты хочешь опубликовать видео с youtube где-то в соц. сетях, то выбирай комбинацию из mp4 и 720p.",
"LinkGitHubIssues": "&gt;&gt; сообщай о проблемах и смотри исходный код на github",
"LinkGitHubChanges": "&gt;&gt; смотри предыдущие изменения на github", "LinkGitHubChanges": "&gt;&gt; смотри предыдущие изменения на github",
"NoScriptMessage": "{appName} использует javascript для обработки ссылок и интерактивного интерфейса. ты должен разрешить использование javascript, чтобы пользоваться сайтом. тут нет никаких трекеров или рекламы, обещаю.", "NoScriptMessage": "{appName} использует javascript для обработки ссылок и интерактивного интерфейса. ты должен разрешить использование javascript, чтобы пользоваться сайтом. тут нет никаких трекеров или рекламы, обещаю.",
"DownloadPopupDescriptionIOS": "зажми кнопку \"скачать\", затем скрой превью видео и выбери \"загрузить файл по ссылке\" в появившемся окне.", "DownloadPopupDescriptionIOS": "зажми кнопку \"скачать\", затем скрой превью видео и выбери \"загрузить файл по ссылке\" в появившемся окне.",
@ -88,7 +86,6 @@
"ErrorNoUrlReturned": "я не получил ссылку для скачивания от сервера. такого происходить не должно. перезагрузи страницу, а если не поможет, то {ContactLink}.", "ErrorNoUrlReturned": "я не получил ссылку для скачивания от сервера. такого происходить не должно. перезагрузи страницу, а если не поможет, то {ContactLink}.",
"ErrorUnknownStatus": "сервер ответил мне чем-то непонятным. такого происходить не должно. перезагрузи страницу, а если не поможет, то {ContactLink}.", "ErrorUnknownStatus": "сервер ответил мне чем-то непонятным. такого происходить не должно. перезагрузи страницу, а если не поможет, то {ContactLink}.",
"PasteFromClipboard": "вставить из буфера обмена", "PasteFromClipboard": "вставить из буфера обмена",
"FollowTwitter": "а ещё, в твиттере {appName} есть опросы, новости, и многое другое: <a class=\"text-backdrop\" href=\"https://twitter.com/justusecobalt\" target=\"_blank\">@justusecobalt</a>",
"ChangelogOlder": "предыдущие версии (на английском)", "ChangelogOlder": "предыдущие версии (на английском)",
"ChangelogPressToExpand": "нажми, чтобы раскрыть", "ChangelogPressToExpand": "нажми, чтобы раскрыть",
"Miscellaneous": "разное", "Miscellaneous": "разное",
@ -104,12 +101,20 @@
"ChangelogPressToHide": "нажми, чтобы скрыть", "ChangelogPressToHide": "нажми, чтобы скрыть",
"Donate": "донаты", "Donate": "донаты",
"DonateSub": "ты можешь помочь!", "DonateSub": "ты можешь помочь!",
"DonateExplanation": "{appName} не пихает рекламу тебе в лицо и не продаёт твои личные данные, а значит работает <span class=\"text-backdrop\">совершенно бесплатно</span>. но оказывается, что хостинг сервиса, которым пользуются сотни тысяч людей, обходится довольно дорого.\n\nесли ты хочешь, чтобы твой любимый загрузчик оставался онлайн, а разработчик не помер с голоду вместе с двумя котами, то подумай над тем, чтобы задонатить. каждый рубль поможет мне, моим котам, и {appName}!", "DonateExplanation": "{appName} не пихает рекламу тебе в лицо и не продаёт твои личные данные, а значит работает <span class=\"text-backdrop\">совершенно бесплатно</span>. но оказывается, что хостинг сервиса, которым пользуются тысячи людей, обходится довольно дорого.\n\nесли ты хочешь, чтобы твой любимый загрузчик оставался онлайн, а разработчик не помер с голоду вместе с двумя котами, то подумай над тем, чтобы задонатить. каждый рубль поможет мне, моим котам, и {appName}!",
"DonateVia": "открыть", "DonateVia": "открыть",
"DonateHireMe": "или же ты можешь <a class=\"text-backdrop\" href=\"{s}\" target=\"_blank\">пригласить меня на работу</a>.", "DonateHireMe": "или же ты можешь <a class=\"text-backdrop italic\" href=\"{s}\" target=\"_blank\">пригласить меня на работу</a>.",
"SettingsVideoMute": "отключить аудио", "SettingsVideoMute": "отключить аудио",
"SettingsVideoMuteExplanation": "убирает аудио при загрузке видео, когда это возможно. игнорируется если включен режим аудио или сервис поддерживает только аудио загрузки.", "SettingsVideoMuteExplanation": "убирает аудио при загрузке видео, когда это возможно. игнорируется если включен режим аудио или сервис поддерживает только аудио загрузки.",
"SettingsVideoGeneral": "основные", "SettingsVideoGeneral": "основные",
"ErrorSoundCloudNoClientId": "мне не удалось достать client_id, который необходим для получения аудио из soundcloud. попробуй ещё раз, но если так и не получится, {ContactLink}." "ErrorSoundCloudNoClientId": "мне не удалось достать client_id, который необходим для получения аудио из soundcloud. попробуй ещё раз, но если так и не получится, {ContactLink}.",
"CollapseServices": "что поддерживается?",
"CollapseSupport": "поддержка и исходный код",
"CollapsePrivacy": "политика конфиденциальности",
"ServicesNote": "этот список далеко не финальный и постоянно пополняется. заглядывай сюда почаще, тогда точно будешь знать, что поддерживается!",
"FollowSupport": "подписывайся на аккаунты {appName} на mastodon или twitter для новостей, поддержки, участия в опросах, и многого другого:",
"SupportNote": "помни, что ответ на твой вопрос может занять время, так как только один человек занимается и разработкой и поддержкой.",
"SourceCode": "пиши о проблемах, шарься в исходнике, или же форкай репозиторий:",
"PrivacyPolicy": "политика конфиденциальности {appName} довольно проста: ничего не хранится об истории твоих действий или загрузок. совсем. даже ошибки.\nто, что ты скачиваешь - не моё дело, а только твоё.\n\nв случаях, когда твоей загрузке требуется лайв-рендер, временно хранится неотслеживаемая информация. это необходимо для работы данной функции.\n\nв этом случае, <span class=\"text-backdrop\">sha256 хэш (с солью) твоего ip адреса</span> и данные о запрошенном стриме хранятся в оперативной памяти сервера в течение <span class=\"text-backdrop\">2-х минут</span>. по истечении этого времени всё стирается. хэш твоего ip адреса используется для предоставления доступа к стриму только тебе. ни у кого (даже у меня) нет доступа к временно хранящимся данным, так как код {appName} специально не позволяет читать такие данные снаружи.\n\nты можешь посмотреть <a class=\"text-backdrop italic\" href=\"{repo}\" target=\"_blank\">исходный код {appName}</a> и убедиться, что всё так, как описано."
} }
} }

View file

@ -15,7 +15,7 @@ export function loadLoc() {
} }
loadLoc(); loadLoc();
export function replaceBase(s) { export function replaceBase(s) {
return s.replace(/\n/g, '<br/>').replace(/{appName}/g, appName).replace(/{repo}/g, repo).replace(/{bS}/g, '<div class=\"bullpadding\">').replace(/{bE}/g, '</div>').replace(/\*;/g, "&bull;"); return s.replace(/\n/g, '<br/>').replace(/{appName}/g, appName).replace(/{repo}/g, repo).replace(/\*;/g, "&bull;");
} }
export function replaceAll(lang, str, string, replacement) { export function replaceAll(lang, str, string, replacement) {
let s = replaceBase(str[string]) let s = replaceBase(str[string])

View file

@ -1,20 +1,25 @@
{ {
"current": { "current": {
"version": "4.8",
"title": "prettier than ever",
"banner": "catmakeup.webp",
"content": "this version brings many visual improvements and a completely revamped \"about\" tab.\n\nwhat's new in \"about\" tab:\n*; all information is now split into collapsible sections, making it easier to navigate.\n*; added privacy policy to further prove that none of your data is collected.\n*; added emoji to the page title to make it look consistent with other pages.\n*; added mastodon account handle and link.\n*; there are now short notes at the end of each section.\n*; other changes that are too small to describe. just go check it out!\n\nthis tab is not yet finalized and i will post a poll with possible future additions on twitter and mastodon. make sure to vote and discuss!\n\nvisual improvements:\n*; less wasted space: paddings and margins have been reduced and optimized for usability, consistency, and overall beauty.\n*; all <a class=\"text-backdrop italic\" href=\"https://youtu.be/dQw4w9WgXcQ\" target=\"_blank\">links</a> are now in italic. it's much easier to tell them apart from <span class=\"text-backdrop\">regular highlights</span>.\n*; error popup no longer looks broken and out of place.\n*; download popup now has a proper close button, not something from 2.x era.\n*; emoji are no longer selectable or draggable.\n*; better scalability: desktop layout for home screen is shown if device viewport is wide enough to fit in three action buttons.\n*; page shouldn't look broken on phones in landscape mode (i still highly recommend using cobalt in portrait mode).\n*; removed bulletpoint padding. it was unnecessary.\n*; updated some service names.\n\nas always, you can suggest features or report bugs on any platform listed in the \"support\" section of about tab.\n\nthank you for using cobalt. i hope you have a good day :)"
},
"history": [{
"version": "4.7", "version": "4.7",
"title": "we're better together! thank you for bug reports.", "title": "we're better together! thank you for bug reports.",
"banner": "bettertogether.webp", "banner": "bettertogether.webp",
"content": "this update includes a bunch of improvements, many of which were made thanks to the community :D\n\nservice-related improvements:\n*; private soundcloud links are now supported (#68);\n*; tiktok usernames with dots in them no longer confuse cobalt (#71);\n*; .ogg files no longer wrongfully include a video channel (#67);\n*; fixed an issue that caused cobalt to freak out when user attempted to download an audio from audio-only service with \"mute video\" option enabled.\n\nui improvements:\n*; popup padding has been evened out. popups are now able to fit in more information on scroll, especially on mobile;\n*; all buttons are now of even size and are displayed without any padding issues across all modern browsers and devices;\n*; checkbox is no longer crippled on ios;\n*; many explanation texts have been simplified to get rid of unnecessary bloat (no bullshit, remember?);\n*; moved tiktok section in video settings higher due to higher priority;\n*; fixed unexpectedly displayed scrollbars on switch rows in firefox.\n\nstability improvements:\n*; ffmpeg process now should end upon finishing the render;\n*; ffmpeg should also quit when download is abruptly cut off;\n*; fixed a memory leak that was caused by misconfigured stream information caching (#63).\n\ninternal improvements:\n*; requested streams are now stored in cache for 2 minutes instead of 1000 hours (yes, 1000 hours, i fucked up);\n*; cached data is now reused if user requests same content within 2 minutes;\n*; page render module is now even cleaner than before;\n*; proper support for bullet-points in loc strings.\n\nyou can suggest features or report bugs on <a class=\"text-backdrop\" href=\"{repo}\" target=\"_blank\">github</a> or <a class=\"text-backdrop\" href=\"https://twitter.com/justusecobalt\" target=\"_blank\">twitter</a>. both work just fine, use whichever you're more comfortable with.\n\nthank you for using cobalt, and thank you for reading this changelog.\n\nyou're amazing, keep it up :)" "content": "this update includes a bunch of improvements, many of which were made thanks to the community :D\n\nservice-related improvements:\n*; private soundcloud links are now supported (#68);\n*; tiktok usernames with dots in them no longer confuse cobalt (#71);\n*; .ogg files no longer wrongfully include a video channel (#67);\n*; fixed an issue that caused cobalt to freak out when user attempted to download an audio from audio-only service with \"mute video\" option enabled.\n\nui improvements:\n*; popup padding has been evened out. popups are now able to fit in more information on scroll, especially on mobile;\n*; all buttons are now of even size and are displayed without any padding issues across all modern browsers and devices;\n*; checkbox is no longer crippled on ios;\n*; many explanation texts have been simplified to get rid of unnecessary bloat (no bullshit, remember?);\n*; moved tiktok section in video settings higher due to higher priority;\n*; fixed unexpectedly displayed scrollbars on switch rows in firefox.\n\nstability improvements:\n*; ffmpeg process now should end upon finishing the render;\n*; ffmpeg should also quit when download is abruptly cut off;\n*; fixed a memory leak that was caused by misconfigured stream information caching (#63).\n\ninternal improvements:\n*; requested streams are now stored in cache for 2 minutes instead of 1000 hours (yes, 1000 hours, i fucked up);\n*; cached data is now reused if user requests same content within 2 minutes;\n*; page render module is now even cleaner than before;\n*; proper support for bullet-points in loc strings.\n\nyou can suggest features or report bugs on <a class=\"text-backdrop italic\" href=\"{repo}\" target=\"_blank\">github</a> or <a class=\"text-backdrop italic\" href=\"https://twitter.com/justusecobalt\" target=\"_blank\">twitter</a>. both work just fine, use whichever you're more comfortable with.\n\nthank you for using cobalt, and thank you for reading this changelog.\n\nyou're amazing, keep it up :)"
}, }, {
"history": [{
"version": "4.6", "version": "4.6",
"title": "mute videos and proper soundcloud support", "title": "mute videos and proper soundcloud support",
"banner": "shutup.png", "banner": "shutup.png",
"content": "i've been longing to implement both of these things, and here they finally are.\n\nservice-related improvements:\n{bS}*; you now can download videos with no audio! simply enable the \"mute audio\" option in settings &gt; audio.\n*; soundcloud module has been updated, and downloads should no longer break after some time.{bE}\nvisual improvements:\n{bS}*; moved some things around in settings popup, and added separators where separation is needed.\n*; updated some texts in english and russian.\n*; version and commit hash have been joined together, now they're a single unit.{bE}\ninternal improvements:\n{bS}*; updated api documentation to include isAudioMuted.\n*; simplified the startup message.\n*; created render elements for separator and explanation due to high duplication of them in the page.\n*; fully deprecated GET method for API requests.\n*; fixed some code quirks.{bE}\nhere's how soundcloud downloads got fixed:\n\npreviously, client_id was (stupidly) hardcoded. that means cobalt wasn't able to fetch song data if soundcloud web app got updated.\nnow, cobalt tries to find the up-to-date client_id, caches it in memory, and checks if web app version has changed to update the id accordingly. you can see this change for yourself on github." "content": "i've been longing to implement both of these things, and here they finally are.\n\nservice-related improvements:\n*; you now can download videos with no audio! simply enable the \"mute audio\" option in settings &gt; audio.\n*; soundcloud module has been updated, and downloads should no longer break after some time.\nvisual improvements:\n*; moved some things around in settings popup, and added separators where separation is needed.\n*; updated some texts in english and russian.\n*; version and commit hash have been joined together, now they're a single unit.\ninternal improvements:\n*; updated api documentation to include isAudioMuted.\n*; simplified the startup message.\n*; created render elements for separator and explanation due to high duplication of them in the page.\n*; fully deprecated GET method for API requests.\n*; fixed some code quirks.\nhere's how soundcloud downloads got fixed:\n\npreviously, client_id was (stupidly) hardcoded. that means cobalt wasn't able to fetch song data if soundcloud web app got updated.\nnow, cobalt tries to find the up-to-date client_id, caches it in memory, and checks if web app version has changed to update the id accordingly. you can see this change for yourself on github."
}, { }, {
"version": "4.5", "version": "4.5",
"title": "better, faster, stronger, stable", "title": "better, faster, stronger, stable",
"banner": "meowthstrong.webp", "banner": "meowthstrong.webp",
"content": "your favorite social media downloader just got even better! this update includes a ton of imporvements and fixes.\n\nin fact, there are so many changes, i had to split them in sections.\n\nservice-related improvements:\n{bS}*; vimeo module has been revamped, all sorts of videos should now be supported.\n*; vimeo audio downloads! you now can download audios from more recent videos.\n*; {appName} now supports all sorts of tumblr links. (even those scary ones from the mobile app)\n*; vk clips support has been fixed. they rolled back the separation of videos and clips, so i had to do the same.\n*; youtube videos with community warnings should now be possible to download.{bE}\nuser interface improvements:\n{bS}*; list of supported services is now MUCH easier to read.\n*; banners in changelog history should no longer overlap each other.\n*; bullet points! they have a bit of extra padding, so it makes them stand out of the rest of text.{bE}\ninternal improvements:\n{bS}*; cobalt will now match the link to regex when using ?u= query for autopasting it into input area.\n*; better rate limiting: limiting now is done per minute, not per 20 minutes. this ensures less waiting and less attack area for request spammers.\n*; moved to my own fork of ytdl-core, cause main project seems to have been abandoned. go check it out on <a class=\"text-backdrop\" href=\"https://github.com/wukko/better-ytdl-core\" target=\"_blank\">github</a> or <a class=\"text-backdrop\" href=\"https://www.npmjs.com/package/better-ytdl-core\" target=\"_blank\">npm</a>!\n*; ALL user inputs are now properly sanitized on the server. that includes variables for POST api method, too.\n*; \"got\" package has been (mostly) replaced by native fetch api. this should greately reduce ram usage.\n*; all unnecessary duplications of module imports have been gotten rid of. no more error passing strings from inside of service modules. you don't make mistakes only if you don't do anything, right?\n*; other code optimizations. there's less clutter overall.{bE}\nhuge update, right? seems like everything's fixed now?\n\nnope, one issue still persists: sometimes youtube server drops packets for an audio file while cobalt's rendering the video for you. this results in abrupt cuts of audio. if you want to help solving this issue, <a class=\"text-backdrop\" href=\"https://github.com/wukko/cobalt/issues/62\" target=\"_blank\">please feel free to do it on github!</a>\n\nthank you for reading this, and thank you for sticking with cobalt and me." "content": "your favorite social media downloader just got even better! this update includes a ton of imporvements and fixes.\n\nin fact, there are so many changes, i had to split them in sections.\n\nservice-related improvements:\n*; vimeo module has been revamped, all sorts of videos should now be supported.\n*; vimeo audio downloads! you now can download audios from more recent videos.\n*; {appName} now supports all sorts of tumblr links. (even those scary ones from the mobile app)\n*; vk clips support has been fixed. they rolled back the separation of videos and clips, so i had to do the same.\n*; youtube videos with community warnings should now be possible to download.\nuser interface improvements:\n*; list of supported services is now MUCH easier to read.\n*; banners in changelog history should no longer overlap each other.\n*; bullet points! they have a bit of extra padding, so it makes them stand out of the rest of text.\ninternal improvements:\n*; cobalt will now match the link to regex when using ?u= query for autopasting it into input area.\n*; better rate limiting: limiting now is done per minute, not per 20 minutes. this ensures less waiting and less attack area for request spammers.\n*; moved to my own fork of ytdl-core, cause main project seems to have been abandoned. go check it out on <a class=\"text-backdrop italic\" href=\"https://github.com/wukko/better-ytdl-core\" target=\"_blank\">github</a> or <a class=\"text-backdrop italic\" href=\"https://www.npmjs.com/package/better-ytdl-core\" target=\"_blank\">npm</a>!\n*; ALL user inputs are now properly sanitized on the server. that includes variables for POST api method, too.\n*; \"got\" package has been (mostly) replaced by native fetch api. this should greately reduce ram usage.\n*; all unnecessary duplications of module imports have been gotten rid of. no more error passing strings from inside of service modules. you don't make mistakes only if you don't do anything, right?\n*; other code optimizations. there's less clutter overall.\nhuge update, right? seems like everything's fixed now?\n\nnope, one issue still persists: sometimes youtube server drops packets for an audio file while cobalt's rendering the video for you. this results in abrupt cuts of audio. if you want to help solving this issue, <a class=\"text-backdrop italic\" href=\"https://github.com/wukko/cobalt/issues/62\" target=\"_blank\">please feel free to do it on github!</a>\n\nthank you for reading this, and thank you for sticking with cobalt and me."
}, { }, {
"version": "4.4", "version": "4.4",
"title": "over 1 million monthly requests. thank you.", "title": "over 1 million monthly requests. thank you.",
@ -28,7 +33,7 @@
"version": "4.3", "version": "4.3",
"title": "developers, developers, developers, developers", "title": "developers, developers, developers, developers",
"banner": "developersdevelopersdevelopers.webp", "banner": "developersdevelopersdevelopers.webp",
"content": "this update features a TON of improvements.\n\n<a class=\"text-backdrop\" href=\"https://www.youtube.com/watch?v=SaVTHG-Ev4k\" target=\"_blank\">developers</a>, you now can rely on {appName} for getting content from social media. the api has been revamped and <a class=\"text-backdrop\" href=\"https://github.com/wukko/cobalt/tree/current/docs/API.md\" target=\"_blank\">documentation</a> is now available. you can read more about API changes down below. go crazy, and have fun :D\n\nif you're not a developer, here's a list of changes that you probably care about:\n- rate limit is now approximately 8 times bigger. no more waiting, even if you want to download entirety of your tiktok \"for you\" page.\n- some updates will now have expressive banners, just like this one.\n- fixed what was causing an error when a youtube video had no description.\n- mp4 format button text should now be displayed properly, no matter if you touched the switcher or not.\n\nnext, the star of this update — improved api!\n- main endpoint now uses POST method instead of GET.\n- internal variables for preferences have been updated to be consistent and easier to understand.\n- ip address is now hashed right upon request, not somewhere deep inside the code.\n- global stream salt variable is no longer unnecessarily passed over a billion functions.\n- url and picker keys are now separate in the json response.\n- {appName} web app now correctly processes responses with \"success\" status.\n\nif you currently have a siri shortcut or some other script that uses the GET method, make sure to update it soon. this method is deprecated, limited, and will be removed entirely in coming updates.\n\nif you ever make something using {appName}'s api, make sure to mention <a class=\"text-backdrop\" href=\"https://twitter.com/justusecobalt\" target=\"_blank\">@justusecobalt</a> on twitter, i would absolutely love to see what you made." "content": "this update features a TON of improvements.\n\n<a class=\"text-backdrop italic\" href=\"https://www.youtube.com/watch?v=SaVTHG-Ev4k\" target=\"_blank\">developers</a>, you now can rely on {appName} for getting content from social media. the api has been revamped and <a class=\"text-backdrop italic\" href=\"https://github.com/wukko/cobalt/tree/current/docs/API.md\" target=\"_blank\">documentation</a> is now available. you can read more about API changes down below. go crazy, and have fun :D\n\nif you're not a developer, here's a list of changes that you probably care about:\n- rate limit is now approximately 8 times bigger. no more waiting, even if you want to download entirety of your tiktok \"for you\" page.\n- some updates will now have expressive banners, just like this one.\n- fixed what was causing an error when a youtube video had no description.\n- mp4 format button text should now be displayed properly, no matter if you touched the switcher or not.\n\nnext, the star of this update — improved api!\n- main endpoint now uses POST method instead of GET.\n- internal variables for preferences have been updated to be consistent and easier to understand.\n- ip address is now hashed right upon request, not somewhere deep inside the code.\n- global stream salt variable is no longer unnecessarily passed over a billion functions.\n- url and picker keys are now separate in the json response.\n- {appName} web app now correctly processes responses with \"success\" status.\n\nif you currently have a siri shortcut or some other script that uses the GET method, make sure to update it soon. this method is deprecated, limited, and will be removed entirely in coming updates.\n\nif you ever make something using {appName}'s api, make sure to mention <a class=\"text-backdrop italic\" href=\"https://twitter.com/justusecobalt\" target=\"_blank\">@justusecobalt</a> on twitter, i would absolutely love to see what you made."
}, { }, {
"version": "4.2", "version": "4.2",
"title": "optimized quality picking and 8k video support", "title": "optimized quality picking and 8k video support",
@ -40,7 +45,7 @@
}, { }, {
"version": "4.0", "version": "4.0",
"title": "better and faster than ever", "title": "better and faster than ever",
"content": "this update has a ton of improvements and new features.\n\nchanges you probably care about:\n- {appName} now has support for recorded twitter spaces! download the previous conversation no matter how long it was.\n- download speeds from youtube are at least 10 times better now. you're welcome.\n- both video and audio length limits have been extended to 2 hours.\n- audio downloads from youtube, youtube music, twitter spaces, and soundcloud now have metadata! most often it's just title and artist, but when {appName} is able to get more info, it adds that metadata too.\n- tiktok downloads have been fixed, yet again, and if they ever break in the future, {appName} will fall back to downloading a less annoyingly watermarked video.\n- soundcloud downloads have been fixed, too.\n\nless notable changes:\n- currently experimenting with using mp3 as default audio format. if you set something other than mp3 before, it'll be set to mp3. you can always change it back in settings. let me know what you think about this.\n- \"download audio\" button from image picker no longer stays on the screen after popup was closed.\n- clipboard button now shows up depending on your browser's support for it.\n- you can no longer manually hide the clipboard button, 'cause it's unnecessary.\n- small internal improvements such as separation of changelog version and title.\n- fair bit of internal clean up.\n\nif you want to help me implement covers for downloaded audios, <a class=\"text-backdrop\" href=\"https://github.com/wukko/cobalt\" target=\"_blank\">you can do it on github</a>." "content": "this update has a ton of improvements and new features.\n\nchanges you probably care about:\n- {appName} now has support for recorded twitter spaces! download the previous conversation no matter how long it was.\n- download speeds from youtube are at least 10 times better now. you're welcome.\n- both video and audio length limits have been extended to 2 hours.\n- audio downloads from youtube, youtube music, twitter spaces, and soundcloud now have metadata! most often it's just title and artist, but when {appName} is able to get more info, it adds that metadata too.\n- tiktok downloads have been fixed, yet again, and if they ever break in the future, {appName} will fall back to downloading a less annoyingly watermarked video.\n- soundcloud downloads have been fixed, too.\n\nless notable changes:\n- currently experimenting with using mp3 as default audio format. if you set something other than mp3 before, it'll be set to mp3. you can always change it back in settings. let me know what you think about this.\n- \"download audio\" button from image picker no longer stays on the screen after popup was closed.\n- clipboard button now shows up depending on your browser's support for it.\n- you can no longer manually hide the clipboard button, 'cause it's unnecessary.\n- small internal improvements such as separation of changelog version and title.\n- fair bit of internal clean up.\n\nif you want to help me implement covers for downloaded audios, <a class=\"text-backdrop italic\" href=\"https://github.com/wukko/cobalt\" target=\"_blank\">you can do it on github</a>."
}, { }, {
"version": "3.7", "version": "3.7",
"title": "support for multi media tweets is here!", "title": "support for multi media tweets is here!",
@ -56,7 +61,7 @@
}, { }, {
"version": "3.5.4", "version": "3.5.4",
"title": "tiktok support is back :D", "title": "tiktok support is back :D",
"content": "you can download videos, sounds, and images from tiktok again!\nhuge thank you to <a class=\"text-backdrop\" href=\"https://github.com/minzique\" target=\"_blank\">@minzique</a> for finding another api endpoint that works." "content": "you can download videos, sounds, and images from tiktok again!\nhuge thank you to <a class=\"text-backdrop italic\" href=\"https://github.com/minzique\" target=\"_blank\">@minzique</a> for finding another api endpoint that works."
}, { }, {
"version": "3.5.2", "version": "3.5.2",
"title": "vk clips support, improved changelog system, and less bugs", "title": "vk clips support, improved changelog system, and less bugs",

View file

@ -3,7 +3,7 @@ const names = {
"🎬": "clapper_board", "🎬": "clapper_board",
"💰": "money_bag", "💰": "money_bag",
"🎉": "party_popper", "🎉": "party_popper",
"❓": "red_question_mark", "❓": "question_mark",
"✨": "sparkles", "✨": "sparkles",
"🪅": "pinata", "🪅": "pinata",
"🪄": "magic_wand", "🪄": "magic_wand",
@ -18,17 +18,22 @@ const names = {
"🕯️": "candle", "🕯️": "candle",
"😺": "cat", "😺": "cat",
"🐶": "dog", "🐶": "dog",
"🎂": "cake" "🎂": "cake",
"🐘": "elephant",
"🐦": "bird",
"🐙": "octopus",
"🔮": "crystal_ball"
} }
let sizing = { let sizing = {
22: 0.4, 22: 0.4,
30: 0.7, 30: 0.7,
48: 0.9 48: 0.9,
64: 0.9
} }
export default function(emoji, size, disablePadding) { export default function(emoji, size, disablePadding) {
if (!size) size = 22; if (!size) size = 22;
let padding = size !== 22 ? `margin-right:${sizing[size] ? sizing[size] : "0.4"}rem;` : ``; let padding = size !== 22 ? `margin-right:${sizing[size] ? sizing[size] : "0.4"}rem;` : ``;
if (disablePadding) padding = 'margin-right:0!important;'; if (disablePadding) padding = 'margin-right:0!important;';
if (!names[emoji]) emoji = "❓"; if (!names[emoji]) emoji = "❓";
return `<img class="emoji" height="${size}" width="${size}" style="${padding}" alt="${emoji}" src="emoji/${names[emoji]}.svg">` return `<img class="emoji" draggable=false height="${size}" width="${size}" style="${padding}" alt="${emoji}" src="emoji/${names[emoji]}.svg">`
} }

View file

@ -2,26 +2,22 @@ import { celebrations } from "../config.js";
export function switcher(obj) { export function switcher(obj) {
let items = ``; let items = ``;
switch(obj.name) { if (obj.name == "download") {
case "download": items = obj.times;
items = obj.items; } else {
break; for (let i = 0; i < obj.items.length; i++) {
default: let classes = obj.items[i]["classes"] ? obj.items[i]["classes"] : []
for (let i = 0; i < obj.items.length; i++) { 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>`
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 ` return `<div id="${obj.name}-switcher" class="switch-container">
<div id="${obj.name}-switcher" class="switch-container">
${obj.subtitle ? `<div class="subtitle">${obj.subtitle}</div>` : ``} ${obj.subtitle ? `<div class="subtitle">${obj.subtitle}</div>` : ``}
<div class="switches">${items}</div> <div class="switches">${items}</div>
${obj.explanation ? `<div class="explanation">${obj.explanation}</div>` : ``} ${obj.explanation ? `<div class="explanation">${obj.explanation}</div>` : ``}
</div>` </div>`
} }
export function checkbox(action, text, aria, paddingType) { export function checkbox(action, text, paddingType, aria) {
let paddingClass = ` ` let paddingClass = ` `
switch (paddingType) { switch (paddingType) {
case 1: case 1:
@ -33,6 +29,8 @@ export function checkbox(action, text, aria, paddingType) {
case 3: case 3:
paddingClass += "no-margin" paddingClass += "no-margin"
break; break;
case 4:
paddingClass += "top-margin-only"
} }
return `<label id="${action}-chkbx" class="checkbox${paddingClass}"> return `<label id="${action}-chkbx" class="checkbox${paddingClass}">
<input id="${action}" type="checkbox" ${aria ? `aria-label="${aria}"` : `aria-label="${text}"`} onclick="checkbox('${action}')"> <input id="${action}" type="checkbox" ${aria ? `aria-label="${aria}"` : `aria-label="${text}"`} onclick="checkbox('${action}')">
@ -65,9 +63,9 @@ export function popup(obj) {
} }
return ` return `
${obj.standalone ? `<div id="popup-${obj.name}" class="popup center box${classes.length > 0 ? ' ' + classes.join(' ') : ''}" style="visibility: hidden;">` : ''} ${obj.standalone ? `<div id="popup-${obj.name}" class="popup center box${classes.length > 0 ? ' ' + classes.join(' ') : ''}" style="visibility: hidden;">` : ''}
${obj.buttonOnly ? obj.emoji : ``}
<div id="popup-header" class="popup-header"> <div id="popup-header" class="popup-header">
${obj.standalone && !obj.buttonOnly ? `<button id="popup-close" class="button mono" onclick="popup('${obj.name}', 0)" ${obj.header.closeAria ? `aria-label="${obj.header.closeAria}"` : ''}>x</button>` : ''} ${obj.standalone && !obj.buttonOnly ? `<button id="close-button" class="switch up" onclick="popup('${obj.name}', 0)" ${obj.header.closeAria ? `aria-label="${obj.header.closeAria}"` : ''}>x</button>` : ''}
${obj.buttonOnly ? obj.header.emoji : ``}
${obj.header.aboveTitle ? `<a id="popup-above-title" target="_blank" href="${obj.header.aboveTitle.url}">${obj.header.aboveTitle.text}</a>` : ''} ${obj.header.aboveTitle ? `<a id="popup-above-title" target="_blank" href="${obj.header.aboveTitle.url}">${obj.header.aboveTitle.text}</a>` : ''}
${obj.header.title ? `<div id="popup-title">${obj.header.title}</div>` : ''} ${obj.header.title ? `<div id="popup-title">${obj.header.title}</div>` : ''}
${obj.header.subtitle ? `<div id="popup-subtitle">${obj.header.subtitle}</div>` : ''} ${obj.header.subtitle ? `<div id="popup-subtitle">${obj.header.subtitle}</div>` : ''}
@ -88,7 +86,7 @@ export function multiPagePopup(obj) {
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>` 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>` 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>` tabs += `<button id="close-button" class="switch tab-${obj.name}" onclick="popup('${obj.name}', 0)" ${obj.closeAria ? `aria-label="${obj.closeAria}"` : ''}>x</button>`
return ` return `
<div id="popup-${obj.name}" class="popup center box scrollable" style="visibility: hidden;"> <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"> <div id="popup-content">${obj.header ? `<div id="popup-header" class="popup-header">
@ -98,13 +96,25 @@ export function multiPagePopup(obj) {
<div id="popup-tabs" class="switches popup-tabs">${tabs}</div> <div id="popup-tabs" class="switches popup-tabs">${tabs}</div>
</div>` </div>`
} }
export function collapsibleList(arr) {
let items = ``
for (let i = 0; i < arr.length; i++) {
items += `<div id="${arr[i]["name"]}-collapse" class="collapse-list">
<div class="collapse-header" onclick="expandCollapsible(event)">
<div class="collapse-title">${arr[i]["title"]}</div>
<div class="collapse-indicator">^</div>
</div>
<div id="${arr[i]["name"]}-body" class="collapse-body">${arr[i]["body"]}</div>
</div>`
}
return items;
}
export function popupWithBottomButtons(obj) { export function popupWithBottomButtons(obj) {
let tabs = `` let tabs = ``
for (let i = 0; i < obj.buttons.length; i++) { for (let i = 0; i < obj.buttons.length; i++) {
tabs += obj.buttons[i] tabs += obj.buttons[i]
} }
tabs += `<button id="close-bottom" class="switch tab-${obj.name}" onclick="popup('${obj.name}', 0)" ${obj.closeAria ? `aria-label="${obj.closeAria}"` : ''}>x</button>` tabs += `<button id="close-button" class="switch tab-${obj.name}" onclick="popup('${obj.name}', 0)" ${obj.closeAria ? `aria-label="${obj.closeAria}"` : ''}>x</button>`
return ` return `
<div id="popup-${obj.name}" class="popup center box scrollable" style="visibility: hidden;"> <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"> <div id="popup-content">${obj.header ? `<div id="popup-header" class="popup-header">
@ -116,13 +126,15 @@ export function popupWithBottomButtons(obj) {
</div>` </div>`
} }
export function backdropLink(link, text) { export function backdropLink(link, text) {
return `<a class="text-backdrop" href="${link}" target="_blank">${text}</a>` return `<a class="text-backdrop italic" href="${link}" target="_blank">${text}</a>`
}
export function socialLink(emoji, name, handle, url) {
return `<div class="social-link">${emoji} ${name}: <a class="text-backdrop italic" href="${url}" target="_blank">${handle}</a></div>`
} }
export function settingsCategory(obj) { export function settingsCategory(obj) {
return `<div id="settings-${obj.name}" class="settings-category"> return `<div id="settings-${obj.name}" class="settings-category">
<div class="category-title">${obj.title ? obj.title : obj.name}</div> <div class="category-title">${obj.title ? obj.title : obj.name}</div>
<div class="settings-category-content">${obj.body}</div> <div class="category-content">${obj.body}</div>
</div>` </div>`
} }

View file

@ -1,4 +1,4 @@
import { backdropLink, celebrationsEmoji, checkbox, explanation, footerButtons, multiPagePopup, popup, popupWithBottomButtons, sep, settingsCategory, switcher } from "./elements.js"; import { backdropLink, celebrationsEmoji, checkbox, collapsibleList, explanation, footerButtons, multiPagePopup, popup, popupWithBottomButtons, sep, settingsCategory, switcher, socialLink } from "./elements.js";
import { services as s, appName, authorInfo, version, quality, repo, donations, supportedAudio } from "../config.js"; import { services as s, appName, authorInfo, version, quality, repo, donations, supportedAudio } from "../config.js";
import { getCommitInfo } from "../sub/currentCommit.js"; import { getCommitInfo } from "../sub/currentCommit.js";
import loc from "../../localization/manager.js"; import loc from "../../localization/manager.js";
@ -11,7 +11,7 @@ let enabledServices = Object.keys(s).filter((p) => {
if (s[p].enabled) return true; if (s[p].enabled) return true;
}).sort().map((p) => { }).sort().map((p) => {
return `<br>&bull; ${s[p].alias ? s[p].alias : p}` return `<br>&bull; ${s[p].alias ? s[p].alias : p}`
}).join(';').substring(4) }).join('').substring(4)
let donate = `` let donate = ``
let donateLinks = `` let donateLinks = ``
@ -79,21 +79,29 @@ export default function(obj) {
url: authorInfo.link url: authorInfo.link
}, },
closeAria: t('AccessibilityClosePopup'), closeAria: t('AccessibilityClosePopup'),
title: t('TitlePopupAbout') title: `${emoji("🔮", 30)} ${t('TitlePopupAbout')}`
}, },
body: [{ body: [{
text: t('AboutSummary') text: t('AboutSummary')
}, { }, {
text: `${t('AboutSupportedServices')}`, text: collapsibleList([{
nopadding: true "name": "services",
}, { "title": t("CollapseServices"),
text: `<div class="bullpadding">${enabledServices}.</div>` "body": `${enabledServices}<br/><br/>${t("ServicesNote")}`
}, { }, {
text: obj.lang !== "ru" ? t('FollowTwitter') : "", "name": "support",
classes: ["desc-padding"] "title": t("CollapseSupport"),
}, { "body": `${t("FollowSupport")}<br/>
text: backdropLink(repo, t('LinkGitHubIssues')), ${socialLink(emoji("🐘"), "mastodon", authorInfo.support.mastodon.handle, authorInfo.support.mastodon.url)}
classes: ["bottom-link"] ${socialLink(emoji("🐦"), "twitter", authorInfo.support.twitter.handle, authorInfo.support.twitter.url)}<br/>
${t("SourceCode")}<br/>
${socialLink(emoji("🐙"), "github", repo.replace("https://github.com/", ''), repo)}<br/>
${t("SupportNote")}`
}, {
"name": "privacy",
"title": t("CollapsePrivacy"),
"body": t("PrivacyPolicy")
}])
}] }]
}) })
}, { }, {
@ -211,7 +219,7 @@ export default function(obj) {
+ settingsCategory({ + settingsCategory({
name: "tiktok", name: "tiktok",
title: "tiktok & douyin", title: "tiktok & douyin",
body: checkbox("disableTikTokWatermark", t('SettingsRemoveWatermark')) body: checkbox("disableTikTokWatermark", t('SettingsRemoveWatermark'), 3)
}) })
+ settingsCategory({ + settingsCategory({
name: "youtube", name: "youtube",
@ -239,11 +247,11 @@ export default function(obj) {
subtitle: t('SettingsFormatSubtitle'), subtitle: t('SettingsFormatSubtitle'),
explanation: t('SettingsAudioFormatDescription'), explanation: t('SettingsAudioFormatDescription'),
items: audioFormats items: audioFormats
}) + sep(0) + checkbox("muteAudio", t('SettingsVideoMute'), t('SettingsVideoMute'), 3) + explanation(t('SettingsVideoMuteExplanation')) }) + sep(0) + checkbox("muteAudio", t('SettingsVideoMute'), 3) + explanation(t('SettingsVideoMuteExplanation'))
}) + settingsCategory({ }) + settingsCategory({
name: "tiktok", name: "tiktok",
title: "tiktok & douyin", title: "tiktok & douyin",
body: checkbox("fullTikTokAudio", t('SettingsAudioFullTikTok'), t('SettingsAudioFullTikTok'), 3) + `<div class="explanation">${t('SettingsAudioFullTikTokDescription')}</div>` body: checkbox("fullTikTokAudio", t('SettingsAudioFullTikTok'), 3) + `<div class="explanation">${t('SettingsAudioFullTikTokDescription')}</div>`
}) })
}, { }, {
name: "other", name: "other",
@ -264,11 +272,11 @@ export default function(obj) {
"action": "light", "action": "light",
"text": t('SettingsThemeLight') "text": t('SettingsThemeLight')
}] }]
}) + checkbox("alwaysVisibleButton", t('SettingsKeepDownloadButton'), t('AccessibilityKeepDownloadButton'), 2) }) + checkbox("alwaysVisibleButton", t('SettingsKeepDownloadButton'), 4, t('AccessibilityKeepDownloadButton'))
}) + settingsCategory({ }) + settingsCategory({
name: "miscellaneous", name: "miscellaneous",
title: t('Miscellaneous'), title: t('Miscellaneous'),
body: checkbox("disableChangelog", t('SettingsDisableNotifications')) + `${!isIOS ? checkbox("downloadPopup", t('SettingsEnableDownloadPopup'), t('AccessibilityEnableDownloadPopup'), 1) : ''}` body: checkbox("disableChangelog", t('SettingsDisableNotifications')) + `${!isIOS ? checkbox("downloadPopup", t('SettingsEnableDownloadPopup'), 1, t('AccessibilityEnableDownloadPopup')) : ''}`
}) })
}], }],
})} })}
@ -301,12 +309,12 @@ export default function(obj) {
name: "error", name: "error",
standalone: true, standalone: true,
buttonOnly: true, buttonOnly: true,
emoji: emoji("☹️", 48, 1),
classes: ["small"], classes: ["small"],
buttonText: t('ErrorPopupCloseButton'), buttonText: t('ErrorPopupCloseButton'),
header: { header: {
closeAria: t('AccessibilityClosePopup'), closeAria: t('AccessibilityClosePopup'),
title: t('TitlePopupError') title: t('TitlePopupError'),
emoji: emoji("☹️", 64, 1),
}, },
body: `<div id="desc-error" class="desc-padding subtext"></div>` body: `<div id="desc-error" class="desc-padding subtext"></div>`
})} })}

View file

@ -2,7 +2,7 @@
"audioIgnore": ["vk"], "audioIgnore": ["vk"],
"config": { "config": {
"bilibili": { "bilibili": {
"alias": "bilibili.com", "alias": "bilibili (.com only)",
"patterns": ["video/:id"], "patterns": ["video/:id"],
"quality_match": ["2160", "1440", "1080", "720", "480", "360", "240", "144"], "quality_match": ["2160", "1440", "1080", "720", "480", "360", "240", "144"],
"enabled": true "enabled": true
@ -63,13 +63,13 @@
"enabled": true "enabled": true
}, },
"tiktok": { "tiktok": {
"alias": "tiktok videos & slideshow & audio", "alias": "tiktok videos & photos & audio",
"patterns": [":user/video/:postId", ":id", "t/:id"], "patterns": [":user/video/:postId", ":id", "t/:id"],
"audioFormats": ["best", "m4a", "mp3"], "audioFormats": ["best", "m4a", "mp3"],
"enabled": true "enabled": true
}, },
"douyin": { "douyin": {
"alias": "douyin videos & slideshow & audio", "alias": "douyin videos & photos & audio",
"patterns": ["video/:postId", ":id"], "patterns": ["video/:postId", ":id"],
"enabled": true "enabled": true
}, },

View file

@ -45,7 +45,7 @@ export function verifyStream(ip, id, hmac, exp) {
let streamInfo = streamCache.get(id); let streamInfo = streamCache.get(id);
if (streamInfo) { if (streamInfo) {
let ghmac = sha256(`${id},${streamInfo.service},${ip},${exp}`, salt); let ghmac = sha256(`${id},${streamInfo.service},${ip},${exp}`, salt);
if (hmac == ghmac && ip == streamInfo.ip && ghmac == streamInfo.hmac && exp > Math.floor(new Date().getTime()) && exp == streamInfo.exp) { if (hmac == ghmac && exp.toString() == streamInfo.exp && ghmac == streamInfo.hmac && ip == streamInfo.ip && exp > Math.floor(new Date().getTime())) {
return streamInfo; return streamInfo;
} else { } else {
return { error: 'Unauthorized', status: 401 }; return { error: 'Unauthorized', status: 401 };