package Ġnet . b jo ern pe ters en . local mp 3 . provider Ċ Ċ import Ġcom . mp at ric . mp 3 agic . Base Exception Ċ import Ġcom . mp at ric . mp 3 agic . Mp 3 File Ċ import Ġjava . io . IOException Ċ import Ġjava . nio . file . Files Ċ import Ġjava . nio . file . Path Ċ import Ġjava . sql . Connection Ċ import Ġjava . sql . Driver Manager Ċ import Ġjava . sql . PreparedStatement Ċ import Ġjava . sql . SQLException Ċ import Ġjava . util . Locale Ċ import Ġjava . util . concurrent . Concurrent HashMap Ċ import Ġkotlin . streams . as Sequence Ċ import Ġkotlinx . coroutines . Comp letable Job Ċ import Ġkotlinx . coroutines . Coroutine Scope Ċ import Ġkotlinx . coroutines . Dispatch ers Ċ import Ġkotlinx . coroutines . Job Ċ import Ġkotlinx . coroutines . coroutine Scope Ċ import Ġkotlinx . coroutines . launch Ċ import Ġkotlinx . coroutines . with Context Ċ import Ġmu . Kotlin Logging Ċ import Ġnet . b jo ern pe ters en . music bot . api . player . Song Ċ import Ġnet . b jo ern pe ters en . music bot . api . player . song Ċ import Ġnet . b jo ern pe ters en . music bot . api . plugin . Plugin Scope Ċ import Ġnet . b jo ern pe ters en . music bot . spi . image . Album Art Supplier Ċ import Ġnet . b jo ern pe ters en . music bot . spi . plugin . management . Init State Writer Ċ Ċ class ĠIndex ( ĊĠĠĠ Ġprivate Ġval Ġprovider : ĠAlbum Art Supplier , ĊĠĠĠ Ġindex Dir : ĠPath , ĊĠĠĠ Ġprivate Ġval Ġsong Dir : ĠPath Ċ ) Ġ: ĠCor outine Scope Ġby ĠPlugin Scope ( Dispatch ers . IO ), ĠAuto Closeable Ġ{ ĊĠĠĠ Ġprivate Ġval Ġlogger Ġ= ĠKotlin Logging . logger Ġ{ Ġ} ĊĠĠĠ Ġprivate Ġval Ġdb Ġ= ĠIndex Db ( provider , Ġindex Dir . resolve (" index . db ")) ĊĊĠĠĠ Ġsuspend Ġfun Ġload ( writer : ĠInit State Writer , Ġrecursive : ĠBoolean ): ĠMap < String , ĠSong > Ġ{ ĊĠĠĠĠĠĠĠ Ġreturn Ġwith Context ( coroutine Context ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġwriter . state (" Loading Ġsongs Ġfrom Ġindex ...") ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġindex S ongs Ġ= Ġdb . read All () ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġ@ Suppress (" Magic Number ") ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġfound S ongs Ġ= ĠConcurrent HashMap < String , ĠSong >( max Of ( 2 5 6 , Ġindex S ongs . size Ġ* Ġ 2 )) ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġwriter . state (" Loading Ġsongs Ġfrom Ġdisk ...") ĊĠĠĠĠĠĠĠĠĠĠĠ Ġcoroutine Scope Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ( if Ġ( recursive ) ĠFiles . walk ( song Dir ) Ġelse ĠFiles . list ( song Dir )) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. as Sequence () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. filter Ġ{ ĠFiles . is Regular File ( it ) Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. filter Ġ{ Ġit . extension . toLowerCase ( Locale . US ) Ġ== Ġ" mp 3 " Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. forEach Ġ{ Ġpath Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġid Ġ= Ġpath . to Id () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġindexed Ġ= Ġindex S ongs [ id ] ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( indexed Ġ!= Ġnull ) Ġfound S ongs [ id ] Ġ= Ġindexed ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġelse Ġlaunch Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġsong Ġ= Ġcreate Song ( writer , Ġpath ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( song Ġ!= Ġnull ) Ġfound S ongs [ id ] Ġ= Ġsong ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġstart Cleanup ( index S ongs , Ġfound S ongs ) ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġfound S ongs ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġstart Cleanup ( ĊĠĠĠĠĠĠĠ Ġindex S ongs : ĠMap < String , ĠSong >, ĊĠĠĠĠĠĠĠ Ġfound S ongs : ĠConcurrent HashMap < String , ĠSong > ĊĠĠĠ Ġ) Ġ{ ĊĠĠĠĠĠĠĠ Ġlaunch Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlogger . debug Ġ{ Ġ" Starting Ġindex Ġcleanup ..." Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġindex S ongs . keys . as Sequence () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. filter Ġ{ Ġit Ġ! in Ġfound S ongs . keys Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. forEach Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlogger . debug Ġ{ Ġ" Deleting Ġ$ it " Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġdb . delete ( it ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġcreate Song ( init Writer : ĠInit State Writer , Ġpath : ĠPath ): ĠSong ? Ġ{ ĊĠĠĠĠĠĠĠ Ġlogger . debug Ġ{ Ġ" Loading Ġtag Ġfor Ġ'$ path '" Ġ} ĊĠĠĠĠĠĠĠ Ġreturn Ġcreate Song ( path ). also Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( it Ġ== Ġnull ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġinit Writer . warning (" Could Ġnot Ġload Ġsong Ġfrom Ġ'$ path '") ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} Ġelse Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġinit Writer . state (""" Loaded Ġsong Ġ${ it . title } """) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlaunch Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlogger . debug Ġ{ Ġ" Writing Ġto Ġdb : Ġ${ it . title }" Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġdb . write ( it ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Suppress (" Return Count ") ĊĠĠĠ Ġprivate Ġfun Ġcreate Song ( path : ĠPath ): ĠSong ? Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġmp 3 Ġ= Ġtry Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ ĠM p 3 File ( path ) ĊĠĠĠĠĠĠĠ Ġ} Ġcatch Ġ( e : ĠIOException ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlogger . error ( e ) Ġ{ Ġe . message Ġ?: Ġ" Exception Ġof Ġtype Ġ${ e :: class . java . name }" Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnull ĊĠĠĠĠĠĠĠ Ġ} Ġcatch Ġ( e : ĠBase Exception ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlogger . error ( e ) Ġ{ Ġe . message Ġ?: Ġ" Exception Ġof Ġtype Ġ${ e :: class . java . name }" Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnull ĊĠĠĠĠĠĠĠ Ġ} Ġ?: Ġreturn Ġnull ĊĊĠĠĠĠĠĠĠ Ġval Ġid 3 Ġ= Ġwhen Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmp 3 . has Id 3 v 1 Tag () Ġ-> Ġmp 3 . id 3 v 1 Tag ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmp 3 . has Id 3 v 2 Tag () Ġ-> Ġmp 3 . id 3 v 2 Tag ĊĠĠĠĠĠĠĠĠĠĠĠ Ġelse Ġ-> Ġreturn Ġnull ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġreturn Ġprovider . song ( path . to Id ()) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtitle Ġ= Ġid 3 . title ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdescription Ġ= Ġid 3 . artist Ġ?: Ġ"" ĊĠĠĠĠĠĠĠĠĠĠĠ Ġduration Ġ= Ġmp 3 . length InSeconds . toInt () ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġoverride Ġfun Ġclose () Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġjob Ġ= Ġcoroutine Context [ Job ] Ġas ĠComp letable Job ĊĠĠĠĠĠĠĠ Ġjob . complete () ĊĠĠĠĠĠĠĠ Ġjob . invoke On Completion Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdb . close () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlogger . debug Ġ{ Ġ" Closed " Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} Ċ } Ċ Ċ @ Suppress (" Magic Number ") Ċ private Ġclass ĠIndex Db ( ĊĠĠĠ Ġprivate Ġval Ġprovider : ĠAlbum Art Supplier , ĊĠĠĠ Ġindex File : ĠPath Ċ ) Ġ: ĠAuto Closeable Ġ{ ĊĠĠĠ Ġprivate Ġval Ġlogger Ġ= ĠKotlin Logging . logger Ġ{ Ġ} ĊĠĠĠ Ġprivate Ġval Ġdb : ĠConnection Ġby Ġlazy Ġ{ ĊĠĠĠĠĠĠĠ ĠDriver Manager . getConnection (" jdbc : sqlite :$ index File "). also Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġit . createStatement (). use Ġ{ Ġstatement Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġstatement . execute ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ""" ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠCREATE ĠTABLE ĠIF ĠNOT ĠEXISTS Ġsongs ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġid ĠTEXT ĠPRIMARY ĠKEY ĠUNIQUE ĠNOT ĠNULL , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġtitle ĠTEXT ĠNOT ĠNULL , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġdescription ĠTEXT ĠNOT ĠNULL , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġduration ĠINT ĠNOT ĠNULL ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ""" . trim Margin () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġval ĠgetAll : ĠPreparedStatement Ġby Ġlazy Ġ{ ĊĠĠĠĠĠĠĠ Ġdb . prepareStatement ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ""" ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠSELECT Ġid , Ġtitle , Ġdescription , Ġduration ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠFROM Ġsongs ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ""" . trim Indent () ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġval Ġinsert Song : ĠPreparedStatement Ġby Ġlazy Ġ{ ĊĠĠĠĠĠĠĠ Ġdb . prepareStatement ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ""" ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠINSERT ĠOR ĠREPLACE ĠINTO Ġsongs Ġ( id , Ġtitle , Ġdescription , Ġduration ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠVALUES ( ?,?, ?, ?) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ""" . trim Indent () ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġval Ġdelete Song : ĠPreparedStatement Ġby Ġlazy Ġ{ ĊĠĠĠĠĠĠĠ Ġdb . prepareStatement ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ""" ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠDELETE ĠFROM Ġsongs ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠWHERE Ġid Ġ= Ġ? ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ""" . trim Indent () ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġread All (): ĠMap < String , ĠSong > Ġ{ ĊĠĠĠĠĠĠĠ Ġreturn Ġsynchronized ( getAll ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtry Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġresult Ġ= ĠgetAll . executeQuery () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġsong ById Ġ= ĠHashMap < String , ĠSong >( 2 5 6 ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġwhile Ġ( result . next ()) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġsong Ġ= Ġprovider . song ( result . getString (" id ")) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġtitle Ġ= Ġresult . getString (" title ") ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġdescription Ġ= Ġresult . getString (" description ") ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġduration Ġ= Ġresult . getInt (" duration ") ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( duration Ġ!= Ġ 0 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġthis . duration Ġ= Ġduration ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġsong ById [ song . id ] Ġ= Ġsong ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġsong ById ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} Ġcatch Ġ( e : ĠSQLException ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlogger . error ( e ) Ġ{ Ġ" Could Ġnot Ġget Ġall Ġsongs " Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġempty Map () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġwrite ( song : ĠSong ) Ġ{ ĊĠĠĠĠĠĠĠ Ġsynchronized ( insert Song ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtry Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġinsert Song . clear Parameters () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġinsert Song . setString ( 1 , Ġsong . id ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġinsert Song . setString ( 2 , Ġsong . title ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġinsert Song . setString ( 3 , Ġsong . description ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġinsert Song . setInt ( 4 , Ġsong . duration Ġ?: Ġ 0 ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġinsert Song . executeUpdate () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} Ġcatch Ġ( e : ĠSQLException ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlogger . error ( e ) Ġ{ Ġ" Could Ġnot Ġwrite Ġsong Ġto Ġindex " Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġdelete ( id : ĠString ) Ġ{ ĊĠĠĠĠĠĠĠ Ġsynchronized ( delete Song ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtry Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġdelete Song . clear Parameters () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġdelete Song . setString ( 1 , Ġid ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġdelete Song . executeUpdate () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} Ġcatch Ġ( e : ĠSQLException ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlogger . error ( e ) Ġ{ Ġ" Could Ġnot Ġdelete Ġsong Ġfrom Ġindex " Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġoverride Ġfun Ġclose () Ġ{ ĊĠĠĠĠĠĠĠ Ġdb . close () ĊĠĠĠ Ġ} Ċ } Ċ