1 line
50 KiB
JavaScript
1 line
50 KiB
JavaScript
(function(G,p,r,z,Dt,D,re,T,v,H,rt,ne){"use strict";const h={DEFAULT_APP_NAME:"Music",DEFAULT_TIME_INTERVAL:5,APPLICATION_ID:"1368513179272871956",MIN_UPDATE_INTERVAL:3,MAX_RETRY_ATTEMPTS:3,RETRY_DELAY:5e3,SERVICES:{lastfm:{name:"Last.fm",baseUrl:"https://ws.audioscrobbler.com/2.0",requiresApiKey:!0,requiresToken:!1},librefm:{name:"Libre.fm",baseUrl:"https://libre.fm/api",requiresApiKey:!0,requiresToken:!1},listenbrainz:{name:"ListenBrainz",baseUrl:"https://api.listenbrainz.org/1",requiresApiKey:!1,requiresToken:!0}},DEFAULT_HEADERS:{},DEFAULT_COVER_HASHES:["2a96cbd8b46e442fc41c2b86b821562f"],DEFAULT_SETTINGS:{username:"",apiKey:"",appName:"Music",timeInterval:5,showTimestamp:!0,listeningTo:!0,service:void 0,librefmUsername:"",librefmApiKey:"",listenbrainzUsername:"",listenbrainzToken:""},API_ERROR_CODES:{2:"Invalid service",3:"Invalid method",4:"Invalid format",5:"Invalid parameters",6:"Invalid resource specified",7:"Invalid session key",8:"Invalid API key",9:"Invalid session",10:"Invalid API signature",11:"Service offline",13:"Invalid method signature supplied",16:"Service temporarily unavailable",26:"Suspended API key",29:"Rate limit exceeded"}};function M(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function ve(e,t){for(var n=0;n<t.length;n++){var a=t[n];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,a.key,a)}}function B(e,t,n){return t&&ve(e.prototype,t),n&&ve(e,n),e}const{SET_ACTIVITY:_t}=z.findByProps("SET_ACTIVITY"),nt=z.findByProps("getAssetIds"),at=z.findByStoreName("SelfPresenceStore"),we=z.findByStoreName("UserStore"),Re=function(...e){return console.log("[Activity]",...e)},it=function(...e){return console.error("[Activity] Error:",...e)},q=function(...e){try{console.log("[Activity] Verbose:",...e)}catch{console.log("[Activity] Verbose:",...e)}};function ae(){return Re("Clearing Discord activity"),Ee(null)}function Ee(e){S.pluginStopped&&(q("Plugin is stopped, clearing activity"),ge(),e=null),S.lastActivity=e,q("Dispatching activity update:",e?"Setting activity":"Clearing activity"),r.FluxDispatcher.dispatch({type:"LOCAL_ACTIVITY_UPDATE",activity:e,pid:2312,socketId:"Multi-Service-Scrobbler@Vendetta"}),Re(e?`Activity set: ${e.details} - ${e.state}`:"Activity cleared")}async function st(e,t=h.APPLICATION_ID){if(!e?.length)return q("No assets to fetch"),[];try{q(`Fetching ${e.length} asset(s):`,e);const n=await nt.fetchAssetIds(t,e);return q(`Successfully fetched ${n.length} asset(s)`),n}catch(n){return it("Failed to fetch assets:",n),[]}}const w={};w.componentMountErrors=[],w.componentMountCount=0,w.settingsLoadAttempts=0,w.serviceErrors={lastfm:[],librefm:[],listenbrainz:[]},w.apiCallCount=0,w.connectionAttempts=0,w.lastCredentialValidation={lastfm:!1,librefm:!1,listenbrainz:!1};const Q=function(...e){return console.log("[Debug]",...e)},Se=function(...e){return console.error("[Debug] Error:",...e)};function ie(e,t){w[e]=t,e==="lastError"&&t?Se("Error recorded:",t.message):e==="lastTrack"&&t?Q("Track updated:",`${t.artist} - ${t.name}`):e==="currentService"&&t&&Q("Service changed to:",t)}function Te(){w.apiCallCount=(w.apiCallCount||0)+1,Q(`API call count: ${w.apiCallCount}`)}function J(e,t){w.serviceErrors=w.serviceErrors||{lastfm:[],librefm:[],listenbrainz:[]},w.serviceErrors[e]=w.serviceErrors[e]||[],w.serviceErrors[e].push(`${new Date().toISOString()}: ${t}`),w.serviceErrors[e].length>10&&(w.serviceErrors[e]=w.serviceErrors[e].slice(-10)),Se(`${e} error:`,t)}function Ie(){w.lastSuccessfulUpdate=new Date().toISOString(),Q("Successful update recorded at:",w.lastSuccessfulUpdate)}function Z(e){return Z=Object.setPrototypeOf?Object.getPrototypeOf:function(n){return n.__proto__||Object.getPrototypeOf(n)},Z(e)}function ke(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch{}return(ke=function(){return!!e})()}function ot(e){if(e===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function lt(e){"@swc/helpers - typeof";return e&&typeof Symbol<"u"&&e.constructor===Symbol?"symbol":typeof e}function ct(e,t){return t&&(lt(t)==="object"||typeof t=="function")?t:ot(e)}function se(e,t,n){return t=Z(t),ct(e,ke()?Reflect.construct(t,n||[],Z(e).constructor):t.apply(e,n))}function oe(e,t){return oe=Object.setPrototypeOf||function(a,s){return a.__proto__=s,a},oe(e,t)}function le(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&oe(e,t)}let ce=(function(){function e(){M(this,e)}return B(e,[{key:"log",value:function(...t){console.log(`[${this.getServiceName()}]`,...t)}},{key:"logError",value:function(...t){console.error(`[${this.getServiceName()}] Error:`,...t)}},{key:"logVerbose",value:function(...t){console.log(`[${this.getServiceName()}] Verbose:`,...t)}},{key:"handleError",value:async function(t){this.lastError=t.error||0;const n=this.getErrorMessage(t);throw this.logError(n),J(this.getServiceName().toLowerCase(),n),new Error(`${this.getServiceName()} API Error: ${n}`)}},{key:"getErrorMessage",value:function(t){return t.error&&h.API_ERROR_CODES[t.error]?h.API_ERROR_CODES[t.error]:t.message||t.toString()||"Unknown error"}},{key:"makeRequest",value:async function(t,n={}){try{this.logVerbose(`Making request to: ${t}`),Te();const a=await fetch(t,{...n,headers:{"User-Agent":"Vendetta Multi-Service Scrobbler/3.0.0",...n.headers}});if(!a.ok){const i=new Error(`HTTP ${a.status}: ${a.statusText}`);throw J(this.getServiceName().toLowerCase(),i.message),i}const s=await a.json();return s.error&&await this.handleError(s),this.retryCount=0,s}catch(a){if(this.retryCount++,this.retryCount>h.MAX_RETRY_ATTEMPTS)throw this.retryCount=0,J(this.getServiceName().toLowerCase(),`Max retries exceeded: ${a.message}`),a;return this.logVerbose(`Request failed, retrying (${this.retryCount}/${h.MAX_RETRY_ATTEMPTS})`),await new Promise(function(s){return setTimeout(s,h.RETRY_DELAY)}),this.makeRequest(t,n)}}},{key:"isDefaultCover",value:function(t){return t?h.DEFAULT_COVER_HASHES.some(function(n){return t.includes(n)}):!0}},{key:"processAlbumArt",value:function(t){return!t||this.isDefaultCover(t)?null:t}},{key:"getLastError",value:function(){return this.lastError}},{key:"resetRetryCount",value:function(){this.retryCount=0}}]),e})(),ut=(function(e){le(t,e);function t(){return M(this,t),se(this,t,arguments)}return B(t,[{key:"getServiceName",value:function(){return"Last.fm"}},{key:"logVerbose",value:function(...n){o.verboseLogging&&console.log(`[${this.getServiceName()}] Verbose:`,...n)}},{key:"validateCredentials",value:async function(){try{if(!o.username||!o.apiKey)throw new Error("Username or API key not set");const n=new URLSearchParams({method:"user.getinfo",user:o.username,api_key:o.apiKey,format:"json"}),a=`${h.SERVICES.lastfm.baseUrl}?${n}`;return await this.makeRequest(a),this.log("Credentials validation successful"),!0}catch(n){return this.logError("Credentials validation failed:",n),!1}}},{key:"fetchLatestScrobble",value:async function(){try{if(!o.username||!o.apiKey)throw new Error("Username or API key not set");this.logVerbose("Fetching latest scrobble for user:",o.username);const n=new URLSearchParams({method:"user.getrecenttracks",user:o.username,api_key:o.apiKey,limit:"1",extended:"1",format:"json"}),a=`${h.SERVICES.lastfm.baseUrl}?${n}`,s=(await this.makeRequest(a))?.recenttracks?.track?.[0];if(!s)throw new Error("No tracks found");this.logVerbose("Raw track data:",s);const i=!!s["@attr"]?.nowplaying,u=s.date?.uts?parseInt(s.date.uts):Math.floor(Date.now()/1e3);let g=null,f=null;if(i)try{const L=new URLSearchParams({method:"track.getInfo",track:s.name,artist:s.artist.name,api_key:o.apiKey,format:"json"}),C=`${h.SERVICES.lastfm.baseUrl}?${L}`,A=await this.makeRequest(C);A?.track?.duration&&(g=parseInt(A.track.duration),g>0&&(g=Math.floor(g/1e3),f=u+g))}catch(L){this.logVerbose("Failed to fetch track duration:",L)}const b=this.processAlbumArt(s.image?.find(function(L){return L.size==="large"})?.["#text"]),k={name:s.name,artist:s.artist.name,album:s.album["#text"],albumArt:b,url:s.url,date:s.date?.["#text"]??"now",nowPlaying:i,loved:s.loved==="1",from:u,to:f,duration:g};return this.logVerbose("Processed track:",k),this.log(`${i?"Now playing":"Last played"}:`,`${k.artist} - ${k.name}`),k}catch(n){throw this.logError("Failed to fetch latest scrobble:",n),n}}}]),t})(ce),gt=(function(e){le(t,e);function t(){return M(this,t),se(this,t,arguments)}return B(t,[{key:"getServiceName",value:function(){return"Libre.fm"}},{key:"logVerbose",value:function(...n){o.verboseLogging&&console.log(`[${this.getServiceName()}] Verbose:`,...n)}},{key:"validateCredentials",value:async function(){try{const n=o.librefmUsername,a=o.librefmApiKey;if(!n||!a)throw new Error("Username or API key not set for Libre.fm");const s=new URLSearchParams({method:"user.getinfo",user:n,api_key:a,format:"json"}),i=`${h.SERVICES.librefm.baseUrl}?${s}`;return await this.makeRequest(i),this.log("Credentials validation successful"),!0}catch(n){return this.logError("Credentials validation failed:",n),!1}}},{key:"fetchLatestScrobble",value:async function(){try{const n=o.librefmUsername,a=o.librefmApiKey;if(!n||!a)throw new Error("Username or API key not set for Libre.fm");this.logVerbose("Fetching latest scrobble for user:",n);const s=new URLSearchParams({method:"user.getrecenttracks",user:n,api_key:a,limit:"1",extended:"1",format:"json"}),i=`${h.SERVICES.librefm.baseUrl}?${s}`,u=(await this.makeRequest(i))?.recenttracks?.track?.[0];if(!u)throw new Error("No tracks found");this.logVerbose("Raw track data:",u);const g=!!u["@attr"]?.nowplaying,f=u.date?.uts?parseInt(u.date.uts):Math.floor(Date.now()/1e3);let b=null,k=null;if(g)try{const A=new URLSearchParams({method:"track.getInfo",track:u.name,artist:u.artist.name,api_key:a,format:"json"}),U=`${h.SERVICES.librefm.baseUrl}?${A}`,te=await this.makeRequest(U);te?.track?.duration&&(b=parseInt(te.track.duration),b>0&&(b=Math.floor(b/1e3),k=f+b))}catch(A){this.logVerbose("Failed to fetch track duration:",A)}const L=this.processAlbumArt(u.image?.find(function(A){return A.size==="large"})?.["#text"]),C={name:u.name,artist:u.artist.name,album:u.album["#text"],albumArt:L,url:u.url,date:u.date?.["#text"]??"now",nowPlaying:g,loved:u.loved==="1",from:f,to:k,duration:b};return this.logVerbose("Processed track:",C),this.log(`${g?"Now playing":"Last played"}:`,`${C.artist} - ${C.name}`),C}catch(n){throw this.logError("Failed to fetch latest scrobble:",n),n}}}]),t})(ce),ft=(function(e){le(t,e);function t(){return M(this,t),se(this,t,arguments)}return B(t,[{key:"getServiceName",value:function(){return"ListenBrainz"}},{key:"logVerbose",value:function(...n){o.verboseLogging&&console.log(`[${this.getServiceName()}] Verbose:`,...n)}},{key:"validateCredentials",value:async function(){try{const n=o.listenbrainzUsername,a=o.listenbrainzToken;if(!n)throw new Error("Username not set for ListenBrainz");const s=`${h.SERVICES.listenbrainz.baseUrl}/user/${encodeURIComponent(n)}/listens?count=1`,i={};return a&&(i.Authorization=`Token ${a}`),await this.makeRequest(s,{headers:i}),this.log("Credentials validation successful"),!0}catch(n){return this.logError("Credentials validation failed:",n),!1}}},{key:"fetchLatestScrobble",value:async function(){try{const n=o.listenbrainzUsername,a=o.listenbrainzToken;if(!n)throw new Error("Username not set for ListenBrainz");this.logVerbose("Fetching latest scrobble for user:",n);let s=null;try{const C=`${h.SERVICES.listenbrainz.baseUrl}/user/${encodeURIComponent(n)}/playing-now`,A={};a&&(A.Authorization=`Token ${a}`);const U=await this.makeRequest(C,{headers:A});U.listens&&U.listens.length>0&&(s=U.listens[0],s.playing_now=!0)}catch(C){this.logVerbose("No currently playing track or failed to fetch:",C)}let i;if(s)i=s,this.logVerbose("Using currently playing track");else{const C=`${h.SERVICES.listenbrainz.baseUrl}/user/${encodeURIComponent(n)}/listens?count=1`,A={};a&&(A.Authorization=`Token ${a}`);const U=await this.makeRequest(C,{headers:A});if(!U.listens||U.listens.length===0)throw new Error("No listens found");i=U.listens[0],this.logVerbose("Using latest completed listen")}this.logVerbose("Raw listen data:",i);const u=!!i.playing_now,g=i.listened_at||Math.floor(Date.now()/1e3);let f=null,b=null;i.track_metadata.additional_info?.duration_ms&&(f=Math.floor(i.track_metadata.additional_info.duration_ms/1e3),u&&f>0&&(b=g+f));const k=null;i.track_metadata.additional_info?.release_mbid;const L={name:i.track_metadata.track_name,artist:i.track_metadata.artist_name,album:i.track_metadata.release_name||"",albumArt:k,url:i.track_metadata.additional_info?.origin_url||`https://listenbrainz.org/user/${n}`,date:u?"now":new Date(g*1e3).toISOString(),nowPlaying:u,loved:!1,from:g,to:b,duration:f};return this.logVerbose("Processed track:",L),this.log(`${u?"Now playing":"Last played"}:`,`${L.artist} - ${L.name}`),L}catch(n){throw this.logError("Failed to fetch latest listen:",n),n}}},{key:"getErrorMessage",value:function(n){return n.error?n.error:n.message?n.message:n.toString()||"Unknown error"}}]),t})(ce);const x=(function(){function e(){M(this,e),this.serviceInstances=new Map}return B(e,[{key:"getService",value:function(t){this.serviceInstances||(this.serviceInstances=new Map);const n=t||o.service;if(!n)throw new Error("[ServiceFactory] No service type specified and no default service configured");return this.serviceInstances.has(n)||this.serviceInstances.set(n,this.createService(n)),this.serviceInstances.get(n)}},{key:"getCurrentService",value:function(){return this.getService(o.service)}},{key:"createService",value:function(t){switch(t){case"lastfm":return new ut;case"librefm":return new gt;case"listenbrainz":return new ft;default:throw new Error(`[ServiceFactory] Unknown service type: ${t}`)}}},{key:"clearCache",value:function(){this.serviceInstances?this.serviceInstances.clear():this.serviceInstances=new Map}},{key:"validateCurrentService",value:function(){return this.getCurrentService().validateCredentials()}},{key:"testService",value:async function(t){try{return await this.getService(t).validateCredentials()}catch(n){return console.error(`[ServiceFactory] Failed to test ${t}:`,n),!1}}},{key:"getSupportedServices",value:function(){return["lastfm","librefm","listenbrainz"]}},{key:"getServiceDisplayName",value:function(t){return this.getService(t).getServiceName()}}],[{key:"getInstance",value:function(){return e.instance||(e.instance=new e),e.instance}}]),e})().getInstance();function Le(e){if(!e||e<0)return"0:00";const t=Math.floor(e/3600),n=Math.floor(e%3600/60),a=Math.floor(e%60);return t>0?`${t}:${n.toString().padStart(2,"0")}:${a.toString().padStart(2,"0")}`:`${n}:${a.toString().padStart(2,"0")}`}function Ae(){return Math.floor(Date.now()/1e3)}var dt=(function(e){return e[e.PLAYING=0]="PLAYING",e[e.STREAMING=1]="STREAMING",e[e.LISTENING=2]="LISTENING",e[e.COMPETING=5]="COMPETING",e})(dt||{});const N=function(...e){return console.log("[Scrobble Plugin]",...e)},K=function(...e){return console.error("[Scrobble Plugin] Error:",...e)},$=function(...e){return o.verboseLogging&&console.log("[Scrobble Plugin] Verbose:",...e)},ue=(function(){function e(){M(this,e)}return B(e,[{key:"updateActivity",value:async function(){if(S.pluginStopped){$("Plugin is stopped, skipping update"),this.stopUpdates();return}const t=x.getCurrentService().getServiceName();$(`Fetching latest track from ${t}...`);let n=!1;try{if(o.ignoreList&&o.ignoreList.length>0){const u=at.findActivity(function(g){return g.name?o.ignoreList.some(function(f){return g.name.toLowerCase().includes(f.toLowerCase())}):!1});if(u){$(`Ignored app (${u.name}) is currently playing, clearing activity`),ae();return}}Te();const a=await x.getCurrentService().fetchLatestScrobble();if(ie("lastTrack",a),!a.nowPlaying){$("Track is not currently playing"),ae();return}if(S.lastTrackUrl===a.url){$("Track hasn't changed"),Ie(),this.consecutiveFailures=0;return}n=!0,N(`\u{1F3B5} Track changed: ${a.artist} - ${a.name}`);let s;if(a.nowPlaying&&o.showTimestamp&&a.from){const u=Ae();let g=a.from;if(g<u-3600){if(a.duration&&a.duration>0){const f=Math.min(a.duration*.1,30);g=u-f}else g=u;$("had to estimate start time")}s={start:g*1e3},a.to&&(s.end=a.to*1e3)}$(`\u{1F3AF} Preparing RPC update for: ${a.artist} - ${a.name}`);const i={name:o.appName||h.DEFAULT_APP_NAME,flags:0,type:o.listeningTo?2:0,details:a.name,state:`${a.artist}`,status_display_type:1,application_id:h.APPLICATION_ID};if(i.name.includes("{{")){const u={artist:a.artist,name:a.name,album:a.album,service:t};for(const[g,f]of Object.entries(u))i.name=i.name.replace(new RegExp(`{{${g}}}`,"g"),f||"")}if(s&&(i.timestamps=s,$("Timestamps set:",{start:new Date(s.start).toISOString(),end:s.end?new Date(s.end).toISOString():"none",duration:a.duration?Le(a.duration):"unknown"})),a.album||a.albumArt){const u=a.albumArt?[a.albumArt]:[];let g=(await st(u))[0];if(g){if(i.assets={large_image:g},o.showLargeText){let f="";if(o.showAlbumInTooltip&&a.album&&(f+=`on ${a.album}`),o.showDurationInTooltip&&a.duration){const b=Le(a.duration);f?f+=` \u2022 ${b}`:f=b}f&&(i.assets.large_text=f)}$("Album art set:",g),i.assets.large_text&&$("Tooltip text set:",i.assets.large_text)}else a.album&&o.showLargeText&&o.showAlbumInTooltip&&(i.assets={large_text:`on ${a.album}`})}$("Setting Discord activity:",i),ie("lastActivity",i),await Ee(i),S.lastTrackUrl=a.url,this.currentActivity=i,S.lastActivity=i,this.consecutiveFailures=0,this.lastUpdateTime=Ae(),Ie(),N(`\u2705 RPC updated successfully: ${a.artist} - ${a.name}`)}catch(a){K("Update failed:",a),J(o.service,a.message),this.handleError(a)}}},{key:"handleError",value:function(t){this.consecutiveFailures++,ie("lastError",t),K(`Failure ${this.consecutiveFailures}/${h.MAX_RETRY_ATTEMPTS}:`,t.message),this.consecutiveFailures>=h.MAX_RETRY_ATTEMPTS&&(K("Max retry attempts reached, initiating reconnection..."),this.startReconnection())}},{key:"startReconnection",value:function(){var t=this;this.isReconnecting||(this.isReconnecting=!0,this.stopUpdates(),N("Starting reconnection process..."),this.reconnectTimer=setInterval(function(){N("Attempting to reconnect..."),t.initialize().then(function(){N("Reconnection successful!"),t.stopReconnection()}).catch(function(n){K("Reconnection attempt failed:",n.message)})},h.RETRY_DELAY))}},{key:"stopReconnection",value:function(){try{this.reconnectTimer&&(clearInterval(this.reconnectTimer),this.reconnectTimer=void 0),this.isReconnecting=!1,this.consecutiveFailures=0}catch(t){console.error("[Scrobble Plugin] Reconnection cleanup error:",t)}}},{key:"stopUpdates",value:function(){try{this.updateTimer&&(clearInterval(this.updateTimer),this.updateTimer=void 0)}catch(t){console.error("[Scrobble Plugin] Timer cleanup error:",t)}}},{key:"initialize",value:async function(){var t=this;if(S.pluginStopped)throw new Error("Plugin is stopped");const n=x.getCurrentService().getServiceName();N(`Initializing with ${n}...`);try{if(!await x.validateCurrentService())throw new Error(`Invalid credentials for ${n}`);N(`${n} credentials validated successfully`)}catch(s){throw K(`Failed to validate ${n} credentials:`,s),s}this.stopUpdates(),N("\u{1F3B5} checking for already playing songs..."),await this.updateActivity();const a=Math.max((Number(o.timeInterval)||h.DEFAULT_SETTINGS.timeInterval)*1e3,h.MIN_UPDATE_INTERVAL*1e3);this.updateTimer=setInterval(function(){return t.updateActivity()},a),N(`Update timer started with interval: ${a}ms (${a/1e3}s)`)}},{key:"stop",value:function(){if(!S.pluginStopped){N("Stopping plugin..."),S.pluginStopped=!0;try{this.stopUpdates(),this.stopReconnection(),ae(),N("Plugin stopped successfully")}catch(t){console.error("[Scrobble Plugin] Stop error:",t)}}}},{key:"switchService",value:async function(t){if(S.pluginStopped)return;N(`Switching to ${t}...`);const n=!S.pluginStopped;this.stop();try{x.clearCache(),S.lastTrackUrl=void 0,this.currentActivity=void 0,this.lastUpdateTime=0,n&&(S.pluginStopped=!1,await this.initialize())}catch(a){K("Failed to switch service:",a)}}},{key:"getStatus",value:function(){const t=x.getCurrentService().getServiceName();return{running:!S.pluginStopped,service:t,consecutiveFailures:this.consecutiveFailures,isReconnecting:this.isReconnecting,lastTrackUrl:S.lastTrackUrl,updateInterval:this.updateTimer?"Active":"Inactive"}}}],[{key:"getInstance",value:function(){return e.instance||(e.instance=new e),e.instance}}]),e})().getInstance(),pt=function(){return ue.initialize()},ge=function(){return ue.stop()},ht=function(e){return ue.switchService(e)},{ScrollView:F}=z.findByProps("ScrollView"),{TableRowGroup:E,TableSwitchRow:Ce,TableCheckboxRow:j,Stack:P,TableRow:c}=z.findByProps("TableSwitchRow","TableCheckboxRow","TableRowGroup","Stack","TableRow"),{TextInput:_}=z.findByProps("TextInput");function bt(){D.useProxy(p.plugin.storage);const[,e]=r.React.useReducer(function(n){return n+1},0),t=async function(){T.showToast("Testing Last.fm connection...",v.getAssetIDByName("ClockIcon"));try{await Y.testService("lastfm")?T.showToast("\u2705 Last.fm connection successful!",v.getAssetIDByName("CheckIcon")):T.showToast("\u274C Last.fm connection failed",v.getAssetIDByName("XIcon"))}catch{T.showToast("\u274C Connection error",v.getAssetIDByName("XIcon"))}};return r.React.createElement(F,{style:{flex:1},contentContainerStyle:{padding:10}},r.React.createElement(P,{spacing:8},r.React.createElement(E,{title:"Credentials"},r.React.createElement(P,{spacing:4},r.React.createElement(_,{placeholder:"Last.fm Username",value:l("username"),onChange:function(n){I("username",n),e()},isClearable:!0}),r.React.createElement(_,{placeholder:"Last.fm API Key",value:l("apiKey"),onChange:function(n){I("apiKey",n),e()},secureTextEntry:!0,isClearable:!0}))),r.React.createElement(E,{title:"Actions"},r.React.createElement(c,{label:"Test Connection",subLabel:"Verify your Last.fm credentials",trailing:r.React.createElement(c.Arrow,null),onPress:t}),r.React.createElement(c,{label:"Get API Key",subLabel:"Create a Last.fm API key at last.fm/api/account/create",trailing:r.React.createElement(c.Arrow,null),onPress:async function(){try{await re.Linking.openURL("https://www.last.fm/api/account/create")}catch(n){console.error("Failed to open Last.fm API URL:",n),T.showToast("Failed to open web browser. Please visit: https://www.last.fm/api/account/create",v.getAssetIDByName("XIcon"))}}}))))}function yt(){D.useProxy(p.plugin.storage);const[,e]=r.React.useReducer(function(n){return n+1},0),t=async function(){T.showToast("Testing Libre.fm connection...",v.getAssetIDByName("ClockIcon"));try{await Y.testService("librefm")?T.showToast("\u2705 Libre.fm connection successful!",v.getAssetIDByName("CheckIcon")):T.showToast("\u274C Libre.fm connection failed",v.getAssetIDByName("XIcon"))}catch{T.showToast("\u274C Connection error",v.getAssetIDByName("XIcon"))}};return r.React.createElement(F,{style:{flex:1},contentContainerStyle:{padding:10}},r.React.createElement(P,{spacing:8},r.React.createElement(E,{title:"Credentials"},r.React.createElement(P,{spacing:4},r.React.createElement(_,{placeholder:"Libre.fm Username",value:l("librefmUsername"),onChange:function(n){I("librefmUsername",n),e()},isClearable:!0}),r.React.createElement(_,{placeholder:"Libre.fm API Key",value:l("librefmApiKey"),onChange:function(n){I("librefmApiKey",n),e()},secureTextEntry:!0,isClearable:!0}))),r.React.createElement(E,{title:"Actions"},r.React.createElement(c,{label:"Test Connection",subLabel:"Verify your Libre.fm credentials",trailing:r.React.createElement(c.Arrow,null),onPress:t}),r.React.createElement(c,{label:"Get API Key",subLabel:"Create a Last.fm API key (compatible with Libre.fm)",trailing:r.React.createElement(c.Arrow,null),onPress:async function(){try{await re.Linking.openURL("https://www.last.fm/api/account/create")}catch(n){console.error("Failed to open Last.fm API URL:",n),T.showToast("Failed to open web browser. Please visit: https://www.last.fm/api/account/create",v.getAssetIDByName("XIcon"))}}}))))}function mt(){D.useProxy(p.plugin.storage);const[,e]=r.React.useReducer(function(n){return n+1},0),t=async function(){T.showToast("Testing ListenBrainz connection...",v.getAssetIDByName("ClockIcon"));try{await Y.testService("listenbrainz")?T.showToast("\u2705 ListenBrainz connection successful!",v.getAssetIDByName("CheckIcon")):T.showToast("\u274C ListenBrainz connection failed",v.getAssetIDByName("XIcon"))}catch{T.showToast("\u274C Connection error",v.getAssetIDByName("XIcon"))}};return r.React.createElement(F,{style:{flex:1},contentContainerStyle:{padding:10}},r.React.createElement(P,{spacing:8},r.React.createElement(E,{title:"Credentials"},r.React.createElement(P,{spacing:4},r.React.createElement(_,{placeholder:"ListenBrainz Username",value:l("listenbrainzUsername"),onChange:function(n){I("listenbrainzUsername",n),e()},isClearable:!0}),r.React.createElement(_,{placeholder:"ListenBrainz Token (for private profiles)",value:l("listenbrainzToken"),onChange:function(n){I("listenbrainzToken",n),e()},secureTextEntry:!0,isClearable:!0}))),r.React.createElement(E,{title:"Actions"},r.React.createElement(c,{label:"Test Connection",subLabel:"Verify your ListenBrainz credentials",trailing:r.React.createElement(c.Arrow,null),onPress:t}),r.React.createElement(c,{label:"Get User Token",subLabel:"Get your ListenBrainz user token at listenbrainz.org/settings/",trailing:r.React.createElement(c.Arrow,null),onPress:async function(){try{await re.Linking.openURL("https://listenbrainz.org/settings/")}catch(n){console.error("Failed to open ListenBrainz settings URL:",n),T.showToast("Failed to open web browser. Please visit: https://listenbrainz.org/settings/",v.getAssetIDByName("XIcon"))}}}))))}function vt(){D.useProxy(p.plugin.storage);const[,e]=r.React.useReducer(function(t){return t+1},0);return r.React.createElement(F,{style:{flex:1},contentContainerStyle:{padding:10}},r.React.createElement(P,{spacing:8},r.React.createElement(E,{title:"Activity Display"},r.React.createElement(P,{spacing:4},r.React.createElement(_,{placeholder:`App Name (Default: ${h.DEFAULT_SETTINGS.appName})`,value:l("appName",h.DEFAULT_SETTINGS.appName),onChange:function(t){I("appName",t),e()},isClearable:!0}),r.React.createElement(_,{placeholder:`Update Interval (Default: ${h.DEFAULT_SETTINGS.timeInterval}s)`,value:String(l("timeInterval",h.DEFAULT_SETTINGS.timeInterval)),onChange:function(t){const n=Number(t);n>=h.MIN_UPDATE_INTERVAL&&(I("timeInterval",n),e())},keyboardType:"numeric",isClearable:!0}))),r.React.createElement(E,{title:"About Display Settings"},r.React.createElement(c,{label:"App Name",subLabel:"The name shown in Discord for your activity"}),r.React.createElement(c,{label:"Update Interval",subLabel:"How often the plugin checks for new tracks (in seconds)"}),r.React.createElement(c,{label:"Minimum Interval",subLabel:`The plugin will never check more frequently than ${h.MIN_UPDATE_INTERVAL} seconds`}))))}function wt(){D.useProxy(p.plugin.storage);const[e,t]=r.React.useState(null),[n,a]=r.React.useState(!0),[s,i]=r.React.useState(0),[u,g]=r.React.useState(!1),f={name:"Bohemian Rhapsody",artist:"Queen",artists:["Queen"],album:"A Night at the Opera",image:null,nowPlaying:!0,duration:354,startTime:Math.floor(Date.now()/1e3)-120},b=function(d){return d?Array.isArray(d)?d.filter(function(y){return y&&typeof y=="string"}):d["#text"]?d["#text"].split(/[,;&]/).map(function(y){return y.trim()}).filter(function(y){return y.length>0}):typeof d=="string"?d.split(/[,;&]/).map(function(y){return y.trim()}).filter(function(y){return y.length>0}):["Unknown Artist"]:["Unknown Artist"]},k=function(d){return!d||d.length===0?"Unknown Artist":d.length===1?d[0]:d.length===2?`${d[0]} & ${d[1]}`:`${d.slice(0,-1).join(", ")} & ${d[d.length-1]}`};r.React.useEffect(function(){(async function(){if(!l("username")||!l("apiKey")){t(f),g(!0),a(!1);return}try{a(!0);const d=await(await fetch(`https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=${l("username")}&api_key=${l("apiKey")}&format=json&limit=1`)).json();if(d.recenttracks&&d.recenttracks.track&&d.recenttracks.track.length>0){const y=d.recenttracks.track[0],O=b(y.artist);let tt=180;try{const Ut=O[0]||"Unknown Artist",me=await(await fetch(`https://ws.audioscrobbler.com/2.0/?method=track.getInfo&api_key=${l("apiKey")}&artist=${encodeURIComponent(Ut)}&track=${encodeURIComponent(y.name)}&format=json&username=${l("username")}`)).json();me.track&&me.track.duration&&(tt=Math.floor(me.track.duration/1e3))}catch{console.log("Could not fetch track duration, using default")}t({name:y.name||"Unknown Track",artist:k(O),artists:O,album:y.album?.["#text"]||"Unknown Album",image:y.image?.[2]?.["#text"]||y.image?.[1]?.["#text"]||null,nowPlaying:y["@attr"]?.nowplaying==="true",duration:tt,startTime:y["@attr"]?.nowplaying==="true"?Math.floor(Date.now()/1e3)-60:null}),g(!1)}else t(f),g(!0)}catch(d){console.error("Failed to fetch preview data, using fallback:",d),t(f),g(!0)}finally{a(!1)}})()},[l("username"),l("apiKey")]),r.React.useEffect(function(){if(!e?.nowPlaying||!l("showTimestamp"))return;const d=setInterval(function(){if(e.startTime&&e.duration){const y=Math.floor(Date.now()/1e3)-e.startTime,O=Math.min(y/e.duration,1);i(O)}},1e3);return function(){return clearInterval(d)}},[e,l("showTimestamp")]);const L=function(d){const y=Math.floor(d/60),O=Math.floor(d%60);return`${y}:${O.toString().padStart(2,"0")}`},C=function(){let d="";if(l("showAlbumInTooltip")&&e?.album&&(d+=`on ${e.album}`),l("showDurationInTooltip")&&e?.duration){const y=` \u2022 ${L(e.duration)}`;d?d+=y:d=L(e.duration)}return d||"No tooltip text"},A=function(){return e?.duration?e.nowPlaying?{current:s*e.duration,total:e.duration,progress:s}:{current:e.duration*.3,total:e.duration,progress:.3}:{current:0,total:0,progress:0}},U=l("listeningTo")?"Listening to":"Playing",te=l("appName","Music");if(n)return r.React.createElement(r.ReactNative.View,{style:m.container},r.React.createElement(r.ReactNative.View,{style:m.loadingContent},r.React.createElement(r.ReactNative.View,{style:m.loadingSpinner}),r.React.createElement(r.ReactNative.Text,{style:m.loadingText},"Loading preview...")));if(!e)return r.React.createElement(r.ReactNative.View,{style:m.container},r.React.createElement(r.ReactNative.Text,{style:m.centeredText},"Unable to load preview"));const ye=A(),et=C();return r.React.createElement(r.ReactNative.View,{style:m.previewContainer},r.React.createElement(r.ReactNative.View,{style:m.header},r.React.createElement(r.ReactNative.Text,{style:m.activityType},U," ",te),r.React.createElement(r.ReactNative.Text,{style:m.rpcPreviewText,numberOfLines:1},"RPC Preview")),r.React.createElement(r.ReactNative.View,{style:m.content},r.React.createElement(r.ReactNative.View,{style:m.albumArt},e.image?r.React.createElement(r.ReactNative.Image,{source:{uri:e.image},style:m.albumImage,resizeMode:"cover"}):r.React.createElement(r.ReactNative.Text,{style:m.musicIcon},"\u{1F3B5}")),r.React.createElement(r.ReactNative.View,{style:m.trackInfo},r.React.createElement(r.ReactNative.Text,{style:m.trackName,numberOfLines:1},e.name),r.React.createElement(r.ReactNative.Text,{style:m.artistName,numberOfLines:1},e.artist),l("showLargeText")&&et!=="No tooltip text"&&r.React.createElement(r.ReactNative.Text,{style:m.tooltipText,numberOfLines:1},et),l("showTimestamp")&&e.duration&&r.React.createElement(r.ReactNative.View,{style:m.progressContainer},r.React.createElement(r.ReactNative.Text,{style:m.timeText},L(ye.current)),r.React.createElement(r.ReactNative.View,{style:m.progressBar},r.React.createElement(r.ReactNative.View,{style:[m.progressFill,{width:`${ye.progress*100}%`}]})),r.React.createElement(r.ReactNative.Text,{style:m.timeText},L(ye.total))))))}const m=r.ReactNative.StyleSheet.create({container:{backgroundColor:"#1e1f22",borderRadius:12,padding:16,marginHorizontal:10,marginBottom:16,borderWidth:1,borderColor:"#3a3c41",alignItems:"center",justifyContent:"center",minHeight:120},loadingContent:{alignItems:"center",justifyContent:"center"},loadingSpinner:{width:20,height:20,borderRadius:10,borderWidth:2,borderColor:"#5865f2",borderTopColor:"transparent",marginBottom:8},previewContainer:{backgroundColor:"#1e1f22",borderRadius:12,padding:16,marginHorizontal:10,marginBottom:16,borderWidth:1,borderColor:"#3a3c41"},header:{flexDirection:"row",justifyContent:"space-between",alignItems:"center",marginBottom:12,gap:8},content:{flexDirection:"row",alignItems:"center"},albumArt:{width:80,height:80,backgroundColor:"#2b2d31",borderRadius:12,justifyContent:"center",alignItems:"center",marginRight:16,borderWidth:1,borderColor:"#40444b",overflow:"hidden",flexShrink:0},trackInfo:{flex:1,minWidth:0},progressContainer:{flexDirection:"row",alignItems:"center",gap:8,marginTop:6},progressBar:{flex:1,height:2,backgroundColor:"#2b2d31",borderRadius:3,overflow:"hidden"},progressFill:{height:2,backgroundColor:"#5865f2",borderRadius:3},loadingText:{color:"#949ba4",fontSize:14,fontWeight:"500"},centeredText:{color:"#949ba4",fontSize:14,fontWeight:"500",textAlign:"center"},activityType:{color:"#dbdee1",fontSize:14,fontWeight:"600",flex:1},rpcPreviewText:{color:"#80848e",fontSize:12,fontStyle:"italic",flexShrink:0},albumImage:{width:80,height:80,borderRadius:12},musicIcon:{color:"#80848e",fontSize:32},trackName:{color:"#ffffff",fontSize:16,fontWeight:"700",marginBottom:4},artistName:{color:"#b5bac1",fontSize:14,fontWeight:"500",marginBottom:4},tooltipText:{color:"#80848e",fontSize:12,fontStyle:"italic",marginBottom:6},timeText:{color:"#80848e",fontSize:11,fontWeight:"500",minWidth:35,textAlign:"center"}});function Rt(){D.useProxy(p.plugin.storage);const[,e]=r.React.useReducer(function(i){return i+1},0),t=l("listeningTo",h.DEFAULT_SETTINGS.listeningTo),n=l("showTimestamp",h.DEFAULT_SETTINGS.showTimestamp),a=function(){const i=!t;I("listeningTo",i),!i&&n&&I("showTimestamp",!1),e()},s=function(){t&&(I("showTimestamp",!n),e())};return r.React.createElement(F,{style:{flex:1},contentContainerStyle:{padding:10}},r.React.createElement(wt,null),r.React.createElement(P,{spacing:8},r.React.createElement(E,{title:"RPC Display Options"},r.React.createElement(j,{label:"Show as Listening",subLabel:"Display as 'Listening to' instead of 'Playing'",checked:t,onPress:a}),r.React.createElement(j,{label:"Show Tooltip Text",subLabel:"Show album name and track duration in Discord activity tooltip",checked:l("showLargeText",!0),onPress:function(){const i=l("showLargeText",!0);I("showLargeText",!i),e()}}),!t&&r.React.createElement(c,{label:"Timestamp Unavailable",subLabel:"Enable 'Show as Listening' to use timestamp feature",disabled:!0,dimmed:!0}),r.React.createElement(j,{label:"Show Timestamp",subLabel:"Display track progress and duration",checked:n,onPress:s,disabled:!t}),r.React.createElement(j,{label:"Show Album in Tooltip",subLabel:"Include album name in the tooltip text",checked:l("showAlbumInTooltip",!0),onPress:function(){const i=l("showAlbumInTooltip",!0);I("showAlbumInTooltip",!i),e()}}),r.React.createElement(j,{label:"Show Duration in Tooltip",subLabel:"Include track duration in the tooltip text",checked:l("showDurationInTooltip",!0),onPress:function(){const i=l("showDurationInTooltip",!0);I("showDurationInTooltip",!i),e()}}))))}function Et(){D.useProxy(p.plugin.storage);const[,e]=r.React.useReducer(function(i){return i+1},0),[t,n]=r.React.useState(""),a=function(){if(!t.trim()){T.showToast("Please enter an app name",v.getAssetIDByName("Small"));return}const i=l("ignoreList",[]);i.includes(t.trim())?T.showToast("App already in ignore list",v.getAssetIDByName("Warning")):(I("ignoreList",[...i,t.trim()]),n(""),e(),T.showToast("App added to ignore list",v.getAssetIDByName("Check")))},s=function(i){const u=l("ignoreList",[]);I("ignoreList",u.filter(function(g){return g!==i})),e(),T.showToast("App removed from ignore list",v.getAssetIDByName("Check"))};return r.React.createElement(F,{style:{flex:1},contentContainerStyle:{padding:10}},r.React.createElement(P,{spacing:8},r.React.createElement(E,{title:"Add App to Ignore"},r.React.createElement(P,{spacing:4},r.React.createElement(_,{placeholder:"Enter app name",value:t,onChange:n,isClearable:!0,onSubmitEditing:a,returnKeyType:"done"}))),r.React.createElement(E,null,r.React.createElement(c,{label:"Add to Ignore List",subLabel:"Add the current app name to your ignore list",trailing:r.React.createElement(c.Arrow,null),onPress:a})),l("ignoreList",[]).length>0&&r.React.createElement(E,{title:"Ignored Apps"},l("ignoreList",[]).map(function(i,u){return r.React.createElement(c,{key:u,label:i,trailing:r.React.createElement(r.ReactNative.TouchableOpacity,{onPress:function(){return s(i)},style:{padding:8,backgroundColor:"#ff4d4d",borderRadius:12,width:24,height:24,justifyContent:"center",alignItems:"center"}},r.React.createElement(r.ReactNative.Image,{source:v.getAssetIDByName("TrashIcon"),style:{width:14,height:14,tintColor:"#ffffff"}}))})})),r.React.createElement(E,{title:"About Ignore List"},r.React.createElement(c,{label:"How it Works",subLabel:"When any app in your ignore list is active, your music status will be hidden"}),r.React.createElement(c,{label:"Detection",subLabel:"Apps are detected by their Discord activity name"}),r.React.createElement(c,{label:"Examples",subLabel:"Spotify, YouTube Music, Kizzy, Metrolist, echo"}))))}function St(){D.useProxy(p.plugin.storage);const[,e]=r.React.useReducer(function(t){return t+1},0);return r.React.createElement(F,{style:{flex:1},contentContainerStyle:{padding:10}},r.React.createElement(P,{spacing:8},r.React.createElement(E,{title:"Logging Options"},r.React.createElement(Ce,{label:"Verbose Logging",subLabel:"Enable detailed console logging for debugging",value:l("verboseLogging",h.DEFAULT_SETTINGS.verboseLogging),onValueChange:function(t){I("verboseLogging",t),e()}})),r.React.createElement(E,{title:"Debug Information"},r.React.createElement(c,{label:"Console Logging",subLabel:"Logs are written to the browser/app console when verbose is enabled"}),r.React.createElement(c,{label:"Error Tracking",subLabel:"Connection errors and API failures are automatically logged"})),r.React.createElement(E,{title:"Log Information"},r.React.createElement(c,{label:"API Calls",subLabel:"All API requests are logged when verbose is enabled"}),r.React.createElement(c,{label:"Track Updates",subLabel:"Song changes and RPC updates are logged"}),r.React.createElement(c,{label:"Error Details",subLabel:"Connection errors and retries are logged"}))))}var Pe,Ne,$e,Ue,De,_e,ze,xe,Fe,Ve,Me,Be,Oe,Ke,je,Ge,qe;(Pe=p.plugin.storage).username??(Pe.username=""),(Ne=p.plugin.storage).apiKey??(Ne.apiKey=""),($e=p.plugin.storage).appName??($e.appName="Music"),(Ue=p.plugin.storage).timeInterval??(Ue.timeInterval=5),(De=p.plugin.storage).showTimestamp??(De.showTimestamp=!0),(_e=p.plugin.storage).listeningTo??(_e.listeningTo=!0),(ze=p.plugin.storage).verboseLogging??(ze.verboseLogging=!1),(xe=p.plugin.storage).service??(xe.service="lastfm"),(Fe=p.plugin.storage).librefmUsername??(Fe.librefmUsername=""),(Ve=p.plugin.storage).librefmApiKey??(Ve.librefmApiKey=""),(Me=p.plugin.storage).listenbrainzUsername??(Me.listenbrainzUsername=""),(Be=p.plugin.storage).listenbrainzToken??(Be.listenbrainzToken=""),(Oe=p.plugin.storage).addToSidebar??(Oe.addToSidebar=!0),(Ke=p.plugin.storage).showLargeText??(Ke.showLargeText=!0),(je=p.plugin.storage).ignoreList??(je.ignoreList=[]),(Ge=p.plugin.storage).showAlbumInTooltip??(Ge.showAlbumInTooltip=!0),(qe=p.plugin.storage).showDurationInTooltip??(qe.showDurationInTooltip=!0);const l=function(e,t){return p.plugin.storage[e]??t},I=function(e,t){return p.plugin.storage[e]=t},Y=(function(){function e(){M(this,e)}return B(e,null,[{key:"getServiceDisplayName",value:function(t){switch(t){case"lastfm":return"Last.fm";case"librefm":return"Libre.fm";case"listenbrainz":return"ListenBrainz";default:return"Unknown"}}},{key:"testService",value:async function(t){try{switch(t){case"lastfm":return await this.testLastFmConnection();case"librefm":return await this.testLibreFmConnection();case"listenbrainz":return await this.testListenBrainzConnection();default:return!1}}catch(n){return console.error(`Error testing ${t}:`,n),!1}}},{key:"testLastFmConnection",value:async function(){const t=l("username"),n=l("apiKey");if(!t||!n)return!1;try{return!(await(await fetch(`https://ws.audioscrobbler.com/2.0/?method=user.getinfo&user=${t}&api_key=${n}&format=json`)).json()).error}catch(a){return console.error("Last.fm connection test failed:",a),!1}}},{key:"testLibreFmConnection",value:async function(){const t=l("librefmUsername"),n=l("librefmApiKey");if(!t||!n)return!1;try{return!(await(await fetch(`https://libre.fm/2.0/?method=user.getinfo&user=${t}&api_key=${n}&format=json`)).json()).error}catch(a){return console.error("Libre.fm connection test failed:",a),!1}}},{key:"testListenBrainzConnection",value:async function(){const t=l("listenbrainzUsername"),n=l("listenbrainzToken");if(!t)return!1;try{const a={"Content-Type":"application/json"};return n&&(a.Authorization=`Token ${n}`),(await fetch(`https://api.listenbrainz.org/1/user/${t}/listen-count`,{headers:a})).status===200}catch(a){return console.error("ListenBrainz connection test failed:",a),!1}}}]),e})();function Ye(){D.useProxy(p.plugin.storage);const e=r.NavigationNative.useNavigation(),[,t]=r.React.useReducer(function(s){return s+1},0),n=l("service"),a=function(s){switch(s){case"lastfm":return l("username")&&l("apiKey")?"\u2705 Configured":"\u274C Missing credentials";case"librefm":return l("librefmUsername")&&l("librefmApiKey")?"\u2705 Configured":"\u274C Missing credentials";case"listenbrainz":return l("listenbrainzUsername")?"\u2705 Configured":"\u274C Missing username";default:return"\u2753 Unknown"}};return r.React.createElement(F,{style:{flex:1},contentContainerStyle:{padding:10}},r.React.createElement(P,{spacing:8},r.React.createElement(E,{title:"Active Service"},r.React.createElement(c,{label:"Current Service",subLabel:n?`Using: ${Y.getServiceDisplayName(n)}`:"No service selected"}),["lastfm","librefm","listenbrainz"].map(function(s){return r.React.createElement(j,{key:s,label:Y.getServiceDisplayName(s),subLabel:a(s),checked:n===s,onPress:function(){s!==n&&(I("service",s),t())}})})),r.React.createElement(E,{title:"Service Configuration"},r.React.createElement(c,{label:"Last.fm Settings",subLabel:"Configure Last.fm credentials and options",trailing:r.React.createElement(c.Arrow,null),onPress:function(){return e.push("VendettaCustomPage",{title:"Last.fm Settings",render:bt})}}),r.React.createElement(c,{label:"Libre.fm Settings",subLabel:"Configure Libre.fm credentials and options",trailing:r.React.createElement(c.Arrow,null),onPress:function(){return e.push("VendettaCustomPage",{title:"Libre.fm Settings",render:yt})}}),r.React.createElement(c,{label:"ListenBrainz Settings",subLabel:"Configure ListenBrainz credentials and options",trailing:r.React.createElement(c.Arrow,null),onPress:function(){return e.push("VendettaCustomPage",{title:"ListenBrainz Settings",render:mt})}})),r.React.createElement(E,{title:"Plugin Configuration"},r.React.createElement(c,{label:"Display Settings",subLabel:"Customize app name and update interval",trailing:r.React.createElement(c.Arrow,null),onPress:function(){return e.push("VendettaCustomPage",{title:"Display Settings",render:vt})}}),r.React.createElement(c,{label:"RPC Customization",subLabel:"Customize Discord rich presence display options",trailing:r.React.createElement(c.Arrow,null),onPress:function(){return e.push("VendettaCustomPage",{title:"RPC Customization",render:Rt})}}),r.React.createElement(c,{label:"Ignore List",subLabel:"Configure apps that should hide your status",trailing:r.React.createElement(c.Arrow,null),onPress:function(){return e.push("VendettaCustomPage",{title:"Ignore List Settings",render:Et})}}),r.React.createElement(c,{label:"Logging Settings",subLabel:"Configure logging and debugging options",trailing:r.React.createElement(c.Arrow,null),onPress:function(){return e.push("VendettaCustomPage",{title:"Logging Settings",render:St})}}),r.React.createElement(Ce,{label:"Add to Sidebar",subLabel:"Show plugin in Discord settings",value:l("addToSidebar",!1),onValueChange:function(s){I("addToSidebar",s),t()}})),r.React.createElement(E,{title:"About"},r.React.createElement(c,{label:"Multi Scrobbler",subLabel:"Show off your music status from multiple services"}),r.React.createElement(c,{label:"Author",subLabel:"kmmiio99o"}),r.React.createElement(c,{label:"Version",subLabel:"1.3.2"}))))}const{FormSection:Tt,FormRow:fe}=rt.Forms,{TableRowIcon:It}=z.findByProps("TableRowIcon"),W=window.bunny,We=W?.metro?.findByPropsLazy("getRootNavigationRef"),de=W?.metro?.findByPropsLazy("SETTING_RENDERER_CONFIG"),Xe=W?.metro?.findByPropsLazy("createList"),He=W?.metro?.findByNameLazy("SettingsOverviewScreen",!1);function kt({tabs:e}){const t=r.NavigationNative.useNavigation();return r.React.createElement(fe,{label:e.title(),leading:r.React.createElement(fe.Icon,{source:e.icon}),trailing:r.React.createElement(r.React.Fragment,{},[e.trailing?e.trailing():null,r.React.createElement(fe.Arrow,{key:"arrow"})]),onPress:function(){const n=e.page;t.navigate("VendettaCustomPage",{title:e.title(),render:function(){return r.React.createElement(n)}})}})}function Lt(e,t){try{t.push(H.after("default",W?.metro?.findByPropsLazy(["renderTitle","sections"],!1),function(n,a){const s=ne.findInReactTree(a.props.children,function(i){return i.type?.name==="UserSettingsOverview"});s&&t.push(H.after("render",s.type.prototype,function(i,u){const g=ne.findInReactTree(u.props.children,function(f){return f?.children?.[1]?.type===Tt})?.children;if(g){const f=g.findIndex(function(b){return["BILLING_SETTINGS","PREMIUM_SETTINGS"].includes(b?.props?.label)});g.splice(-~f||4,0,r.React.createElement(kt,{key:e.key,tabs:e}))}}))},!0))}catch(n){p.logger.info("Panel UI patch failed graciously \u{1F494}",n)}}function At(e,t){if(!de||!We){console.warn("[LastFm] Missing required constants for tabs UI patch");return}const n={[e.key]:{type:"pressable",title:e.title,icon:e.icon,IconComponent:e.icon&&function(){return r.React.createElement(It,{source:e.icon})},usePredicate:e.predicate,useTrailing:e.trailing,onPress:function(){const i=We.getRootNavigationRef(),u=e.page;i.navigate("VendettaCustomPage",{title:e.title(),render:function(){return r.React.createElement(u)}})},withArrow:!0}};let a=de.SETTING_RENDERER_CONFIG;Object.defineProperty(de,"SETTING_RENDERER_CONFIG",{enumerable:!0,configurable:!0,get:function(){return{...a,...n}},set:function(i){return a=i}});const s=Symbol("pinToSettings meow meow");try{if(!Xe)return;t.push(H.after("createList",Xe,function(i,u){if(!i[0][s]){i[0][s]=!0;const[g]=i,f=g.sections?.find(function(b){return["Bunny","Revenge","Kettu","Vencore","ShiggyCord"].some(function(k){return b.label===k&&b.title===k})});f?.settings&&(f.settings=[...f.settings,e.key])}}))}catch{if(!He)return;t.push(H.after("default",He,function(i,u){if(!i[0][s]){i[0][s]=!0;const{sections:g}=ne.findInReactTree(u,function(b){return b.props?.sections}).props,f=g?.find(function(b){return["Bunny","Revenge","Kettu","Vencore","ShiggyCord"].some(function(k){return b.label===k&&b.title===k})});f?.settings&&(f.settings=[...f.settings,e.key])}}))}}function Ct(e){const t=[];let n=!1;const a=e.predicate||function(){return!0};return e.predicate=function(){return n?!1:a()},Lt(e,t),At(e,t),t.push(function(){return n=!0}),function(){for(const s of t)s()}}function Qe(){if(!p.plugin.storage.addToSidebar)return console.log("[Multi Scrobbler] Sidebar disabled in settings"),function(){};console.log("[Multi Scrobbler] Patching sidebar using custom patchSettingsPin...");try{const e=Ct({key:"LastFmScrobbler",icon:v.getAssetIDByName("MusicIcon"),title:function(){return"Multi Scrobbler"},predicate:function(){return p.plugin.storage.addToSidebar===!0},page:Ye});return console.log("[Multi Scrobbler] Successfully patched sidebar"),e}catch(e){return console.error("[Multi Scrobbler] Failed to patch sidebar:",e),function(){}}}var Je;const S={pluginStopped:!1,lastActivity:void 0,updateInterval:void 0,lastTrackUrl:void 0},pe=[];let V;const Ze=h.DEFAULT_SETTINGS;for(const e of Object.keys(Ze))p.plugin.storage[e]=p.plugin.storage[e]??Ze[e];(Je=p.plugin.storage).addToSidebar??(Je.addToSidebar=!1);const o=new Proxy(p.plugin.storage,{get(e,t){return e[t]},set(e,t,n){return e[t]=n,!0}});let ee=0;const he=3,Pt=5e3,R=function(...e){return console.log("[Scrobble Plugin]",...e)},be=function(...e){return console.error("[Scrobble Plugin] Error:",...e)};async function X(){try{await pt(),ee=0,R("Plugin initialized successfully")}catch(e){be("Initialization failed:",e),ee++,ee<he?(R(`Retrying connection (${ee}/${he})...`),setTimeout(X,Pt)):be(`Failed to connect after ${he} attempts`)}}async function Nt(){if(!o.service){R("No service selected. Please configure a service in plugin settings.");return}const e=x.getCurrentService().getServiceName(),t=o.service;let n=!1;switch(t){case"lastfm":n=!!(o.username&&o.apiKey);break;case"librefm":n=!!(o.librefmUsername&&o.librefmApiKey);break;case"listenbrainz":n=!!o.listenbrainzUsername;break}if(!n){be(`Missing credentials for ${e}. Please configure in plugin settings.`);return}if(R(`Starting with ${e}...`),we.getCurrentUser())R("Discord is already connected, initializing immediately..."),await X();else{R("Waiting for Discord connection...");const a=function(){we.getCurrentUser()&&(R("Discord connection established"),X(),r.FluxDispatcher.unsubscribe("CONNECTION_OPEN",a))};r.FluxDispatcher.subscribe("CONNECTION_OPEN",a)}}var $t={onLoad(){if(R("Plugin loading..."),S.pluginStopped=!1,!o.service)R("No service configured. Please select a service in plugin settings.");else{const e=x.getCurrentService().getServiceName();R(`Configuration: Service=${e}, Update Interval=${o.timeInterval}s, Verbose=${o.verboseLogging}`)}try{V=Qe(),pe.push(function(){V&&(V(),V=void 0)})}catch(e){R("Sidebar setup failed:",e)}Nt()},onUnload(){R("Plugin unloading..."),S.pluginStopped=!0,pe.forEach(function(e){try{e()}catch{}}),pe.length=0,ge(),R("Plugin unloaded")},async onSettingsUpdate(e){const t=o.service,n=e.service,a=o.addToSidebar,s=e.addToSidebar;if(Object.assign(o,e),R("Settings updated:",Object.keys(e)),a!==s)if(s)try{V=Qe(),R("Sidebar enabled")}catch(i){R("Failed to enable sidebar:",i)}else V&&(V(),V=void 0,R("Sidebar disabled"));t!==n&&n?(R(`Service changed from ${t||"none"} to ${n}`),await ht(n)):!S.pluginStopped&&o.service?(R("Restarting with updated settings..."),await X()):o.service||(R("Service unselected, stopping plugin..."),ge())},onDiscordReconnect(){S.pluginStopped||(R("Discord reconnected, reinitializing plugin..."),X())},settings:Ye};return G.currentSettings=o,G.default=$t,G.pluginState=S,Object.defineProperty(G,"__esModule",{value:!0}),G})({},vendetta,vendetta.metro.common,vendetta.metro,window.React,vendetta.storage,vendetta.metro.common.ReactNative,vendetta.ui.toasts,vendetta.ui.assets,vendetta.patcher,vendetta.ui.components,vendetta.utils);
|