diff --git a/plugins/plugins/clean-lyrics/dist/index.js b/plugins/plugins/clean-lyrics/dist/index.js new file mode 100644 index 0000000..17e4a89 --- /dev/null +++ b/plugins/plugins/clean-lyrics/dist/index.js @@ -0,0 +1,22 @@ +var g=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var k=(t,e)=>()=>(t&&(e=t(t=0)),e);var P=(t,e)=>{for(var r in e)g(t,r,{get:e[r],enumerable:!0})},R=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of A(e))!N.call(t,a)&&a!==r&&g(t,a,{get:()=>e[a],enumerable:!(n=I(e,a))||n.enumerable});return t};var _=t=>R(g({},"__esModule",{value:!0}),t);var C={};P(C,{Tracer:()=>p,libTrace:()=>q});import{actions as h}from"@neptune";var p,q,b=k(()=>{"use strict";p=t=>{let e=l=>{let i=(...c)=>{l(t,...c)};return i.withContext=c=>(...y)=>{l(t,c,...y)},i},r=e(console.log),n=e(console.warn),a=e(console.error),s=e(console.debug),o=(l,i,c)=>{let y=d=>{l(d),i({message:`${t} - ${d}`,category:"OTHER",severity:c})};return y.withContext=d=>{let w=l.withContext(d);return m=>{w(m),m instanceof Error&&(m=m.message),i({message:`${t}.${d} - ${m}`,category:"OTHER",severity:c})}},y};return{log:r,warn:n,err:a,debug:s,msg:{log:o(r,h.message.messageInfo,"INFO"),warn:o(n,h.message.messageWarn,"WARN"),err:o(a,h.message.messageError,"ERROR")}}},q=p("[lib]")});b();import{intercept as O}from"@neptune";function T(t,e){let r=document.querySelector('[class*="moreContainer--"]');if(r){let n=document.createElement("button");n.style.height="32px",n.style.width="32px",n.style.display="flex",n.style.border="none",n.style.backgroundColor="transparent",n.style.verticalAlign="middle";let a=document.createElement("img");a.src=e,a.style.backgroundColor="transparent",n.onclick=t,n.appendChild(a),r.appendChild(n)}}b();var L=p("[Clean Lyrics]"),D="https://raw.githubusercontent.com/ItzzExcel/neptune-projects/refs/heads/main/plugins/plugins/clean-lyrics/src/lyrics-styles.css",x,u;function M(){let t=document.querySelector('div[class*="textContainer--"] > a > span');t&&t.addEventListener("DOMSubtreeModified",()=>{setTimeout(()=>{f()},300)})}function S(t){let e=document.createElement("style");return e.type="text/css",e.styleSheet?e.styleSheet.cssText=t:e.appendChild(document.createTextNode(t)),document.head.appendChild(e),e}function U(){u&&u.parentNode&&u.parentNode.removeChild(u)}async function $(t){try{let e=await fetch(t);if(!e.ok)throw new Error(`HTTP error! status: ${e.status}`);return await e.text()}catch(e){return L.msg.err(`Failed to fetch URL: ${e.message}`),null}}var f=function(t=0){t===1&&setTimeout(()=>{f()},2e3);let e=document.querySelector('figure[class*="albumImage"] > div > div > div > img'),r;e&&(r=e.src,r=r.replace(/\d+x\d+/,"1280x1280"),e.src=r);let n=document.querySelector('[class*="nowPlayingContainer"]');if(n&&r){n.querySelectorAll(".corner-image").forEach(l=>l.remove());let s=document.createElement("img");s.src=r,s.className="corner-image",s.style.position="absolute",s.style.left="50%",s.style.top="50%",s.style.transform="translate(-50%, -50%)",s.style.width="75vw",s.style.height="150vh",s.style.objectFit="cover",s.style.zIndex="-1",s.style.filter="blur(100px) brightness(0.6) contrast(1.2) saturate(1)",s.style.animation="spin 35s linear infinite",n.appendChild(s);let o=document.createElement("img");if(o.src=r,o.className="corner-image",o.style.position="absolute",o.style.left="50%",o.style.top="50%",o.style.transform="translate(-50%, -50%)",o.style.width="75vw",o.style.height="150vh",o.style.objectFit="cover",o.style.zIndex="-1",o.style.filter="blur(100px) brightness(0.6) contrast(1.2) saturate(1)",o.style.animation="spin 35s linear infinite",n.appendChild(o),!document.querySelector("#spinAnimation")){let l=document.createElement("style");l.id="spinAnimation",l.textContent=` + @keyframes spin { + from { transform: translate(-50%, -50%) rotate(0deg); } + to { transform: translate(-50%, -50%) rotate(360deg); } + } + `,document.head.appendChild(l)}}},H=function(){[...document.getElementsByClassName("corner-image")].forEach(t=>{t.remove()})},B=["playbackControls/PREFILL_MEDIA_PRODUCT_TRANSITION","playbackControls/MEDIA_PRODUCT_TRANSITION","playbackControls/SEEK","playbackControls/SET_PLAYBACK_STATE","playbackControls/TIME_UPDATE"];(async()=>(x=await $(D),u=S(x)))();M();var v=!1,z=` + [data-test="close-now-playing"], + [data-test="request-fullscreen"], + [role="tablist"], + [data-test="search-popover-container"] { + display: none; + } + + [data-test="footer-player"] { + visibility: hidden; + transition: visibility 0.2s ease-in-out; + } + + [data-test="footer-player"]:hover { + visibility: visible; + } +`,E;function j(){if(document.fullscreenElement)E&&E.remove(),document.exitFullscreen(),v=!1;else{document.documentElement.requestFullscreen();let t=document.querySelector('[data-test="footer-player"]');E=S(z),v=!0}}T(j,"https://cdn.discordapp.com/attachments/1286571643807731783/1343838074441830491/image.png?ex=67beba3c&is=67bd68bc&hm=7474cc11cf3d17ece9dd532ef3ac003273662b95c4fdccacc06ed85a9cbec035&");var K=B.map(t=>O(t,f));function X(){U(),K.forEach(e=>e()),H();let t=document.querySelector('div[class*="textContainer--"] > a > span');t&&t.removeEventListener("DOMSubtreeModified",f)}export{X as onUnload}; diff --git a/plugins/plugins/clean-lyrics/dist/manifest.json b/plugins/plugins/clean-lyrics/dist/manifest.json new file mode 100644 index 0000000..4834e4a --- /dev/null +++ b/plugins/plugins/clean-lyrics/dist/manifest.json @@ -0,0 +1 @@ +{"name":"Clean lyrics","description":"Apple-like version of your full-screen tracks.","author":"itzzexcel@github","hash":"89741f43b17b5ac9f7180e0cf535a979","metafile":{"inputs":{"plugins/clean-lyrics/src/tracer.js":{"bytes":1596,"imports":[{"path":"@neptune","kind":"import-statement","external":true}],"format":"esm"},"plugins/clean-lyrics/src/ui.js":{"bytes":844,"imports":[],"format":"esm"},"plugins/clean-lyrics/src/index.js":{"bytes":7312,"imports":[{"path":"plugins/clean-lyrics/src/tracer.js","kind":"import-statement","original":"./tracer"},{"path":"@neptune","kind":"import-statement","external":true},{"path":"plugins/clean-lyrics/src/ui.js","kind":"import-statement","original":"./ui"},{"path":"plugins/clean-lyrics/src/tracer.js","kind":"require-call","original":"./tracer"}],"format":"esm"}},"outputs":{"plugins/clean-lyrics/dist/index.js":{"imports":[{"path":"@neptune","kind":"import-statement","external":true},{"path":"@neptune","kind":"import-statement","external":true}],"exports":["onUnload"],"entryPoint":"plugins/clean-lyrics/src/index.js","inputs":{"plugins/clean-lyrics/src/tracer.js":{"bytesInOutput":693},"plugins/clean-lyrics/src/index.js":{"bytesInOutput":3598},"plugins/clean-lyrics/src/ui.js":{"bytesInOutput":406}},"bytes":5167}}}} \ No newline at end of file diff --git a/plugins/plugins/clean-lyrics/package.json b/plugins/plugins/clean-lyrics/package.json new file mode 100644 index 0000000..d020e40 --- /dev/null +++ b/plugins/plugins/clean-lyrics/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "canvas-confetti": "^1.6.0", + "neptune-types": "1.0.1" + } +} \ No newline at end of file diff --git a/plugins/plugins/clean-lyrics/plugin.json b/plugins/plugins/clean-lyrics/plugin.json new file mode 100644 index 0000000..03f345d --- /dev/null +++ b/plugins/plugins/clean-lyrics/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "Clean lyrics", + "description": "Apple-like version of your full-screen tracks.", + "author": "itzzexcel@github", + "main": "./src/index.js" +} \ No newline at end of file diff --git a/plugins/plugins/clean-lyrics/pnpm-lock.yaml b/plugins/plugins/clean-lyrics/pnpm-lock.yaml new file mode 100644 index 0000000..1d2e826 --- /dev/null +++ b/plugins/plugins/clean-lyrics/pnpm-lock.yaml @@ -0,0 +1,16 @@ +lockfileVersion: '6.1' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + canvas-confetti: + specifier: ^1.6.0 + version: 1.6.0 + +packages: + + /canvas-confetti@1.6.0: + resolution: {integrity: sha512-ej+w/m8Jzpv9Z7W7uJZer14Ke8P2ogsjg4ZMGIuq4iqUOqY2Jq8BNW42iGmNfRwREaaEfFIczLuZZiEVSYNHAA==} + dev: false diff --git a/plugins/plugins/clean-lyrics/src/index.js b/plugins/plugins/clean-lyrics/src/index.js new file mode 100644 index 0000000..331ecc0 --- /dev/null +++ b/plugins/plugins/clean-lyrics/src/index.js @@ -0,0 +1,213 @@ +require("./tracer") +import { Tracer } from "./tracer"; +import { intercept, store, utils } from "@neptune"; + +import * as ui from "./ui"; + +const trace = Tracer("[Clean Lyrics]"); +const themeUrl = "https://raw.githubusercontent.com/ItzzExcel/neptune-projects/refs/heads/main/plugins/plugins/clean-lyrics/src/lyrics-styles.css"; + +let style; +let styleElement; + +function observeTrackTitle() { + const trackTitleElement = document.querySelector('div[class*="textContainer--"] > a > span'); + if (trackTitleElement) { + trackTitleElement.addEventListener('DOMSubtreeModified', () => { + setTimeout(() => { + onTrackChanged(); + }, 300); + }); + } +} + +function ApplyCSS(style) { + const styleElement = document.createElement("style"); + styleElement.type = "text/css"; + if (styleElement.styleSheet) styleElement.styleSheet.cssText = style; + else styleElement.appendChild(document.createTextNode(style)); + + document.head.appendChild(styleElement); + return styleElement; +} + +function CleanUpCSS() { + if (styleElement && styleElement.parentNode) { + styleElement.parentNode.removeChild(styleElement); + } +} + +async function HttpGet(url) { + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const content = await response.text(); + return content; + } catch (error) { + trace.msg.err(`Failed to fetch URL: ${error.message}`); + return null; + } +} + +const onTrackChanged = function (method = 0) { + // Tu amor tan liminal, tu amor tan liminal + // - Ghouljaboy, 2021 + + if (method === 1) { + setTimeout(() => { + onTrackChanged(); + return; + }, 2000); + } + + let albumImageElement = document.querySelector('figure[class*="albumImage"] > div > div > div > img'); + let albumImageSrc; + + if (albumImageElement) { + albumImageSrc = albumImageElement.src; + + // Set res to 1280x1280 + albumImageSrc = albumImageSrc.replace(/\d+x\d+/, '1280x1280'); + albumImageElement.src = albumImageSrc; + } + + // Setting background to the *="nowPlayingContainer" element + let nowPlayingContainerElement = document.querySelector('[class*="nowPlayingContainer"]'); + if (nowPlayingContainerElement && albumImageSrc) { + // Remove existing corner images if they exist + const existingImages = nowPlayingContainerElement.querySelectorAll('.corner-image'); + existingImages.forEach(img => img.remove()); + + // Create and append center image + const centerImg = document.createElement('img'); + centerImg.src = albumImageSrc; + centerImg.className = 'corner-image'; + centerImg.style.position = 'absolute'; + centerImg.style.left = '50%'; + centerImg.style.top = '50%'; + centerImg.style.transform = 'translate(-50%, -50%)'; + centerImg.style.width = '75vw'; + centerImg.style.height = '150vh'; + centerImg.style.objectFit = 'cover'; + centerImg.style.zIndex = '-1'; + centerImg.style.filter = 'blur(100px) brightness(0.6) contrast(1.2) saturate(1)'; + centerImg.style.animation = 'spin 35s linear infinite'; + nowPlayingContainerElement.appendChild(centerImg); + + const centerImg2 = document.createElement('img'); + centerImg2.src = albumImageSrc; + centerImg2.className = 'corner-image'; + centerImg2.style.position = 'absolute'; + centerImg2.style.left = '50%'; + centerImg2.style.top = '50%'; + centerImg2.style.transform = 'translate(-50%, -50%)'; + centerImg2.style.width = '75vw'; + centerImg2.style.height = '150vh'; + centerImg2.style.objectFit = 'cover'; + centerImg2.style.zIndex = '-1'; + centerImg2.style.filter = 'blur(100px) brightness(0.6) contrast(1.2) saturate(1)'; + centerImg2.style.animation = 'spin 35s linear infinite'; + nowPlayingContainerElement.appendChild(centerImg2); + + // Add keyframe animation if it doesn't exist + if (!document.querySelector('#spinAnimation')) { + const styleSheet = document.createElement('style'); + styleSheet.id = 'spinAnimation'; + styleSheet.textContent = ` + @keyframes spin { + from { transform: translate(-50%, -50%) rotate(0deg); } + to { transform: translate(-50%, -50%) rotate(360deg); } + } + `; + document.head.appendChild(styleSheet); + } + } +} + +const cleanUpDynamicArt = function () { + [...document.getElementsByClassName("corner-image")].forEach((element) => { + element.remove(); + }); +} + +const PLAYBACK_EVENTS = [ + "playbackControls/PREFILL_MEDIA_PRODUCT_TRANSITION", + "playbackControls/MEDIA_PRODUCT_TRANSITION", + "playbackControls/SEEK", + "playbackControls/SET_PLAYBACK_STATE", + "playbackControls/TIME_UPDATE", + // "playbackControls/" +]; + +/// Load +// Since HttpGet is async, we need to await its result +(async () => { + style = await HttpGet(themeUrl); + styleElement = ApplyCSS(style); +})(); + +// Self-explained +observeTrackTitle(); + +// Create toggle custom fullscreen button +let isFullscreen = false; +// const hiddableElements = [ +// '[data-test="close-now-playing"]', +// '[data-test="request-fullscreen"]', +// '[role="tablist"]', +// '[data-test="search-popover-container"]' +// ] +const elementsHiddenStyle = +` + [data-test="close-now-playing"], + [data-test="request-fullscreen"], + [role="tablist"], + [data-test="search-popover-container"] { + display: none; + } + + [data-test="footer-player"] { + visibility: hidden; + transition: visibility 0.2s ease-in-out; + } + + [data-test="footer-player"]:hover { + visibility: visible; + } +` + +var _elementsHiddenStyleTag; + +function ToggleCustomFullscreen() { + if (!document.fullscreenElement) { + document.documentElement.requestFullscreen(); + _elementsHiddenStyleTag = ApplyCSS(elementsHiddenStyle); + isFullscreen = true; + } else { + if (_elementsHiddenStyleTag) { + _elementsHiddenStyleTag.remove(); + } + document.exitFullscreen(); + isFullscreen = false; + } +} + +ui.NewPlayerButton(ToggleCustomFullscreen, "https://cdn.discordapp.com/attachments/1286571643807731783/1343838074441830491/image.png?ex=67beba3c&is=67bd68bc&hm=7474cc11cf3d17ece9dd532ef3ac003273662b95c4fdccacc06ed85a9cbec035&") + + +const unsubscribeFunctions = PLAYBACK_EVENTS.map(event => + intercept(event, onTrackChanged) +); + +export function onUnload() { + CleanUpCSS(); + unsubscribeFunctions.forEach(unsubscribe => unsubscribe()); + cleanUpDynamicArt(); + + const trackTitleElement = document.querySelector('div[class*="textContainer--"] > a > span'); + if (trackTitleElement) { + trackTitleElement.removeEventListener('DOMSubtreeModified', onTrackChanged); + } +} \ No newline at end of file diff --git a/plugins/plugins/clean-lyrics/src/lyrics-styles.css b/plugins/plugins/clean-lyrics/src/lyrics-styles.css new file mode 100644 index 0000000..8642f29 --- /dev/null +++ b/plugins/plugins/clean-lyrics/src/lyrics-styles.css @@ -0,0 +1,70 @@ +/* Credits to https://github.com/surfbryce for the fonts */ +@font-face { + font-family: "AbyssFont"; + font-weight: 400; + src: url("https://excel.lexploits.top/extra/tidal/LyricsRegular.woff2") format("woff2"); +} + +@font-face { + font-family: "AbyssFont"; + font-weight: 500; + src: url("https://excel.lexploits.top/extra/tidal/LyricsMedium.woff2") format("woff2"); +} + +@font-face { + font-family: "AbyssFont"; + font-weight: 600; + src: url("https://excel.lexploits.top/extra/tidal/LyricsSemibold.woff2") format("woff2"); +} + +@font-face { + font-family: "AbyssFont"; + font-weight: 700; + src: url("https://excel.lexploits.top/extra/tidal/LyricsBold.woff2") format("woff2"); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +[data-test="now-playing-track-title"] { + text-shadow: 0 0 1px #fff, 0 0 30px #fff; +} + +[data-current] { + transition: text-shadow 0.7s ease-in-out, text-shadow 0.7s ease-in-out padding 0.7s ease-in-out; +} + +[data-current="true"] { + text-shadow: 0 0 2px #fff, 0 0 20px #fff; + padding-left: 20px; + transition-duration: 0.7s; +} + +[data-current="false"] { + text-shadow: 0 0 0px transparent, 0 0 0px transparent; + transition-duration: 0.25s; + color: rgba(128, 128, 128, 0.4); + +} + +[data-current="false"]:hover { + text-shadow: 0 0 2px lightgray, 0 0 20px lightgray; + color: lightgray; + padding-left: 20px; + transition-duration: 0.7s; +} + +[class^="lyricsContainer"]>div>div>span { + margin-bottom: 2rem; + opacity: 1; + font-family: "AbyssFont", system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + font-weight: 700; + font-size: 43px !important; +} + +[class^="lyricsProvider"] { + visibility: hidden; +} + +[class^="syncButton"] { + margin-bottom: 10px; +} \ No newline at end of file diff --git a/plugins/plugins/clean-lyrics/src/tracer.js b/plugins/plugins/clean-lyrics/src/tracer.js new file mode 100644 index 0000000..f18bcd3 --- /dev/null +++ b/plugins/plugins/clean-lyrics/src/tracer.js @@ -0,0 +1,55 @@ +// Based on https://github.com/Inrixia/neptune-plugins/blob/3d28c9ea3252782da830698032dbb49dbe5b9fd6/plugins/_lib/trace.ts +// Credits to inrixia + +import { actions } from "@neptune"; + +export const Tracer = (source) => { + const createLogger = (logger) => { + const _logger = (...data) => { + logger(source, ...data); + return undefined; + }; + _logger.withContext = (context) => (...data) => { + logger(source, context, ...data); + return undefined; + }; + return _logger; + }; + + const log = createLogger(console.log); + const warn = createLogger(console.warn); + const err = createLogger(console.error); + const debug = createLogger(console.debug); + + const createMessager = (logger, messager, severity) => { + const _messager = (message) => { + logger(message); + messager({ message: `${source} - ${message}`, category: "OTHER", severity }); + return undefined; + }; + _messager.withContext = (context) => { + const loggerWithContext = logger.withContext(context); + return (message) => { + loggerWithContext(message); + if (message instanceof Error) message = message.message; + messager({ message: `${source}.${context} - ${message}`, category: "OTHER", severity }); + return undefined; + }; + }; + return _messager; + }; + + return { + log, + warn, + err, + debug, + msg: { + log: createMessager(log, actions.message.messageInfo, "INFO"), + warn: createMessager(warn, actions.message.messageWarn, "WARN"), + err: createMessager(err, actions.message.messageError, "ERROR"), + }, + }; +}; + +export const libTrace = Tracer("[lib]"); \ No newline at end of file diff --git a/plugins/plugins/clean-lyrics/src/ui.js b/plugins/plugins/clean-lyrics/src/ui.js new file mode 100644 index 0000000..71997fb --- /dev/null +++ b/plugins/plugins/clean-lyrics/src/ui.js @@ -0,0 +1,21 @@ +export function NewPlayerButton(callback, iconSource) { + let buttonContainer = document.querySelector("[class*=\"moreContainer--\"]"); + if (buttonContainer) { + let buttonElement = document.createElement("button"); + buttonElement.style.height = "32px" + buttonElement.style.width = "32px" + buttonElement.style.display = "flex"; + buttonElement.style.border = "none"; + buttonElement.style.backgroundColor = "transparent"; + buttonElement.style.verticalAlign = "middle"; + + let buttonIcon = document.createElement("img"); + buttonIcon.src = iconSource; + buttonIcon.style.backgroundColor = "transparent"; + + buttonElement.onclick = callback; + + buttonElement.appendChild(buttonIcon); + buttonContainer.appendChild(buttonElement); + } +} \ No newline at end of file