@@ -580,16 +580,20 @@ const fnStr = (f) => {
580580 const exportedMemos = exportedReactObjects [ Symbol . for ( "react.memo" ) ] ;
581581 const exportedForwardRefs = exportedReactObjects [ Symbol . for ( "react.forward_ref" ) ] ;
582582 const exportedMemoFRefs = exportedMemos . filter ( ( m ) => m . type . $$typeof === Symbol . for ( "react.forward_ref" ) ) ;
583- const exposeReactComponentsUI = ( { modules, functionModules, exportedForwardRefs } ) => {
584- const componentNames = Object . keys ( modules . filter ( Boolean ) . find ( ( e ) => e . BrowserDefaultFocusStyleProvider ) ) ;
585- const componentRegexes = componentNames . map ( ( n ) => new RegExp ( `"data-encore-id":(?:[a-zA-Z_$][w$]*\\.){2}${ n } \\b` ) ) ;
586- const componentPairs = [ functionModules . map ( ( f ) => [ f , f ] ) , exportedForwardRefs . map ( ( f ) => [ f . render , f ] ) ]
583+ const exposeReactComponentsUI = ( { modules, functionModules, exportedForwardRefs, exportedMemoFRefs } ) => {
584+ const componentNames = Object . keys ( modules . filter ( Boolean ) . find ( ( e ) => typeof e . BrowserDefaultFocusStyleProvider === "string" ) ) ;
585+ const componentRegexes = componentNames . map ( ( n ) => new RegExp ( `"data-encore-id":(?:[a-zA-Z_$][\\w$]*\\.){2}${ n } \\b` ) ) ;
586+ const componentPairs = [
587+ functionModules . map ( ( f ) => [ f , f ] ) ,
588+ exportedForwardRefs . map ( ( f ) => [ f . render , f ] ) ,
589+ exportedMemoFRefs . map ( ( f ) => [ f . type . render , f ] ) ,
590+ ]
587591 . flat ( )
588592 . map ( ( [ s , f ] ) => [ componentNames . find ( ( _ , i ) => fnStr ( s ) ?. match ( componentRegexes [ i ] ) ) , f ] ) ;
589593
590594 return Object . fromEntries ( componentPairs ) ;
591595 } ;
592- const reactComponentsUI = exposeReactComponentsUI ( { modules, functionModules, exportedForwardRefs } ) ;
596+ const reactComponentsUI = exposeReactComponentsUI ( { modules, functionModules, exportedForwardRefs, exportedMemoFRefs } ) ;
593597
594598 const knownMenuTypes = [ "album" , "show" , "artist" , "track" , "playlist" ] ;
595599 const menus = modules
@@ -616,6 +620,11 @@ const fnStr = (f) => {
616620 } )
617621 . filter ( Boolean ) ;
618622
623+ const menuOverrides = [
624+ [ "PlaylistMenu" , exportedMemos ?. find ( ( m ) => fnStr ( m . type ) . includes ( "labelPlacement" ) && fnStr ( m . type ) . includes ( "menuPlacement" ) ) ] ,
625+ [ "TrackMenu" , exportedMemos ?. find ( ( m ) => fnStr ( m . type ) . includes ( "canSwitchVisuals" ) && fnStr ( m . type ) . includes ( "showCanvasAction" ) ) ] ,
626+ ] . filter ( ( [ , v ] ) => v !== undefined ) ;
627+
619628 const cardTypesToFind = [ "album" , "artist" , "audiobook" , "episode" , "playlist" , "profile" , "show" , "track" ] ;
620629 const cards = [
621630 ...functionModules
@@ -1041,7 +1050,25 @@ body[data-dragging-uri-type] .spicetify-sc-chevronBtn { pointer-events: none; }`
10411050 StoreProvider : functionModules . find ( ( m ) => fnStr ( m ) . includes ( "notifyNestedSubs" ) && fnStr ( m ) . includes ( "serverState" ) ) ,
10421051 ScrollableContainer : _ScrollableContainer ,
10431052 IconComponent : reactComponentsUI . Icon ,
1053+ Navigation : ( ( ) => {
1054+ // Spotify >= 1.2.86
1055+ try {
1056+ const navModuleEntry = Object . entries ( require . m ) . find ( ( [ , v ] ) => fnStr ( v ) . includes ( "navigationalRoot" ) && fnStr ( v ) . includes ( "noLink" ) ) ;
1057+ if ( navModuleEntry ) {
1058+ const Logo = require ( navModuleEntry [ 0 ] ) ?. A ;
1059+ if ( typeof Logo === "function" ) {
1060+ const element = Logo ( { customLink : "/" , noLink : false , hasText : false } ) ;
1061+ if ( element ?. type ) return element . type ;
1062+ }
1063+ }
1064+ // <= Spotify 1.2.85
1065+ return exportedMemoFRefs . find ( ( m ) => fnStr ( m . type ?. render ) . includes ( "navigationalRoot" ) ) ;
1066+ } catch {
1067+ return undefined ;
1068+ }
1069+ } ) ( ) ,
10441070 ...Object . fromEntries ( menus ) ,
1071+ ...Object . fromEntries ( menuOverrides ) ,
10451072 } ,
10461073 ReactHook : {
10471074 DragHandler : functionModules . find ( ( m ) => fnStr ( m ) . includes ( "dataTransfer" ) && fnStr ( m ) . includes ( "data-dragging" ) ) ,
@@ -1078,21 +1105,12 @@ body[data-dragging-uri-type] .spicetify-sc-chevronBtn { pointer-events: none; }`
10781105 } ) ;
10791106
10801107 if ( ! Spicetify . ContextMenuV2 . _context ) Spicetify . ContextMenuV2 . _context = Spicetify . React . createContext ( { } ) ;
1081- if ( ! Spicetify . ReactComponent . Navigation )
1082- Spicetify . ReactComponent . Navigation = exportedMemoFRefs . find ( ( m ) => fnStr ( m . type . render ) . includes ( "navigationalRoot" ) ) ;
10831108
10841109 ( function waitForChunks ( ) {
1085- const listOfComponents = [
1086- "Slider" ,
1087- "Dropdown" ,
1088- "Toggle" ,
1089- // "Cards.Artist",
1090- // "Cards.Audiobook",
1091- // "Cards.Profile",
1092- // "Cards.Show",
1093- // "Cards.Track",
1094- ] ;
1110+ const listOfComponents = [ "Slider" , "Dropdown" , "Toggle" , "Cards.Artist" , "Cards.Audiobook" , "Cards.Profile" , "Cards.Show" , "Cards.Track" ] ;
10951111 if ( listOfComponents . every ( ( component ) => component . split ( "." ) . reduce ( ( o , k ) => o ?. [ k ] , Spicetify . ReactComponent ) !== undefined ) ) return ;
1112+ const currentChunks = Object . entries ( require . m ) ;
1113+
10961114 const cache = Object . keys ( require . m ) . map ( ( id ) => require ( id ) ) ;
10971115 const modules = cache
10981116 . filter ( ( module ) => typeof module === "object" )
@@ -1108,45 +1126,49 @@ body[data-dragging-uri-type] .spicetify-sc-chevronBtn { pointer-events: none; }`
11081126 ? Object . values ( module ) . filter ( ( v ) => typeof v === "function" && ! webpackFactories . has ( v ) )
11091127 : [ ]
11101128 ) ;
1111- const exportedMemos = modules . filter ( ( m ) => m ?. $$typeof === Symbol . for ( "react.memo" ) ) ;
1112- const cardTypesToFind = [ "artist" , "audiobook" , "profile" , "show" , "track" ] ;
1113- // const cards = [
1114- // ...functionModules
1115- // .flatMap((m) => {
1116- // return cardTypesToFind.map((type) => {
1117- // if (m.toString().includes(`featureIdentifier:"${type}"`)) {
1118- // cardTypesToFind.splice(cardTypesToFind.indexOf(type), 1);
1119- // return [type[0].toUpperCase() + type.slice(1), m];
1120- // }
1121- // });
1122- // })
1123- // .filter(Boolean),
1124- // ...modules
1125- // .flatMap((m) => {
1126- // return cardTypesToFind.map((type) => {
1127- // try {
1128- // if (m?.type?.toString().includes(`featureIdentifier:"${type}"`)) {
1129- // cardTypesToFind.splice(cardTypesToFind.indexOf(type), 1);
1130- // return [type[0].toUpperCase() + type.slice(1), m];
1131- // }
1132- // } catch {}
1133- // });
1134- // })
1135- // .filter(Boolean),
1136- // ];
1129+
1130+ const cardTypesToFind = [ "audiobook" , "profile" , "show" ] ;
1131+ const lazyCards = [
1132+ ...functionModules
1133+ . flatMap ( ( m ) => {
1134+ return cardTypesToFind . map ( ( type ) => {
1135+ if ( fnStr ( m ) . includes ( `featureIdentifier:"${ type } "` ) ) {
1136+ cardTypesToFind . splice ( cardTypesToFind . indexOf ( type ) , 1 ) ;
1137+ return [ type [ 0 ] . toUpperCase ( ) + type . slice ( 1 ) , m ] ;
1138+ }
1139+ } ) ;
1140+ } )
1141+ . filter ( Boolean ) ,
1142+ ...modules
1143+ . flatMap ( ( m ) => {
1144+ return cardTypesToFind . map ( ( type ) => {
1145+ try {
1146+ if ( ( m ?. type ? fnStr ( m . type ) : "" ) . includes ( `featureIdentifier:"${ type } "` ) ) {
1147+ cardTypesToFind . splice ( cardTypesToFind . indexOf ( type ) , 1 ) ;
1148+ return [ type [ 0 ] . toUpperCase ( ) + type . slice ( 1 ) , m ] ;
1149+ }
1150+ } catch { }
1151+ } ) ;
1152+ } )
1153+ . filter ( Boolean ) ,
1154+ ] ;
1155+ Object . assign ( Spicetify . ReactComponent . Cards , Object . fromEntries ( lazyCards ) ) ;
11371156
11381157 Spicetify . ReactComponent . Slider = wrapProvider ( functionModules . find ( ( m ) => fnStr ( m ) . includes ( "progressBarRef" ) ) ) ;
11391158 Spicetify . ReactComponent . Toggle = functionModules . find ( ( m ) => fnStr ( m ) . includes ( "onSelected" ) && fnStr ( m ) . includes ( 'type:"checkbox"' ) ) ;
1140- // Object.assign(Spicetify.ReactComponent.Cards, Object.fromEntries(cards));
11411159
11421160 // chunks
1143- const dropdownChunk = chunks . find ( ( [ , value ] ) => fnStr ( value ) . includes ( "dropDown " ) && fnStr ( value ) . includes ( "isSafari" ) ) ;
1161+ const dropdownChunk = currentChunks . find ( ( [ , value ] ) => fnStr ( value ) . includes ( "dropdown-list " ) && fnStr ( value ) . includes ( '"listbox"' ) ) ;
11441162 if ( dropdownChunk ) {
11451163 Spicetify . ReactComponent . Dropdown =
1146- Object . values ( require ( dropdownChunk [ 0 ] ) ) ?. [ 0 ] ?. render ?? Object . values ( require ( dropdownChunk [ 0 ] ) ) . find ( ( m ) => typeof m === "function" ) ;
1164+ Object . values ( require ( dropdownChunk [ 0 ] ) ) . find (
1165+ ( m ) => m ?. $$typeof === Symbol . for ( "react.forward_ref" ) && fnStr ( m . render ) . includes ( "dropdown-list" )
1166+ ) ??
1167+ Object . values ( require ( dropdownChunk [ 0 ] ) ) ?. [ 0 ] ?. render ??
1168+ Object . values ( require ( dropdownChunk [ 0 ] ) ) . find ( ( m ) => typeof m === "function" ) ;
11471169 }
11481170
1149- const toggleChunk = chunks . find ( ( [ , value ] ) => fnStr ( value ) . includes ( "onSelected" ) && fnStr ( value ) . includes ( 'type:"checkbox"' ) ) ;
1171+ const toggleChunk = currentChunks . find ( ( [ , value ] ) => fnStr ( value ) . includes ( "onSelected" ) && fnStr ( value ) . includes ( 'type:"checkbox"' ) ) ;
11501172 if ( toggleChunk && ! Spicetify . ReactComponent . Toggle ) {
11511173 Spicetify . ReactComponent . Toggle = Object . values ( require ( toggleChunk [ 0 ] ) ) [ 0 ] . render ;
11521174 }
@@ -1300,22 +1322,11 @@ body[data-dragging-uri-type] .spicetify-sc-chevronBtn { pointer-events: none; }`
13001322 setTimeout ( bindColorExtractor , 10 ) ;
13011323 return ;
13021324 }
1303- let imageAnalysis = functionModules . find ( ( m ) => m . toString ( ) . match ( / ! [ \w $ ] + \. i s F a l l b a c k | \{ e x t r a c t C o l o r / g) ) ;
1325+ const imageAnalysis = functionModules . find (
1326+ ( m ) => fnStr ( m ) . match ( / ! [ \w $ ] + \. i s F a l l b a c k | \{ e x t r a c t C o l o r / g) || ( fnStr ( m ) . includes ( "extractedColors" ) && fnStr ( m ) . includes ( "imageUris" ) )
1327+ ) ;
13041328 const fallbackPreset = modules . find ( ( m ) => m ?. colorDark ) ;
13051329
1306- // Search chunk in Spotify 1.2.13 or much older because it is impossible to find any distinguishing features
1307- if ( ! imageAnalysis ) {
1308- let chunk = chunks . find (
1309- ( [ , value ] ) =>
1310- ( value . toString ( ) . match ( / [ \w $ ] + \. i s F a l l b a c k / g) || value . toString ( ) . includes ( "colorRaw:" ) ) && value . toString ( ) . match ( / .e x t r a c t C o l o r / g)
1311- ) ;
1312- if ( ! chunk ) {
1313- await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
1314- chunk = chunks . find ( ( [ , value ] ) => value . toString ( ) . match ( / [ \w $ ] + \. i s F a l l b a c k / g) && value . toString ( ) . match ( / .e x t r a c t C o l o r / g) ) ;
1315- }
1316- imageAnalysis = Object . values ( require ( chunk [ 0 ] ) ) . find ( ( m ) => typeof m === "function" ) ;
1317- }
1318-
13191330 Spicetify . extractColorPreset = async ( image ) => {
13201331 const analysis = await imageAnalysis ( Spicetify . GraphQL . Request , image ) ;
13211332 for ( const result of analysis ) {
0 commit comments