diff --git a/package.json b/package.json index 41099c3a..ecf96a4f 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "homepage": "https://github.com/wukko/cobalt#readme", "dependencies": { - "content-disposition-header": "^0.6.0", + "content-disposition-header": "0.6.0", "cors": "^2.8.5", "dotenv": "^16.0.1", "esbuild": "^0.14.51", @@ -37,6 +37,6 @@ "set-cookie-parser": "2.6.0", "undici": "^5.19.1", "url-pattern": "1.0.3", - "youtubei.js": "^5.4.0" + "youtubei.js": "^6.4.1" } } diff --git a/src/config.json b/src/config.json index c14f6ce5..56c8ed5c 100644 --- a/src/config.json +++ b/src/config.json @@ -13,15 +13,20 @@ "url": "https://twitter.com/justusecobalt", "handle": "@justusecobalt" }, + "discord": { + "emoji": "👾", + "url": "https://discord.gg/pQPt8HBUPu", + "handle": "cobalt community server" + }, "mastodon": { "emoji": "🐘", "url": "https://wetdry.world/@cobalt", "handle": "@cobalt@wetdry.world" }, - "discord": { - "emoji": "👾", - "url": "https://discord.gg/pQPt8HBUPu", - "handle": "cobalt community server" + "support email": { + "emoji": "📧", + "url": "mailto:support@cobalt.tools", + "handle": "support@cobalt.tools" } } } diff --git a/src/front/cobalt.css b/src/front/cobalt.css index 07c14158..e17b552f 100644 --- a/src/front/cobalt.css +++ b/src/front/cobalt.css @@ -886,6 +886,46 @@ button:active, opacity: 1; transition: opacity 0.2s ease-out; } +.sponsored-by-text { + text-align: center!important; + font-size: .85rem; + color: var(--accent-subtext); + user-select: none; +} +#sponsored-logos { + width: 100%; + display: flex; + justify-content: center; + flex-wrap: wrap; + gap: 0.2rem 1rem; + margin-bottom: 1rem; +} +.sponsored-logo svg { + height: inherit; + width: inherit; +} +.sponsored-logo svg path { + fill: var(--accent-subtext); +} +#filename-preview { + background: var(--accent-button); + margin-top: 0.8rem; +} +.filename-item { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + gap: 1rem; + padding: 0.5rem 0.7rem; +} +.filename-item.line { + border-bottom: 0.1rem solid var(--accent-button-elevated); +} +.filename-label { + color: var(--accent-subtext); + font-size: 0.8rem; +} /* rounded corners */ #bottom #paste, #footer .switch, @@ -900,7 +940,8 @@ button:active, #popup-about .switch, #popup-tabs .switch, .text-to-copy, -.text-to-copy.text-backdrop { +.text-to-copy.text-backdrop, +#filename-preview { border-radius: 5px / 6px; } [type=checkbox] { @@ -946,27 +987,6 @@ button:active, .collapse-list.last.expanded .collapse-header { border-radius: 0; } -.sponsored-by-text { - text-align: center!important; - font-size: .85rem; - color: var(--accent-subtext); - user-select: none; -} -#sponsored-logos { - width: 100%; - display: flex; - justify-content: center; - flex-wrap: wrap; - gap: 0.2rem 1rem; - margin-bottom: 1rem; -} -.sponsored-logo svg { - height: inherit; - width: inherit; -} -.sponsored-logo svg path { - fill: var(--accent-subtext); -} /* prevent resizing fliecker on ios if web app is installed as standalone */ @media all and (display-mode: standalone) { #home.visible { diff --git a/src/front/cobalt.js b/src/front/cobalt.js index 1757dc6a..eed6b43f 100644 --- a/src/front/cobalt.js +++ b/src/front/cobalt.js @@ -34,14 +34,20 @@ const checkboxes = [ const exceptions = { // used for mobile devices "vQuality": "720" }; -const bottomPopups = ["error", "download"] +const bottomPopups = ["error", "download"]; const pageQuery = new URLSearchParams(window.location.search); let store = {}; -function changeAPI(url) { - apiURL = url; +function fixApiUrl(url) { + return url.endsWith('/') ? url.slice(0, -1) : url +} + +let apiURL = fixApiUrl(defaultApiUrl); + +function changeApi(url) { + apiURL = fixApiUrl(url); return true } function eid(id) { @@ -128,6 +134,8 @@ function detectColorScheme() { document.documentElement.setAttribute("data-theme", theme); } function changeTab(evnt, tabId, tabClass) { + if (tabId === "tab-settings-other") updateFilenamePreview(); + let tabcontent = document.getElementsByClassName(`tab-content-${tabClass}`); let tablinks = document.getElementsByClassName(`tab-${tabClass}`); @@ -275,6 +283,7 @@ function changeSwitcher(li, b) { (switchers[li][i] === b) ? enable(`${li}-${b}`) : disable(`${li}-${switchers[li][i]}`) } if (li === "theme") detectColorScheme(); + if (li === "filenamePattern") updateFilenamePreview(); } else { let pref = switchers[li][0]; if (isMobile && exceptions[li]) pref = exceptions[li]; @@ -293,28 +302,6 @@ function checkbox(action) { } action === "disableChangelog" && sGet(action) === "true" ? notificationCheck("disable") : notificationCheck(); } -function loadSettings() { - if (sGet("alwaysVisibleButton") === "true") { - eid("alwaysVisibleButton").checked = true; - eid("download-button").value = '>>' - eid("download-button").style.padding = '0 1rem'; - } - if (sGet("downloadPopup") === "true" && !isIOS) { - eid("downloadPopup").checked = true; - } - if (sGet("reduceTransparency") === "true" || isOldFirefox) { - eid("cobalt-body").classList.add('no-transparency'); - } - if (sGet("disableAnimations") === "true") { - eid("cobalt-body").classList.add('no-animation'); - } - for (let i = 0; i < checkboxes.length; i++) { - if (sGet(checkboxes[i]) === "true") eid(checkboxes[i]).checked = true; - } - for (let i in switchers) { - changeSwitcher(i, sGet(i)) - } -} function changeButton(type, text) { switch (type) { case 0: //error @@ -516,6 +503,73 @@ function unpackSettings(b64) { } return changed } +function updateFilenamePreview() { + let videoFilePreview = ``; + let audioFilePreview = ``; + let resMatch = { + "max": "3840x2160", + "2160": "3840x2160", + "1440": "2560x1440", + "1080": "1920x1080", + "720": "1280x720", + "480": "854x480", + "360": "640x360", + } + // "dubLang" + // sGet("muteAudio") === "true" + switch(sGet("filenamePattern")) { + case "classic": + videoFilePreview = `youtube_yPYZpwSpKmA_${resMatch[sGet('vQuality')]}_${sGet('vCodec')}` + + `${sGet("muteAudio") === "true" ? "_mute" : ""}.${sGet('vCodec') === "vp9" ? 'webm' : 'mp4'}`; + audioFilePreview = `youtube_yPYZpwSpKmA_audio.${sGet('aFormat') !== "best" ? sGet('aFormat') : 'opus'}`; + break; + case "pretty": + videoFilePreview = + `${loc.FilenamePreviewVideoTitle} ` + + `(${sGet('vQuality') === "max" ? "2160p" : `${sGet('vQuality')}p`}, ${sGet('vCodec')}, ` + + `${sGet("muteAudio") === "true" ? "mute, " : ""}youtube).${sGet('vCodec') === "vp9" ? 'webm' : 'mp4'}`; + audioFilePreview = `${loc.FilenamePreviewAudioTitle} - ${loc.FilenamePreviewAudioAuthor} (soundcloud).${sGet('aFormat') !== "best" ? sGet('aFormat') : 'opus'}`; + break; + case "basic": + videoFilePreview = + `${loc.FilenamePreviewVideoTitle} ` + + `(${sGet('vQuality') === "max" ? "2160p" : `${sGet('vQuality')}p`}, ${sGet('vCodec')}${sGet("muteAudio") === "true" ? " mute" : ""}).${sGet('vCodec') === "vp9" ? 'webm' : 'mp4'}`; + audioFilePreview = `${loc.FilenamePreviewAudioTitle} - ${loc.FilenamePreviewAudioAuthor}.${sGet('aFormat') !== "best" ? sGet('aFormat') : 'opus'}`; + break; + case "nerdy": + videoFilePreview = + `${loc.FilenamePreviewVideoTitle} ` + + `(${sGet('vQuality') === "max" ? "2160p" : `${sGet('vQuality')}p`}, ${sGet('vCodec')}, ` + + `${sGet("muteAudio") === "true" ? "mute, " : ""}youtube, yPYZpwSpKmA).${sGet('vCodec') === "vp9" ? 'webm' : 'mp4'}`; + audioFilePreview = `${loc.FilenamePreviewAudioTitle} - ${loc.FilenamePreviewAudioAuthor} (soundcloud, 1242868615).${sGet('aFormat') !== "best" ? sGet('aFormat') : 'opus'}`; + break; + } + eid("video-filename-text").innerHTML = videoFilePreview + eid("audio-filename-text").innerHTML = audioFilePreview +} +function loadSettings() { + if (sGet("alwaysVisibleButton") === "true") { + eid("alwaysVisibleButton").checked = true; + eid("download-button").value = '>>' + eid("download-button").style.padding = '0 1rem'; + } + if (sGet("downloadPopup") === "true" && !isIOS) { + eid("downloadPopup").checked = true; + } + if (sGet("reduceTransparency") === "true" || isOldFirefox) { + eid("cobalt-body").classList.add('no-transparency'); + } + if (sGet("disableAnimations") === "true") { + eid("cobalt-body").classList.add('no-animation'); + } + for (let i = 0; i < checkboxes.length; i++) { + if (sGet(checkboxes[i]) === "true") eid(checkboxes[i]).checked = true; + } + for (let i in switchers) { + changeSwitcher(i, sGet(i)) + } + updateFilenamePreview() +} window.onload = () => { loadCelebrationsEmoji(); diff --git a/src/front/emoji/3d/film_frames.svg b/src/front/emoji/3d/film_frames.svg new file mode 100644 index 00000000..4caf8736 --- /dev/null +++ b/src/front/emoji/3d/film_frames.svg @@ -0,0 +1,338 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/front/emoji/3d/headphone.svg b/src/front/emoji/3d/headphone.svg new file mode 100644 index 00000000..90fc6cd6 --- /dev/null +++ b/src/front/emoji/3d/headphone.svg @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/front/emoji/email.svg b/src/front/emoji/email.svg new file mode 100644 index 00000000..144c9534 --- /dev/null +++ b/src/front/emoji/email.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/front/emoji/film_frames.svg b/src/front/emoji/film_frames.svg new file mode 100644 index 00000000..7471d431 --- /dev/null +++ b/src/front/emoji/film_frames.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/front/emoji/headphone.svg b/src/front/emoji/headphone.svg new file mode 100644 index 00000000..1c9b6702 --- /dev/null +++ b/src/front/emoji/headphone.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/localization/languages/en.json b/src/localization/languages/en.json index d90792c0..3264998f 100644 --- a/src/localization/languages/en.json +++ b/src/localization/languages/en.json @@ -107,7 +107,7 @@ "SourceCode": "report issues, explore source code, star or fork the repo:", "PrivacyPolicy": "cobalt's privacy policy is simple: no data about you is ever collected or stored. zero, zilch, nada, nothing.\nwhat you download is solely your business, not mine or anyone else's.\n\nif your download requires live render, some non-backtraceable data is temporarily stored in server's RAM. it's necessary for this feature to function.\n\nin this case info about requested content is stored for 20 seconds and then permanently removed.\nno one (even me) has access to this data. official cobalt codebase doesn't provide a way to read it outside of processing functions.\n\nyou can check cobalt's source code yourself and see that everything is as stated.", "ErrorYTUnavailable": "this youtube video is unavailable, it could be region or age restricted. try another one!", - "ErrorYTTryOtherCodec": "i couldn't find anything to download with your settings. try another codec or quality!\n\nnote: youtube api sometimes acts unexpectedly. blame google for this, not me.", + "ErrorYTTryOtherCodec": "i couldn't find anything to download with your settings. try another codec or quality!\n\nsometimes youtube api sometimes acts unexpectedly. try again or try another settings.", "SettingsCodecSubtitle": "youtube codec", "SettingsCodecDescription": "h264: generally better player support, but quality tops out at 1080p.\nav1: low player support, but supports 8k & HDR.\nvp9: usually highest bitrate, preserves most detail. supports 4k & HDR.\n\npick h264 if you want best editor/player/social media compatibility.", "SettingsAudioDub": "youtube audio track", @@ -146,10 +146,16 @@ "DataTransferError": "something went wrong when transferring your preferences. you'll have to open settings and configure cobalt by hand.", "SupportNotAffiliated": "cobalt is not affiliated with any services listed above.", "SponsoredBy": "sponsored by", - "FilenamePattern": "file name preset", + "FilenameTitle": "file name style", "FilenamePatternClassic": "classic", "FilenamePatternPretty": "pretty", "FilenamePatternBasic": "basic", - "FilenamePatternNerdy": "nerdy" + "FilenamePatternNerdy": "nerdy", + "FilenameDescription": "classic: default cobalt file name pattern.\npretty: title and info in brackets.\nbasic: title and basic info in brackets.\nnerdy: title and all info in brackets.\n\nsome services don’t support rich file names and always use the classic style.", + "Preview": "preview", + "FilenamePreviewVideoTitle": "Video Title", + "FilenamePreviewAudioTitle": "Audio Title", + "FilenamePreviewAudioAuthor": "Audio Author", + "UrgentFilenameUpdate": "customizable file names!" } } diff --git a/src/localization/languages/ru.json b/src/localization/languages/ru.json index d55d163a..87bd8826 100644 --- a/src/localization/languages/ru.json +++ b/src/localization/languages/ru.json @@ -148,10 +148,16 @@ "SupportNotAffiliated": "кобальт не аффилирован ни с одним из перечисленных выше сервисов.", "SupportMetaNoticeRU": "деятельность meta platforms inc. (владелец instagram) запрещена на территории россии.", "SponsoredBy": "спонсируется", - "FilenamePattern": "шаблон названий файлов", + "FilenameTitle": "стиль названий файлов", "FilenamePatternClassic": "классический", "FilenamePatternPretty": "красивый", "FilenamePatternBasic": "простой", - "FilenamePatternNerdy": "полный" + "FilenamePatternNerdy": "полный", + "FilenameDescription": "классический: стандартный стиль названия файлов кобальта.\nкрасивый: название и инфа в скобках.\nпростой: название и основная инфа в скобках.\nполный: название и вся инфа в скобках.\n\nнекоторые сервисы не поддерживают красивые имена файлов и всегда используют классический стиль.", + "Preview": "превью", + "FilenamePreviewVideoTitle": "Название Видео", + "FilenamePreviewAudioTitle": "Название Аудио", + "FilenamePreviewAudioAuthor": "Автор Аудио", + "UrgentFilenameUpdate": "изменяемые названия файлов!" } } diff --git a/src/modules/changelog/changelog.json b/src/modules/changelog/changelog.json index db2a0f8e..9e32ca56 100644 --- a/src/modules/changelog/changelog.json +++ b/src/modules/changelog/changelog.json @@ -1,5 +1,16 @@ { "current": { + "version": "7.6", + "date": "October 14, 2023", + "title": "customizable file names, instagram stories, and sponsorship!", + "banner": { + "file": "meowthstrong.webp", + "width": 851, + "height": 640 + }, + "content": "as many have (very) often requested, cobalt now lets you pick between several file name format styles!\ngo to settings > other and change it to whichever you like! there's a preview of each style, so you know how exactly files are gonna look like.\n\nif you liked file names the way they were before, don't worry: classic style is still the default :)\n\non a different but not any less important note: cobalt is now sponsored by royalehosting.net!\noverall service performance and stability is gonna be better, but also more content will be possible to download thanks to geniuine server locations. and yes, still no ads or trackers.\n\nif you would like to sponsor cobalt by donating compute power (like royale) or any other way, reach out to me at support@cobalt.tools.\n\nthis update also includes a bunch of other changes, check them out:\n\nservice improvements:\n*; added support for rich file names for youtube, vimeo, soundcloud, rutube, and vk.\n*; added support for instagram stories.\n*; mute and audio dub file name tags don't appear together anymore.\n*; youtube: dub file name tag doesn't appear anymore if audio track is default.\n\ninterface improvements:\n*; added a list of sponsors to about tab. if you host an instance, it's disabled by default, but can be enabled with showSponsors env variable.\n*; about button now opens about tab when no new changelog is available.\n*; fixed download button thickness on ios.\n\nyou now can reach out to cobalt via email for support! it's located in the about tab along with other socials, such as discord.\n\ni hope you enjoy this long-awaited update and have a blissful day :D" + }, + "history": [{ "version": "7.5", "date": "September 16, 2023", "title": "support for twitch clips and rutube!", @@ -9,8 +20,7 @@ "height": 640 }, "content": "hey! this update (finally) adds support for twitch clips and rutube, among other smaller changes.\n\nservice improvements:\n*; added support for twitch clips. no vods, they're unnecessary. just clip whatever you want to download!\n*; added support for rutube in case you ever wanted to download something russian.\n\ninterface improvements:\n*; added a note about cobalt not being affiliated with any supported services.\n*; added a note about meta (the company) in russian.\n*; better russian localization. will keep improving it to make it sound not so robotic over time.\n\nother improvements:\n*; all official servers are now using the docker package. and so should you!\n*; moved the load balancer to poland. requests should be slightly faster now.\n*; minor codebase clean up.\n\nif you're confused about the new domain, read the older changelog! just scroll lower and press \"expand\".\n\ni hope you find this update useful and have a wonderful day :)\n\nbtw, cobalt has a pretty active community server on discord. go to about > support & source code to join!" - }, - "history": [{ + }, { "version": "7.4", "date": "September 9, 2023", "title": "new domain, what's coming in future, bug fixes, and more!", diff --git a/src/modules/emoji.js b/src/modules/emoji.js index 04e053f0..39cd94b8 100644 --- a/src/modules/emoji.js +++ b/src/modules/emoji.js @@ -35,12 +35,16 @@ const names = { "📑": "boring_document", "🧮": "abacus", "😸": "cat_grin", - "📰": "newspaper" + "📰": "newspaper", + "🎞️": "film_frames", + "🎧": "headphone", + "📧": "email" } let sizing = { 18: 0.8, 22: 0.4, 30: 0.7, + 32: 0.8, 48: 0.9, 64: 0.9, 78: 0.9 diff --git a/src/modules/pageRender/page.js b/src/modules/pageRender/page.js index 6f818b6a..8381d424 100644 --- a/src/modules/pageRender/page.js +++ b/src/modules/pageRender/page.js @@ -430,23 +430,40 @@ export default function(obj) { }) + settingsCategory({ name: "filename", - title: t('FilenamePattern'), + title: t('FilenameTitle'), body: switcher({ name: "filenamePattern", items: [{ action: "classic", text: t('FilenamePatternClassic') - }, { - action: "pretty", - text: t('FilenamePatternPretty') }, { action: "basic", text: t('FilenamePatternBasic') + }, { + action: "pretty", + text: t('FilenamePatternPretty') }, { action: "nerdy", text: t('FilenamePatternNerdy') }] }) + + `
+
+ ${emoji('🎞️', 32, 1, 1)} +
+
${t('Preview')}
+
+
+
+
+ ${emoji('🎧', 32, 1, 1)} +
+
${t('Preview')}
+
+
+
+
` + + explanation(t('FilenameDescription')) }) + settingsCategory({ name: "accessibility", @@ -545,8 +562,8 @@ export default function(obj) { diff --git a/src/modules/processing/createFilename.js b/src/modules/processing/createFilename.js index 8c4f832c..ca72bf4c 100644 --- a/src/modules/processing/createFilename.js +++ b/src/modules/processing/createFilename.js @@ -56,7 +56,7 @@ export default function(f, template, isAudioOnly, isAudioMuted) { break; case "nerdy": // Loossemble (루셈블) - 'Sensitive' MV (1080p, h264, ru, youtube, MMK3L4W70g4).mp4 - // Loossemble (루셈블) - 'Sensitive' MV (1080p, h264, ru, youtube, MMK3L4W70g4).mp4 + // Loossemble (루셈블) - 'Sensitive' MV - Loossemble (ru, youtube, MMK3L4W70g4).mp4 filename += `${f.title} `; if (!isAudioOnly) { filename += '('