2022-03-07 02:02:06 +01:00
< template >
2022-03-14 01:34:31 +01:00
< div class = "page" : class = "streamLibraryItem ? 'streaming' : ''" >
2022-05-14 00:40:43 +02:00
< app -book -shelf -toolbar page = "podcast-search" / >
2022-05-29 18:46:45 +02:00
2022-09-17 22:23:33 +02:00
< div id = "bookshelf" class = "w-full overflow-y-auto px-2 py-6 sm:px-4 md:p-12 relative" >
2022-05-29 18:46:45 +02:00
< div class = "w-full max-w-4xl mx-auto flex" >
< form @submit.prevent ="submit" class = "flex flex-grow" >
2023-02-25 00:31:16 +01:00
< ui -text -input v -model = " searchInput " type = "search" :disabled ="processing" placeholder = "Enter search term or RSS feed URL" class = "flex-grow mr-2 text-sm md:text-base" / >
2022-11-09 00:10:08 +01:00
< ui -btn type = "submit" :disabled ="processing" class = "hidden md:block" > { { $strings . ButtonSubmit } } < / u i - b t n >
< ui -btn type = "submit" :disabled ="processing" class = "block md:hidden" small > { { $strings . ButtonSubmit } } < / u i - b t n >
2022-05-14 00:40:43 +02:00
< / form >
2022-11-09 00:10:08 +01:00
< ui -file -input ref = "fileInput" : accept = "'.opml, .txt'" class = "ml-2" @change ="opmlFileUpload" > {{ $ strings.ButtonUploadOPMLFile }} < / ui -file -input >
2022-05-14 00:40:43 +02:00
< / div >
< div class = "w-full max-w-3xl mx-auto py-4" >
2022-11-09 00:10:08 +01:00
< p v-if ="termSearched && !results.length && !processing" class="text-center text-xl" > {{ $ strings.MessageNoPodcastsFound }} < / p >
2022-05-14 00:40:43 +02:00
< template v-for ="podcast in results" >
< div :key ="podcast.id" class = "flex p-1 hover:bg-primary hover:bg-opacity-25 cursor-pointer" @click ="selectPodcast(podcast)" >
2022-06-26 18:34:58 +02:00
< div class = "w-20 min-w-20 h-20 md:w-24 md:min-w-24 md:h-24 bg-primary" >
2022-05-14 00:40:43 +02:00
< img v -if = " podcast.cover " :src ="podcast.cover" class = "h-full w-full" / >
< / div >
< div class = "flex-grow pl-4 max-w-2xl" >
2023-02-25 00:31:16 +01:00
< div class = "flex items-center" >
< a :href ="podcast.pageUrl" class = "text-base md:text-lg text-gray-200 hover:underline" target = "_blank" @ click.stop > { { podcast . title } } < / a >
< widgets -explicit -indicator :explicit ="podcast.explicit" / >
2023-07-01 16:00:40 +02:00
< widgets -already -in -library -indicator :already-in-library ="podcast.alreadyInLibrary" / >
2023-02-25 00:31:16 +01:00
< / div >
2022-06-26 18:34:58 +02:00
< p class = "text-sm md:text-base text-gray-300 whitespace-nowrap truncate" > by { { podcast . artistName } } < / p >
2022-05-14 00:40:43 +02:00
< p class = "text-xs text-gray-400 leading-5" > { { podcast . genres . join ( ', ' ) } } < / p >
2022-11-09 00:10:08 +01:00
< p class = "text-xs text-gray-400 leading-5" > { { podcast . trackCount } } { { $strings . HeaderEpisodes } } < / p >
2022-05-14 00:40:43 +02:00
< / div >
2022-03-07 02:02:06 +01:00
< / div >
2022-05-14 00:40:43 +02:00
< / template >
< / div >
2022-03-07 02:02:06 +01:00
2022-05-14 00:40:43 +02:00
< div v-show ="processing" class="absolute top-0 left-0 w-full h-full flex items-center justify-center bg-black bg-opacity-25 z-40" >
< ui -loading -indicator / >
2022-03-07 02:02:06 +01:00
< / div >
< / div >
2022-03-19 01:16:54 +01:00
< modals -podcast -new -modal v -model = " showNewPodcastModal " :podcast-data ="selectedPodcast" :podcast-feed-data ="selectedPodcastFeed" / >
2022-05-29 18:46:45 +02:00
< modals -podcast -opml -feeds -modal v -model = " showOPMLFeedsModal " :feeds ="opmlFeeds" / >
2022-03-07 02:02:06 +01:00
< / div >
< / template >
< script >
export default {
2022-03-22 01:24:38 +01:00
async asyncData ( { params , query , store , app , redirect } ) {
2023-12-17 18:06:03 +01:00
// Podcast search/add page is restricted to admins
if ( ! store . getters [ 'user/getIsAdminOrUp' ] ) {
return redirect ( ` /library/ ${ params . library } ` )
}
2022-03-22 01:24:38 +01:00
var libraryId = params . library
2022-04-14 19:57:34 +02:00
var libraryData = await store . dispatch ( 'libraries/fetch' , libraryId )
if ( ! libraryData ) {
2022-03-22 01:24:38 +01:00
return redirect ( '/oops?message=Library not found' )
}
2022-04-14 19:57:34 +02:00
// Redirect book libraries
const library = libraryData . library
if ( library . mediaType === 'book' ) {
return redirect ( ` /library/ ${ libraryId } ` )
}
2022-03-22 01:24:38 +01:00
return {
libraryId
}
} ,
2022-03-07 02:02:06 +01:00
data ( ) {
return {
2022-04-13 23:55:48 +02:00
searchInput : '' ,
2022-03-07 02:02:06 +01:00
results : [ ] ,
termSearched : '' ,
2022-03-19 01:16:54 +01:00
processing : false ,
showNewPodcastModal : false ,
selectedPodcast : null ,
2022-05-29 18:46:45 +02:00
selectedPodcastFeed : null ,
showOPMLFeedsModal : false ,
2023-02-25 00:31:16 +01:00
opmlFeeds : [ ] ,
existentPodcasts : [ ]
2022-03-07 02:02:06 +01:00
}
} ,
computed : {
2023-02-25 00:31:16 +01:00
currentLibraryId ( ) {
return this . $store . state . libraries . currentLibraryId
} ,
2022-03-14 01:34:31 +01:00
streamLibraryItem ( ) {
return this . $store . state . streamLibraryItem
2022-03-07 02:02:06 +01:00
}
} ,
methods : {
2022-05-29 18:46:45 +02:00
async opmlFileUpload ( file ) {
this . processing = true
var txt = await new Promise ( ( resolve ) => {
const reader = new FileReader ( )
reader . onload = ( ) => {
resolve ( reader . result )
}
reader . readAsText ( file )
} )
2022-05-29 19:22:16 +02:00
if ( this . $refs . fileInput ) {
this . $refs . fileInput . reset ( )
}
2022-05-29 18:46:45 +02:00
if ( ! txt || ! txt . includes ( '<opml' ) || ! txt . includes ( '<outline ' ) ) {
// Quick lazy check for valid OPML
this . $toast . error ( 'Invalid OPML file <opml> tag not found OR an <outline> tag was not found' )
this . processing = false
return
}
await this . $axios
. $post ( ` /api/podcasts/opml ` , { opmlText : txt } )
. then ( ( data ) => {
console . log ( data )
this . opmlFeeds = data . feeds || [ ]
this . showOPMLFeedsModal = true
} )
. catch ( ( error ) => {
console . error ( 'Failed' , error )
this . $toast . error ( 'Failed to parse OPML file' )
} )
this . processing = false
} ,
2022-04-13 23:55:48 +02:00
submit ( ) {
if ( ! this . searchInput ) return
if ( this . searchInput . startsWith ( 'http:' ) || this . searchInput . startsWith ( 'https:' ) ) {
this . termSearched = ''
this . results = [ ]
this . checkRSSFeed ( this . searchInput )
} else {
this . submitSearch ( this . searchInput )
}
} ,
async checkRSSFeed ( rssFeed ) {
this . processing = true
var payload = await this . $axios . $post ( ` /api/podcasts/feed ` , { rssFeed } ) . catch ( ( error ) => {
console . error ( 'Failed to get feed' , error )
this . $toast . error ( 'Failed to get podcast feed' )
return null
} )
this . processing = false
if ( ! payload ) return
this . selectedPodcastFeed = payload . podcast
this . selectedPodcast = null
this . showNewPodcastModal = true
} ,
async submitSearch ( term ) {
2022-03-07 02:02:06 +01:00
this . processing = true
this . termSearched = ''
2023-07-01 16:00:40 +02:00
let results = await this . $axios . $get ( ` /api/search/podcast?term= ${ encodeURIComponent ( term ) } ` ) . catch ( ( error ) => {
2022-03-07 02:02:06 +01:00
console . error ( 'Search request failed' , error )
return [ ]
} )
console . log ( 'Got results' , results )
2023-07-01 16:00:40 +02:00
// Filter out podcasts without an RSS feed
results = results . filter ( ( r ) => r . feedUrl )
2023-02-25 00:31:16 +01:00
for ( let result of results ) {
2023-02-25 00:57:25 +01:00
let podcast = this . existentPodcasts . find ( ( p ) => p . itunesId === result . id || p . title === result . title . toLowerCase ( ) )
2023-02-25 00:31:16 +01:00
if ( podcast ) {
result . alreadyInLibrary = true
2023-02-27 19:22:17 +01:00
result . existentId = podcast . id
2023-02-25 00:31:16 +01:00
}
}
2022-03-07 02:02:06 +01:00
this . results = results
this . termSearched = term
this . processing = false
} ,
async selectPodcast ( podcast ) {
console . log ( 'Selected podcast' , podcast )
2023-07-01 16:00:40 +02:00
if ( podcast . existentId ) {
2023-02-27 19:22:17 +01:00
this . $router . push ( ` /item/ ${ podcast . existentId } ` )
return
}
2022-03-07 02:02:06 +01:00
if ( ! podcast . feedUrl ) {
this . $toast . error ( 'Invalid podcast - no feed' )
return
}
this . processing = true
2023-07-01 16:00:40 +02:00
const payload = await this . $axios . $post ( ` /api/podcasts/feed ` , { rssFeed : podcast . feedUrl } ) . catch ( ( error ) => {
2022-03-07 02:02:06 +01:00
console . error ( 'Failed to get feed' , error )
this . $toast . error ( 'Failed to get podcast feed' )
return null
} )
this . processing = false
2022-04-13 23:55:48 +02:00
if ( ! payload ) return
this . selectedPodcastFeed = payload . podcast
2022-03-19 01:16:54 +01:00
this . selectedPodcast = podcast
this . showNewPodcastModal = true
2022-04-13 23:55:48 +02:00
console . log ( 'Got podcast feed' , payload . podcast )
2023-02-25 00:31:16 +01:00
} ,
async fetchExistentPodcastsInYourLibrary ( ) {
this . processing = true
const podcasts = await this . $axios . $get ( ` /api/libraries/ ${ this . currentLibraryId } /items?page=0&minified=1 ` ) . catch ( ( error ) => {
2023-02-25 00:45:06 +01:00
console . error ( 'Failed to fetch podcasts' , error )
2023-02-25 00:31:16 +01:00
return [ ]
} )
this . existentPodcasts = podcasts . results . map ( ( p ) => {
return {
title : p . media . metadata . title . toLowerCase ( ) ,
2023-02-27 19:22:17 +01:00
itunesId : p . media . metadata . itunesId ,
id : p . id
2023-02-25 00:31:16 +01:00
}
} )
this . processing = false
2022-03-07 02:02:06 +01:00
}
} ,
2023-02-25 00:31:16 +01:00
mounted ( ) {
this . fetchExistentPodcastsInYourLibrary ( )
}
2022-03-07 02:02:06 +01:00
}
2023-02-22 13:48:12 +01:00
< / script >