11// Default catalog configurations
22const defaultCatalogs = [
3- { id : 'watchly.rec' , name : 'Top Picks for You' , enabled : true , minItems : 20 , maxItems : 24 , description : 'Personalized recommendations based on your library' } ,
4- { id : 'watchly.loved' , name : 'More Like' , enabled : true , minItems : 20 , maxItems : 24 , description : 'Recommendations similar to content you explicitly loved' } ,
5- { id : 'watchly.watched' , name : 'Because You Watched' , enabled : true , minItems : 20 , maxItems : 24 , description : 'Recommendations based on your recent watch history' } ,
6- { id : 'watchly.theme' , name : 'Genre & Keyword Catalogs' , enabled : true , minItems : 20 , maxItems : 24 , description : 'Dynamic catalogs based on your favorite genres, keyword, countries and many more. Just like netflix. Example: American Horror, Based on Novel or Book etc.' } ,
3+ { id : 'watchly.rec' , name : 'Top Picks for You' , enabled : true , enabledMovie : true , enabledSeries : true , minItems : 20 , maxItems : 24 , description : 'Personalized recommendations based on your library' } ,
4+ { id : 'watchly.loved' , name : 'More Like' , enabled : true , enabledMovie : true , enabledSeries : true , minItems : 20 , maxItems : 24 , description : 'Recommendations similar to content you explicitly loved' } ,
5+ { id : 'watchly.watched' , name : 'Because You Watched' , enabled : true , enabledMovie : true , enabledSeries : true , minItems : 20 , maxItems : 24 , description : 'Recommendations based on your recent watch history' } ,
6+ { id : 'watchly.theme' , name : 'Genre & Keyword Catalogs' , enabled : true , enabledMovie : true , enabledSeries : true , minItems : 20 , maxItems : 24 , description : 'Dynamic catalogs based on your favorite genres, keyword, countries and many more. Just like netflix. Example: American Horror, Based on Novel or Book etc.' } ,
77] ;
88
99let catalogs = JSON . parse ( JSON . stringify ( defaultCatalogs ) ) ;
@@ -364,6 +364,8 @@ async function fetchStremioIdentity(authKey) {
364364 if ( remote . name ) local . name = remote . name ;
365365 if ( typeof remote . min_items === 'number' ) local . minItems = remote . min_items ;
366366 if ( typeof remote . max_items === 'number' ) local . maxItems = remote . max_items ;
367+ if ( typeof remote . enabled_movie === 'boolean' ) local . enabledMovie = remote . enabled_movie ;
368+ if ( typeof remote . enabled_series === 'boolean' ) local . enabledSeries = remote . enabled_series ;
367369 }
368370 } ) ;
369371 renderCatalogList ( ) ;
@@ -406,7 +408,7 @@ function initializeEmailPasswordLogin() {
406408 }
407409 if ( ! isValidEmail ( email ) ) {
408410 showEmailPwdError ( 'Please enter a valid email address.' ) ;
409- try { emailInput ?. focus ( ) ; } catch ( e ) { }
411+ try { emailInput ?. focus ( ) ; } catch ( e ) { }
410412 return ;
411413 }
412414 try {
@@ -592,10 +594,37 @@ async function initializeFormSubmission() {
592594 // Enforce server policy: min <= 20, max <= 32, and max >= min
593595 minV = Math . max ( 1 , Math . min ( 20 , minV ) ) ;
594596 maxV = Math . max ( minV , Math . min ( 32 , maxV ) ) ;
597+
598+ // Get enabled_movie and enabled_series from toggle buttons
599+ const activeBtn = document . querySelector ( `.catalog-type-btn[data-catalog-id="${ catalogId } "].bg-white` ) ;
600+ let enabledMovie = true ;
601+ let enabledSeries = true ;
602+
603+ if ( activeBtn ) {
604+ const mode = activeBtn . dataset . mode ;
605+ if ( mode === 'movie' ) {
606+ enabledMovie = true ;
607+ enabledSeries = false ;
608+ } else if ( mode === 'series' ) {
609+ enabledMovie = false ;
610+ enabledSeries = true ;
611+ } else {
612+ // 'both' or default
613+ enabledMovie = true ;
614+ enabledSeries = true ;
615+ }
616+ } else {
617+ // Fallback to catalog state
618+ enabledMovie = originalCatalog . enabledMovie !== false ;
619+ enabledSeries = originalCatalog . enabledSeries !== false ;
620+ }
621+
595622 catalogsToSend . push ( {
596623 id : catalogId ,
597624 name : originalCatalog . name ,
598625 enabled : enabled ,
626+ enabled_movie : enabledMovie ,
627+ enabled_series : enabledSeries ,
599628 min_items : minV ,
600629 max_items : maxV ,
601630 } ) ;
@@ -748,6 +777,13 @@ function createCatalogItem(cat, index) {
748777 item . setAttribute ( 'data-index' , index ) ;
749778
750779 const isRenamable = cat . id !== 'watchly.theme' ;
780+
781+ // Determine active mode for toggle buttons
782+ const enabledMovie = cat . enabledMovie !== false ;
783+ const enabledSeries = cat . enabledSeries !== false ;
784+ let activeMode = 'both' ;
785+ if ( enabledMovie && ! enabledSeries ) activeMode = 'movie' ;
786+ else if ( ! enabledMovie && enabledSeries ) activeMode = 'series' ;
751787 item . innerHTML = `
752788 <div class="flex items-start gap-3 sm:items-center sm:gap-4">
753789 <div class="sort-buttons flex flex-col gap-1 flex-shrink-0 mt-0.5 sm:mt-0">
@@ -772,22 +808,17 @@ function createCatalogItem(cat, index) {
772808 </label>
773809 </div>
774810 <div class="catalog-desc hidden sm:block text-xs text-slate-500 mt-2 ml-8 pl-1">${ escapeHtml ( cat . description || '' ) } </div>
775- <div class="mt-3 grid grid-cols-2 gap-3 ml-8">
776- <div class="text-xs text-slate-400 flex items-center gap-2">
777- <span class="whitespace-nowrap">Min items</span>
778- <div class="flex items-center gap-2 bg-neutral-950 border border-white/10 rounded-lg px-2 py-1">
779- <button type="button" class="btn-minus step-btn text-slate-300 hover:text-white hover:bg-white/10 rounded-md px-2 py-1" aria-label="Decrease minimum">−</button>
780- <input type="number" name="min-items" min="1" max="20" value="${ ( cat . minItems ?? 20 ) } " class="stepper-input w-16 bg-transparent outline-none text-white text-sm text-center" />
781- <button type="button" class="btn-plus step-btn text-slate-300 hover:text-white hover:bg-white/10 rounded-md px-2 py-1" aria-label="Increase minimum">+</button>
782- </div>
783- </div>
784- <div class="text-xs text-slate-400 flex items-center gap-2">
785- <span class="whitespace-nowrap">Max items</span>
786- <div class="flex items-center gap-2 bg-neutral-950 border border-white/10 rounded-lg px-2 py-1">
787- <button type="button" class="btn-minus-max step-btn text-slate-300 hover:text-white hover:bg-white/10 rounded-md px-2 py-1" aria-label="Decrease maximum">−</button>
788- <input type="number" name="max-items" min="1" max="32" value="${ ( cat . maxItems ?? 24 ) } " class="stepper-input w-16 bg-transparent outline-none text-white text-sm text-center" />
789- <button type="button" class="btn-plus-max step-btn text-slate-300 hover:text-white hover:bg-white/10 rounded-md px-2 py-1" aria-label="Increase maximum">+</button>
790- </div>
811+ <div class="mt-3 ml-8">
812+ <div class="inline-flex items-center bg-neutral-950 border border-white/10 rounded-lg p-1" role="group" aria-label="Content type selection">
813+ <button type="button" class="catalog-type-btn px-3 py-1.5 text-sm font-medium rounded-md transition-all ${ activeMode === 'both' ? 'bg-white text-black shadow-sm hover:text-black' : 'text-slate-400 hover:text-white' } " data-catalog-id="${ cat . id } " data-mode="both">
814+ Both
815+ </button>
816+ <button type="button" class="catalog-type-btn px-3 py-1.5 text-sm font-medium rounded-md transition-all ${ activeMode === 'movie' ? 'bg-white text-black shadow-sm hover:text-black' : 'text-slate-400 hover:text-white' } " data-catalog-id="${ cat . id } " data-mode="movie">
817+ Movie
818+ </button>
819+ <button type="button" class="catalog-type-btn px-3 py-1.5 text-sm font-medium rounded-md transition-all ${ activeMode === 'series' ? 'bg-white text-black shadow-sm hover:text-black' : 'text-slate-400 hover:text-white' } " data-catalog-id="${ cat . id } " data-mode="series">
820+ Series
821+ </button>
791822 </div>
792823 </div>
793824 ` ;
@@ -801,39 +832,38 @@ function createCatalogItem(cat, index) {
801832 else item . classList . add ( 'opacity-50' ) ;
802833 } ) ;
803834
835+ // Handle movie/series toggle button changes
836+ const allTypeButtons = item . querySelectorAll ( `.catalog-type-btn[data-catalog-id="${ cat . id } "]` ) ;
837+
838+ allTypeButtons . forEach ( btn => {
839+ btn . addEventListener ( 'click' , ( e ) => {
840+ const mode = e . target . dataset . mode ;
841+
842+ // Update state
843+ if ( mode === 'both' ) {
844+ cat . enabledMovie = true ;
845+ cat . enabledSeries = true ;
846+ } else if ( mode === 'movie' ) {
847+ cat . enabledMovie = true ;
848+ cat . enabledSeries = false ;
849+ } else if ( mode === 'series' ) {
850+ cat . enabledMovie = false ;
851+ cat . enabledSeries = true ;
852+ }
853+
854+ // Update UI
855+ allTypeButtons . forEach ( b => {
856+ b . classList . remove ( 'bg-white' , 'text-black' , 'shadow-sm' , 'hover:text-black' ) ;
857+ b . classList . add ( 'text-slate-400' , 'hover:text-white' ) ;
858+ } ) ;
859+ e . target . classList . remove ( 'text-slate-400' , 'hover:text-white' ) ;
860+ e . target . classList . add ( 'bg-white' , 'text-black' , 'shadow-sm' , 'hover:text-black' ) ;
861+ } ) ;
862+ } ) ;
863+
804864 item . querySelector ( '.move-up' ) . addEventListener ( 'click' , ( e ) => { e . preventDefault ( ) ; moveCatalogUp ( index ) ; } ) ;
805865 item . querySelector ( '.move-down' ) . addEventListener ( 'click' , ( e ) => { e . preventDefault ( ) ; moveCatalogDown ( index ) ; } ) ;
806866
807- // keep min/max in model
808- const minEl = item . querySelector ( "input[name='min-items']" ) ;
809- const maxEl = item . querySelector ( "input[name='max-items']" ) ;
810- const clamp = ( v , lo , hi ) => Math . max ( lo , Math . min ( hi , v ) ) ;
811- const sync = ( ) => {
812- let minV = parseInt ( minEl . value || '20' , 10 ) ;
813- let maxV = parseInt ( maxEl . value || '24' , 10 ) ;
814- if ( Number . isNaN ( minV ) ) minV = 20 ;
815- if ( Number . isNaN ( maxV ) ) maxV = 24 ;
816- minV = clamp ( minV , 1 , 20 ) ;
817- maxV = clamp ( maxV , 1 , 32 ) ;
818- if ( maxV < minV ) maxV = minV ;
819- minEl . value = String ( minV ) ;
820- maxEl . value = String ( maxV ) ;
821- cat . minItems = minV ;
822- cat . maxItems = maxV ;
823- } ;
824- minEl . addEventListener ( 'change' , sync ) ;
825- maxEl . addEventListener ( 'change' , sync ) ;
826-
827- const inc = ( el , delta ) => { el . value = String ( ( parseInt ( el . value || '0' , 10 ) || 0 ) + delta ) ; sync ( ) ; } ;
828- const btnMinMinus = item . querySelector ( '.btn-minus' ) ;
829- const btnMinPlus = item . querySelector ( '.btn-plus' ) ;
830- const btnMaxMinus = item . querySelector ( '.btn-minus-max' ) ;
831- const btnMaxPlus = item . querySelector ( '.btn-plus-max' ) ;
832- btnMinMinus . addEventListener ( 'click' , ( ) => inc ( minEl , - 1 ) ) ;
833- btnMinPlus . addEventListener ( 'click' , ( ) => inc ( minEl , + 1 ) ) ;
834- btnMaxMinus . addEventListener ( 'click' , ( ) => inc ( maxEl , - 1 ) ) ;
835- btnMaxPlus . addEventListener ( 'click' , ( ) => inc ( maxEl , + 1 ) ) ;
836-
837867 return item ;
838868}
839869
0 commit comments