// ==UserScript== // @name Enable Picture-in-picture (PiP) & more for Weverse.io // @namespace Weverse Enhancements // @match *://weverse.io/*/live/* // @include *://weverse.io/*/live* // @include *://weverse.io* // @grant GM_getValue // @grant GM_setValue // @version 4.0 // @author jho / @jhooo_o // @run-at document-end // @description Enable Picture-in-picture (PiP), Auto translate artists' posts on weverse.io // @license MIT // @icon https://cdn-v2pstatic.weverse.io/wev_web_fe/assets/1.0.0/icons/logo192.png // @downloadURL none // ==/UserScript== let auto_translate_status = GM_getValue('storage_translator', false); const pip_btn_icon = ` Toggle Picture-in-picture `; const trans_button_active = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 38 38'%3E%3Ccircle fill='%2363D5A9' cx='27.3' cy='9.9' r='4.2'/%3E%3Cpath d='M32.9,28.3l-1.9-4c0,0,0,0,0,0l-3.8-8.1c-1.1,0-2.1-0.3-3-0.8l-3.7,7.9c-0.6-0.1-1.3-0.3-2-0.6c-1-0.4-2-1-2.7-1.5l0,0 c-0.1,0-0.1-0.1-0.2-0.1c1.1-1.1,1.9-2.4,2.5-3.9c0.5-1.3,0.8-2.6,0.9-4H22c-0.3-0.6-0.6-1.2-0.8-1.8h-6v-2c0-0.5-0.4-0.9-0.9-0.9 c-0.5,0-0.9,0.4-0.9,0.9v2h-7c-0.5,0-0.9,0.4-0.9,0.9c0,0.5,0.4,0.9,0.9,0.9h7.9l0,0l0,0h3c-0.1,1.1-0.4,2.2-0.8,3.3 c-0.5,1.3-1.3,2.4-2.2,3.4c-1.1-1.1-1.9-2.4-2.4-3.9c-0.2-0.5-0.7-0.7-1.2-0.6c-0.5,0.2-0.7,0.7-0.6,1.2c0.6,1.7,1.5,3.2,2.8,4.5 c-0.7,0.5-1.5,1-2.3,1.3c-1.3,0.5-2.7,0.8-4.2,0.8c-0.5,0-0.9,0.4-0.9,0.9c0,0.5,0.4,0.9,0.9,0.9c1.7,0,3.3-0.3,4.9-1 c1.1-0.4,2.1-1,3-1.8c0.2,0.1,0.3,0.3,0.5,0.4c0.8,0.6,1.9,1.2,3.1,1.7c0.6,0.3,1.2,0.5,1.9,0.6l-1.6,3.4c-0.2,0.5,0,1,0.4,1.2 s1,0,1.2-0.4l1.6-3.5h8.1l1.6,3.5c0.2,0.5,0.8,0.7,1.2,0.4S33.1,28.8,32.9,28.3z M22.4,23.8l3.2-6.9l3.2,6.9H22.4z'/%3E%3C/svg%3E%0A")`; const trans_button_not_active = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 38 38'%3E%3Ccircle fill='%23d5639a' cx='27.3' cy='9.9' r='4.2'/%3E%3Cpath d='M32.9,28.3l-1.9-4c0,0,0,0,0,0l-3.8-8.1c-1.1,0-2.1-0.3-3-0.8l-3.7,7.9c-0.6-0.1-1.3-0.3-2-0.6c-1-0.4-2-1-2.7-1.5l0,0 c-0.1,0-0.1-0.1-0.2-0.1c1.1-1.1,1.9-2.4,2.5-3.9c0.5-1.3,0.8-2.6,0.9-4H22c-0.3-0.6-0.6-1.2-0.8-1.8h-6v-2c0-0.5-0.4-0.9-0.9-0.9 c-0.5,0-0.9,0.4-0.9,0.9v2h-7c-0.5,0-0.9,0.4-0.9,0.9c0,0.5,0.4,0.9,0.9,0.9h7.9l0,0l0,0h3c-0.1,1.1-0.4,2.2-0.8,3.3 c-0.5,1.3-1.3,2.4-2.2,3.4c-1.1-1.1-1.9-2.4-2.4-3.9c-0.2-0.5-0.7-0.7-1.2-0.6c-0.5,0.2-0.7,0.7-0.6,1.2c0.6,1.7,1.5,3.2,2.8,4.5 c-0.7,0.5-1.5,1-2.3,1.3c-1.3,0.5-2.7,0.8-4.2,0.8c-0.5,0-0.9,0.4-0.9,0.9c0,0.5,0.4,0.9,0.9,0.9c1.7,0,3.3-0.3,4.9-1 c1.1-0.4,2.1-1,3-1.8c0.2,0.1,0.3,0.3,0.5,0.4c0.8,0.6,1.9,1.2,3.1,1.7c0.6,0.3,1.2,0.5,1.9,0.6l-1.6,3.4c-0.2,0.5,0,1,0.4,1.2 s1,0,1.2-0.4l1.6-3.5h8.1l1.6,3.5c0.2,0.5,0.8,0.7,1.2,0.4S33.1,28.8,32.9,28.3z M22.4,23.8l3.2-6.9l3.2,6.9H22.4z'/%3E%3C/svg%3E%0A")`; const trans_status_btn_style = document.createElement("style"); trans_status_btn_style.setAttribute('type', 'text/css'); trans_status_btn_style.innerHTML = ` .HeaderView_translate_status:hover { background-color: rgba(0,0,0,.04); border-radius: 50%; margin: -12px; padding: 12px; } .HeaderView_translate_status:before { content: ""; background-image: var(--trans_background); display: block; height: 38px; width: 38px; } .HeaderView_translate_status:focus-visible { outline: none; } `; document.head.appendChild(trans_status_btn_style); const mutation_config = { childList: true, subtree: true }; function toggle_pip() { const video_player_wrapper = document.querySelector(".webplayer-internal-source-wrapper"); const video_player = document.querySelector(".webplayer-internal-video"); if (document.pictureInPictureElement) { document.exitPictureInPicture() .then(() => console.log("██████████ Exited Picture-in-picture mode")) .catch(error => console.error("██████████ Error exiting PiP mode:", error)); } else { video_player.requestPictureInPicture() .then(() => console.log("██████████ Entered Picture-in-picture mode")) .catch(error => console.error("██████████ Error entering PiP mode:", error)); } } function toggle_auto_translate() { const translateStatusBtn = document.querySelector(".HeaderView_translate_status"); if (auto_translate_status) { auto_translate_status = false; translateStatusBtn.style.setProperty("--trans_background", auto_translate_status ? trans_button_active : trans_button_not_active); const status = auto_translate_status ? "Auto translator for artists' posts is enabled" : "Auto translator for artists' posts is disabled"; translateStatusBtn.setAttribute('title', status); GM_setValue('storage_translator', false); popup_alert(auto_translate_status, status); } else { auto_translate_status = true; translateStatusBtn.style.setProperty("--trans_background", auto_translate_status ? trans_button_active : trans_button_not_active); const status = auto_translate_status ? "Auto translator for artists' posts is enabled" : "Auto translator for artists' posts is disabled"; translateStatusBtn.setAttribute('title', status); GM_setValue('storage_translator', true); popup_alert(auto_translate_status, status); } } function popup_alert(auto_translate_status, status) { const popup_elem = document.createElement("div"); popup_elem.classList.add('translate_popup'); popup_elem.innerText = status; if (auto_translate_status) { background = 'linear-gradient(135deg,#e0fdfc,#e3fff5)'; color = '#08ccca'; } else { background = 'linear-gradient(135deg,#fde0e0,#ffe3e3)'; color = '#c36666'; } const popup_elem_styles = { color: color, position: 'fixed', top: '10px', margin: 'auto', border: `2px solid ${color}20`, borderRadius: '14px', backgroundColor: 'white', background: background, fontWeight: '900', padding: '20px 20px', opacity: 0, right: 0, left: 0, width: 'fit-content', boxShadow: '0 1px 12px #3e3f4e05, 0 5px 24px #3e3f4e1a', zIndex: 3999, transition: 'opacity 0.5s ease-in-out' }; Object.assign(popup_elem.style, popup_elem_styles); document.body.appendChild(popup_elem); void popup_elem.offsetWidth; popup_elem.style.opacity = 1; setTimeout(() => { popup_elem.style.opacity = 0; setTimeout(() => { document.body.removeChild(popup_elem); }, 500); }, 2000); } function append_elem(changes, observer) { // Append Picture-in-picture const video_player = document.querySelector(".webplayer-internal-video"); if (video_player) { if (video_player.hasAttribute("disablepictureinpicture")) { video_player.removeAttribute("disablepictureinpicture"); console.log("██████████ Picture-in-picture is re-enabled."); } const locations = document.querySelectorAll(".pzp-pc__bottom-buttons-right, .pzp-mobile-bottom.pzp-mobile__bottom"); if (locations.length > 0) { const pip_btn_exist = Array.from(locations).some(location => location.querySelector(".pzp-button-pip")); if (!pip_btn_exist) { const btn = document.createElement("button"); btn.setAttribute("aria-label", "Toggle Picture-in-picture"); const btn_class_names = locations[0].classList.contains("pzp-mobile-bottom") ? ["pzp-button", "pzp-setting-button", "pzp-mobile__setting-button", "pzp-button-pip"] : ["pzp-button", "pzp-button-pip", "pzp-pc-viewmode-button", "pzp-pc__viewmode-button", "pzp-pc-ui-button"]; btn_class_names.forEach(item => btn.classList.add(item)); btn.innerHTML = pip_btn_icon; btn.addEventListener("click", () => toggle_pip()); locations[0].insertBefore(btn, locations[0].lastChild); } } } // Append Translation Button's Toggle const nav_bar = document.querySelector('div[class*="HeaderView_action__"]'); if (nav_bar) { if (!nav_bar.querySelector('div[class*="HeaderView_action__"] .HeaderView_translate_status[trans_btn_added]')) { const trans_status_btn = document.createElement("button"); trans_status_btn.classList.add("HeaderView_translate_status"); trans_status_btn.setAttribute("type", "button"); trans_status_btn.setAttribute("trans_btn_added", ""); trans_status_btn.style.setProperty("--trans_background", auto_translate_status ? trans_button_active : trans_button_not_active); trans_status_btn.setAttribute('title', auto_translate_status ? "Auto translator for artists' posts is active" : "Auto translator for artists' posts is not active"); trans_status_btn.addEventListener("click", () => toggle_auto_translate()); nav_bar.insertBefore(trans_status_btn, nav_bar.children[nav_bar.children.length - 2]); if (auto_translate_status) { start_auto_translate() } } } } function start_auto_translate() { console.log("Auto translate is activated."); auto_translate_observer = new MutationObserver(auto_translate_f); auto_translate_observer .observe(document, mutation_config); } function stop_auto_translate() { console.log("Auto translate is deactivated."); autoTranslateArtist = false; GM_setValue('storage_translator', false); auto_translate_observer .disconnect(); } function auto_translate_f(changes, observer) { const current_url = window.location.href; const valid_url_pattern = /^https:\/\/weverse\.io\/\w+\/(artist|feed|fanpost|moment).*/; if (valid_url_pattern.test(current_url)) { const artist_posts = document.querySelectorAll('div[class^="PostHeaderView_header_wrap"]:has(em.BadgeView_badge__sSoG5.BadgeView_-artist__jr7QG) button.TranslationButtonView_translation_button__VSa80, button.TranslationButtonView_translation_button__VSa80.TranslationButtonView_style-artist__VoK6K, button.TranslationButtonView_translation_button__VSa80.TranslationButtonView_style-moment__BuR5U'); if (artist_posts) { const not_auto_clicked_buttons = Array.from(artist_posts).filter(button => button.getAttribute("aria-pressed") === "false"); not_auto_clicked_buttons.forEach(button => { const is_moment = /^https:\/\/weverse\.io\/\w+\/moment.*/; if (is_moment.test(current_url)) { console.log('This is a moment page.'); const moment_video = document.querySelector('.webplayer-internal-video'); if (moment_video && !moment_video.paused) { button.click(); button.setAttribute("auto_clicked", ""); button.setAttribute("aria-pressed", "true"); setTimeout(() => { const vtt_element = document.querySelector('div[class*=MomentViewerControlUiView_translation_result'); // Replace 'yourElementId' with the actual ID of the created element if (vtt_element) { console.log('Subtitle activated!'); } else { button.click(); button.setAttribute("auto_clicked", ""); button.setAttribute("aria-pressed", "true"); console.log('retrying...'); } }, 300); } else { console.log('video paused.') } } else { console.log('2', not_auto_clicked_buttons) button.click(); button.setAttribute("auto_clicked", ""); button.setAttribute("aria-pressed", "true"); } }); } } } // Observers const elem_appender_observer = new MutationObserver(append_elem); elem_appender_observer.observe(document, mutation_config);