Subsonic-->Libresonic regex

This commit is contained in:
Eugene E. Kashpureff Jr
2016-05-14 22:26:36 +00:00
parent 8cd951f9a7
commit 745969264a
1324 changed files with 4084 additions and 4100 deletions
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="cacheFactory" class="org.libresonic.player.cache.CacheFactory"/>
<bean id="userCache" factory-bean="cacheFactory" factory-method="getCache">
<constructor-arg value="userCache"/>
</bean>
<bean id="mediaFileMemoryCache" factory-bean="cacheFactory" factory-method="getCache">
<constructor-arg value="mediaFileMemoryCache"/>
</bean>
</beans>
@@ -0,0 +1,246 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="loginFailureLogger" class="org.libresonic.player.security.LoginFailureLogger"/>
<bean class="org.libresonic.player.security.LibresonicApplicationEventListener">
<property name="loginFailureLogger" ref="loginFailureLogger"/>
</bean>
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
PATTERN_TYPE_APACHE_ANT
/wap**=httpSessionContextIntegrationFilter,logoutFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,basicExceptionTranslationFilter,filterInvocationInterceptor
/podcastChannel**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
/podcast**=httpSessionContextIntegrationFilter,logoutFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,basicExceptionTranslationFilter,filterInvocationInterceptor
/rest/**=httpSessionContextIntegrationFilter,logoutFilter,basicProcessingFilter,restRequestParameterProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,basicExceptionTranslationFilter,filterInvocationInterceptor
/**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
</value>
</property>
</bean>
<bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"/>
<bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter">
<constructor-arg value="/login.view?logout"/>
<!-- URL redirected to after logout -->
<constructor-arg>
<list>
<ref bean="rememberMeServices"/>
<bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/>
</list>
</constructor-arg>
</bean>
<bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureUrl" value="/login.view?error"/>
<property name="defaultTargetUrl" value="/"/>
<property name="alwaysUseDefaultTargetUrl" value="true"/>
<property name="filterProcessesUrl" value="/j_acegi_security_check"/>
<property name="rememberMeServices" ref="rememberMeServices"/>
</bean>
<bean id="basicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationEntryPoint" ref="basicProcessingFilterEntryPoint"/>
</bean>
<bean id="restRequestParameterProcessingFilter" class="org.libresonic.player.security.RESTRequestParameterProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="settingsService" ref="settingsService"/>
<property name="securityService" ref="securityService"/>
<property name="loginFailureLogger" ref="loginFailureLogger"/>
</bean>
<bean id="basicProcessingFilterEntryPoint" class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
<property name="realmName" value="Libresonic"/>
</bean>
<bean id="securityContextHolderAwareRequestFilter" class="org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter"/>
<bean id="rememberMeProcessingFilter" class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="rememberMeServices" ref="rememberMeServices"/>
</bean>
<bean id="anonymousProcessingFilter" class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
<property name="key" value="libresonic"/>
<property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS"/>
</bean>
<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint">
<bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl" value="/login.view?"/>
<property name="forceHttps" value="false"/>
</bean>
</property>
<property name="accessDeniedHandler">
<bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
<property name="errorPage" value="/accessDenied.view"/>
</bean>
</property>
</bean>
<bean id="basicExceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint">
<bean class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
<property name="realmName" value="Libresonic"/>
</bean>
</property>
</bean>
<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="alwaysReauthenticate" value="true"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="objectDefinitionSource">
<value>
PATTERN_TYPE_APACHE_ANT
/login.view=IS_AUTHENTICATED_ANONYMOUSLY
/recover.view=IS_AUTHENTICATED_ANONYMOUSLY
/accessDenied.view=IS_AUTHENTICATED_ANONYMOUSLY
/coverArt.view=IS_AUTHENTICATED_ANONYMOUSLY
/hls/**=IS_AUTHENTICATED_ANONYMOUSLY
/stream/**=IS_AUTHENTICATED_ANONYMOUSLY
/ws/**=IS_AUTHENTICATED_ANONYMOUSLY
/share/**=IS_AUTHENTICATED_ANONYMOUSLY
/style/**=IS_AUTHENTICATED_ANONYMOUSLY
/icons/**=IS_AUTHENTICATED_ANONYMOUSLY
/flash/**=IS_AUTHENTICATED_ANONYMOUSLY
/script/**=IS_AUTHENTICATED_ANONYMOUSLY
/sonos/**=IS_AUTHENTICATED_ANONYMOUSLY
/crossdomain.xml=IS_AUTHENTICATED_ANONYMOUSLY
/personalSettings.view=ROLE_SETTINGS
/passwordSettings.view=ROLE_SETTINGS
/playerSettings.view=ROLE_SETTINGS
/shareSettings.view=ROLE_SETTINGS
/generalSettings.view=ROLE_ADMIN
/advancedSettings.view=ROLE_ADMIN
/userSettings.view=ROLE_ADMIN
/musicFolderSettings.view=ROLE_ADMIN
/networkSettings.view=ROLE_ADMIN
/dlnaSettings.view=ROLE_ADMIN
/sonosSettings.view=ROLE_ADMIN
/transcodingSettings.view=ROLE_ADMIN
/internetRadioSettings.view=ROLE_ADMIN
/podcastSettings.view=ROLE_ADMIN
/db.view=ROLE_ADMIN
/deletePlaylist.view=ROLE_PLAYLIST
/savePlaylist.view=ROLE_PLAYLIST
/download.view=ROLE_DOWNLOAD
/upload.view=ROLE_UPLOAD
/createShare.view=ROLE_SHARE
/changeCoverArt.view=ROLE_COVERART
/editTags.view=ROLE_COVERART
/setMusicFileInfo.view=ROLE_COMMENT
/podcastReceiverAdmin.view=ROLE_PODCAST
/**=IS_AUTHENTICATED_REMEMBERED
</value>
</property>
</bean>
<bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="false"/>
<property name="decisionVoters">
<list>
<bean class="org.acegisecurity.vote.RoleVoter"/>
<bean class="org.acegisecurity.vote.AuthenticatedVoter"/>
</list>
</property>
</bean>
<bean id="rememberMeServices" class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="securityService"/>
<property name="tokenValiditySeconds" value="31536000"/>
<!-- One year -->
<property name="key" value="libresonic"/>
</bean>
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
<ref local="ldapAuthenticationProvider"/>
<bean class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
<property name="key" value="libresonic"/>
</bean>
<bean class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider">
<property name="key" value="libresonic"/>
</bean>
</list>
</property>
</bean>
<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="securityService"/>
<property name="userCache" ref="userCacheWrapper"/>
</bean>
<bean id="userCacheWrapper" class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">
<property name="cache" ref="userCache"/>
</bean>
<bean id="ldapAuthenticationProvider" class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
<constructor-arg ref="bindAuthenticator"/>
<constructor-arg ref="userDetailsServiceBasedAuthoritiesPopulator"/>
<property name="userCache" ref="userCacheWrapper"/>
</bean>
<bean id="bindAuthenticator" class="org.libresonic.player.ldap.LibresonicLdapBindAuthenticator">
<property name="securityService" ref="securityService"/>
<property name="settingsService" ref="settingsService"/>
</bean>
<bean id="userDetailsServiceBasedAuthoritiesPopulator"
class="org.libresonic.player.ldap.UserDetailsServiceBasedAuthoritiesPopulator">
<property name="userDetailsService" ref="securityService"/>
</bean>
<!-- Authorization of AJAX services. -->
<bean id="ajaxServiceInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="objectDefinitionSource">
<value>
org.libresonic.player.ajax.TagService.setTags=ROLE_COVERART
org.libresonic.player.ajax.TransferService.getUploadInfo=ROLE_UPLOAD
</value>
</property>
</bean>
<bean id="ajaxTagServiceSecure" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="ajaxTagService"/>
<property name="interceptorNames">
<list>
<idref local="ajaxServiceInterceptor"/>
</list>
</property>
</bean>
<bean id="ajaxTransferServiceSecure" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="ajaxTransferService"/>
<property name="interceptorNames">
<list>
<idref local="ajaxServiceInterceptor"/>
</list>
</property>
</bean>
</beans>
@@ -0,0 +1,300 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!-- DAO's -->
<bean id="playerDao" class="org.libresonic.player.dao.PlayerDao">
<property name="daoHelper" ref="daoHelper"/>
</bean>
<bean id="mediaFileDao" class="org.libresonic.player.dao.MediaFileDao">
<property name="daoHelper" ref="daoHelper"/>
</bean>
<bean id="artistDao" class="org.libresonic.player.dao.ArtistDao">
<property name="daoHelper" ref="daoHelper"/>
</bean>
<bean id="albumDao" class="org.libresonic.player.dao.AlbumDao">
<property name="daoHelper" ref="daoHelper"/>
</bean>
<bean id="playlistDao" class="org.libresonic.player.dao.PlaylistDao">
<property name="daoHelper" ref="daoHelper"/>
</bean>
<bean id="playQueueDao" class="org.libresonic.player.dao.PlayQueueDao">
<property name="daoHelper" ref="daoHelper"/>
</bean>
<bean id="internetRadioDao" class="org.libresonic.player.dao.InternetRadioDao">
<property name="daoHelper" ref="daoHelper"/>
</bean>
<bean id="musicFileInfoDao" class="org.libresonic.player.dao.RatingDao">
<property name="daoHelper" ref="daoHelper"/>
</bean>
<bean id="musicFolderDao" class="org.libresonic.player.dao.MusicFolderDao">
<property name="daoHelper" ref="daoHelper"/>
</bean>
<bean id="userDao" class="org.libresonic.player.dao.UserDao">
<property name="daoHelper" ref="daoHelper"/>
</bean>
<bean id="transcodingDao" class="org.libresonic.player.dao.TranscodingDao">
<property name="daoHelper" ref="daoHelper"/>
</bean>
<bean id="podcastDao" class="org.libresonic.player.dao.PodcastDao">
<property name="daoHelper" ref="daoHelper"/>
</bean>
<bean id="avatarDao" class="org.libresonic.player.dao.AvatarDao">
<property name="daoHelper" ref="daoHelper"/>
</bean>
<bean id="shareDao" class="org.libresonic.player.dao.ShareDao">
<property name="daoHelper" ref="daoHelper"/>
</bean>
<bean id="bookmarkDao" class="org.libresonic.player.dao.BookmarkDao">
<property name="daoHelper" ref="daoHelper"/>
</bean>
<bean id="daoHelper" class="org.libresonic.player.dao.DaoHelperFactory" factory-method="create"/>
<!-- Services -->
<bean id="mediaFileService" class="org.libresonic.player.service.MediaFileService">
<property name="securityService" ref="securityService"/>
<property name="settingsService" ref="settingsService"/>
<property name="mediaFileMemoryCache" ref="mediaFileMemoryCache"/>
<property name="mediaFileDao" ref="mediaFileDao"/>
<property name="albumDao" ref="albumDao"/>
<property name="metaDataParserFactory" ref="metaDataParserFactory"/>
</bean>
<bean id="securityService" class="org.libresonic.player.service.SecurityService">
<property name="settingsService" ref="settingsService"/>
<property name="userDao" ref="userDao"/>
<property name="userCache" ref="userCache"/>
</bean>
<bean id="settingsService" class="org.libresonic.player.service.SettingsService" init-method="init">
<property name="internetRadioDao" ref="internetRadioDao"/>
<property name="musicFolderDao" ref="musicFolderDao"/>
<property name="userDao" ref="userDao"/>
<property name="avatarDao" ref="avatarDao"/>
<property name="versionService" ref="versionService"/>
</bean>
<bean id="mediaScannerService" class="org.libresonic.player.service.MediaScannerService" init-method="init" depends-on="metaDataParserFactory">
<property name="settingsService" ref="settingsService"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="mediaFileDao" ref="mediaFileDao"/>
<property name="playlistService" ref="playlistService"/>
<property name="artistDao" ref="artistDao"/>
<property name="albumDao" ref="albumDao"/>
<property name="searchService" ref="searchService"/>
</bean>
<bean id="searchService" class="org.libresonic.player.service.SearchService">
<property name="mediaFileService" ref="mediaFileService"/>
<property name="artistDao" ref="artistDao"/>
<property name="albumDao" ref="albumDao"/>
</bean>
<bean id="networkService" class="org.libresonic.player.service.NetworkService" init-method="init">
<property name="settingsService" ref="settingsService"/>
<property name="upnpService" ref="upnpService"/>
</bean>
<bean id="playerService" class="org.libresonic.player.service.PlayerService" init-method="init">
<property name="playerDao" ref="playerDao"/>
<property name="statusService" ref="statusService"/>
<property name="securityService" ref="securityService"/>
<property name="transcodingService" ref="transcodingService"/>
</bean>
<bean id="playlistService" class="org.libresonic.player.service.PlaylistService">
<property name="mediaFileService" ref="mediaFileService"/>
<property name="securityService" ref="securityService"/>
<property name="settingsService" ref="settingsService"/>
<property name="mediaFileDao" ref="mediaFileDao"/>
<property name="playlistDao" ref="playlistDao"/>
</bean>
<bean id="versionService" class="org.libresonic.player.service.VersionService" init-method="init"/>
<bean id="statusService" class="org.libresonic.player.service.StatusService">
<property name="mediaFileService" ref="mediaFileService"/>
</bean>
<bean id="ratingService" class="org.libresonic.player.service.RatingService">
<property name="ratingDao" ref="musicFileInfoDao"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="securityService" ref="securityService"/>
</bean>
<bean id="musicIndexService" class="org.libresonic.player.service.MusicIndexService">
<property name="settingsService" ref="settingsService"/>
<property name="mediaFileService" ref="mediaFileService"/>
</bean>
<bean id="audioScrobblerService" class="org.libresonic.player.service.AudioScrobblerService">
<property name="settingsService" ref="settingsService"/>
</bean>
<bean id="transcodingService" class="org.libresonic.player.service.TranscodingService">
<property name="transcodingDao" ref="transcodingDao"/>
<property name="settingsService" ref="settingsService"/>
<property name="playerService" ref="playerService"/>
</bean>
<bean id="shareService" class="org.libresonic.player.service.ShareService">
<property name="shareDao" ref="shareDao"/>
<property name="settingsService" ref="settingsService"/>
<property name="securityService" ref="securityService"/>
<property name="mediaFileService" ref="mediaFileService"/>
</bean>
<bean id="podcastService" class="org.libresonic.player.service.PodcastService" init-method="init">
<property name="podcastDao" ref="podcastDao"/>
<property name="settingsService" ref="settingsService"/>
<property name="securityService" ref="securityService"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="metaDataParserFactory" ref="metaDataParserFactory"/>
</bean>
<bean id="adService" class="org.libresonic.player.service.AdService">
<property name="adInterval" value="8"/>
</bean>
<bean id="jukeboxService" class="org.libresonic.player.service.JukeboxService">
<property name="statusService" ref="statusService"/>
<property name="transcodingService" ref="transcodingService"/>
<property name="audioScrobblerService" ref="audioScrobblerService"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="settingsService" ref="settingsService"/>
<property name="securityService" ref="securityService"/>
</bean>
<bean id="folderBasedContentDirectory" class="org.libresonic.player.service.upnp.FolderBasedContentDirectory">
<property name="settingsService" ref="settingsService"/>
<property name="playlistService" ref="playlistService"/>
<property name="playerService" ref="playerService"/>
<property name="transcodingService" ref="transcodingService"/>
<property name="mediaFileService" ref="mediaFileService"/>
</bean>
<bean id="upnpService" class="org.libresonic.player.service.UPnPService" init-method="init">
<property name="settingsService" ref="settingsService"/>
<property name="versionService" ref="versionService"/>
<property name="folderBasedContentDirectory" ref="folderBasedContentDirectory"/>
</bean>
<bean id="lastFmService" class="org.libresonic.player.service.LastFmService" init-method="init">
<property name="mediaFileService" ref="mediaFileService"/>
<property name="mediaFileDao" ref="mediaFileDao"/>
<property name="artistDao" ref="artistDao"/>
</bean>
<bean id="sonosService" class="org.libresonic.player.service.SonosService" init-method="init">
<property name="sonosHelper" ref="sonosHelper"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="securityService" ref="securityService"/>
<property name="settingsService" ref="settingsService"/>
<property name="playlistService" ref="playlistService"/>
<property name="upnpService" ref="upnpService"/>
</bean>
<bean id="metaDataParserFactory" class="org.libresonic.player.service.metadata.MetaDataParserFactory">
<property name="parsers">
<list>
<bean class="org.libresonic.player.service.metadata.JaudiotaggerParser"/>
<bean class="org.libresonic.player.service.metadata.FFmpegParser">
<property name="transcodingService" ref="transcodingService"/>
</bean>
<bean class="org.libresonic.player.service.metadata.DefaultMetaDataParser"/>
</list>
</property>
</bean>
<!-- AJAX services -->
<bean id="ajaxMultiService" class="org.libresonic.player.ajax.MultiService">
<property name="networkService" ref="networkService"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="settingsService" ref="settingsService"/>
<property name="securityService" ref="securityService"/>
<property name="lastFmService" ref="lastFmService"/>
</bean>
<bean id="ajaxNowPlayingService" class="org.libresonic.player.ajax.NowPlayingService">
<property name="playerService" ref="playerService"/>
<property name="statusService" ref="statusService"/>
<property name="settingsService" ref="settingsService"/>
<property name="mediaScannerService" ref="mediaScannerService"/>
</bean>
<bean id="ajaxPlayQueueService" class="org.libresonic.player.ajax.PlayQueueService">
<property name="playerService" ref="playerService"/>
<property name="playlistService" ref="playlistService"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="lastFmService" ref="lastFmService"/>
<property name="mediaFileDao" ref="mediaFileDao"/>
<property name="playQueueDao" ref="playQueueDao"/>
<property name="jukeboxService" ref="jukeboxService"/>
<property name="transcodingService" ref="transcodingService"/>
<property name="settingsService" ref="settingsService"/>
<property name="searchService" ref="searchService"/>
<property name="ratingService" ref="ratingService"/>
<property name="securityService" ref="securityService"/>
<property name="podcastService" ref="podcastService"/>
</bean>
<bean id="ajaxPlaylistService" class="org.libresonic.player.ajax.PlaylistService">
<property name="playlistService" ref="playlistService"/>
<property name="securityService" ref="securityService"/>
<property name="settingsService" ref="settingsService"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="playerService" ref="playerService"/>
<property name="mediaFileDao" ref="mediaFileDao"/>
<property name="localeResolver" ref="localeResolver"/>
</bean>
<bean id="ajaxLyricsService" class="org.libresonic.player.ajax.LyricsService"/>
<bean id="ajaxCoverArtService" class="org.libresonic.player.ajax.CoverArtService">
<property name="securityService" ref="securityService"/>
<property name="mediaFileService" ref="mediaFileService"/>
</bean>
<bean id="ajaxStarService" class="org.libresonic.player.ajax.StarService">
<property name="securityService" ref="securityService"/>
<property name="mediaFileDao" ref="mediaFileDao"/>
</bean>
<bean id="ajaxTagService" class="org.libresonic.player.ajax.TagService">
<property name="mediaFileService" ref="mediaFileService"/>
<property name="metaDataParserFactory" ref="metaDataParserFactory"/>
</bean>
<bean id="ajaxTransferService" class="org.libresonic.player.ajax.TransferService"/>
<bean id="ajaxChatService" class="org.libresonic.player.ajax.ChatService" init-method="init">
<property name="securityService" ref="securityService"/>
</bean>
<bean id="localeResolver" class="org.libresonic.player.i18n.LibresonicLocaleResolver">
<property name="securityService" ref="securityService"/>
<property name="settingsService" ref="settingsService"/>
</bean>
</beans>
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml"/>
<import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
<jaxws:endpoint id="sonosEndpoint" implementor="#sonosService" address="/Sonos">
<jaxws:outFaultInterceptors>
<bean class="org.libresonic.player.service.sonos.SonosFaultInterceptor"/>
</jaxws:outFaultInterceptors>
</jaxws:endpoint>
<bean id="sonosHelper" class="org.libresonic.player.service.sonos.SonosHelper">
<property name="mediaFileService" ref="mediaFileService"/>
<property name="settingsService" ref="settingsService"/>
<property name="playlistService" ref="playlistService"/>
<property name="playerService" ref="playerService"/>
<property name="transcodingService" ref="transcodingService"/>
<property name="musicIndexService" ref="musicIndexService"/>
<property name="searchService" ref="searchService"/>
<property name="ratingService" ref="ratingService"/>
<property name="lastFmService" ref="lastFmService"/>
<property name="podcastService" ref="podcastService"/>
<property name="mediaFileDao" ref="mediaFileDao"/>
</bean>
</beans>
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN" "http://getahead.org/dwr/dwr30.dtd">
<dwr>
<allow>
<create creator="spring" javascript="multiService">
<param name="beanName" value="ajaxMultiService"/>
</create>
<create creator="spring" javascript="nowPlayingService">
<param name="beanName" value="ajaxNowPlayingService"/>
</create>
<create creator="spring" javascript="playQueueService">
<param name="beanName" value="ajaxPlayQueueService"/>
</create>
<create creator="spring" javascript="playlistService">
<param name="beanName" value="ajaxPlaylistService"/>
</create>
<create creator="spring" javascript="lyricsService">
<param name="beanName" value="ajaxLyricsService"/>
</create>
<create creator="spring" javascript="coverArtService">
<param name="beanName" value="ajaxCoverArtService"/>
</create>
<create creator="spring" javascript="starService">
<param name="beanName" value="ajaxStarService"/>
</create>
<create creator="spring" javascript="tagService">
<param name="beanName" value="ajaxTagService"/>
</create>
<create creator="spring" javascript="transferService">
<param name="beanName" value="ajaxTransferService"/>
</create>
<create creator="spring" javascript="chatService">
<param name="beanName" value="ajaxChatService"/>
</create>
<convert converter="bean" match="org.libresonic.player.ajax.NetworkStatus"/>
<convert converter="bean" match="org.libresonic.player.ajax.NowPlayingInfo"/>
<convert converter="bean" match="org.libresonic.player.ajax.ScanInfo"/>
<convert converter="bean" match="org.libresonic.player.ajax.PlayQueueInfo"/>
<convert converter="bean" match="org.libresonic.player.ajax.PlayQueueInfo$Entry"/>
<convert converter="bean" match="org.libresonic.player.ajax.PlaylistInfo"/>
<convert converter="bean" match="org.libresonic.player.ajax.PlaylistInfo$Entry"/>
<convert converter="bean" match="org.libresonic.player.domain.Playlist"/>
<convert converter="bean" match="org.libresonic.player.domain.ArtistBio"/>
<convert converter="bean" match="org.libresonic.player.ajax.UploadInfo"/>
<convert converter="bean" match="org.libresonic.player.ajax.LyricsInfo"/>
<convert converter="bean" match="org.libresonic.player.ajax.CoverArtInfo"/>
<convert converter="bean" match="org.libresonic.player.ajax.ChatService$Message"/>
<convert converter="bean" match="org.libresonic.player.ajax.ChatService$Messages"/>
<convert converter="bean" match="org.libresonic.player.ajax.SimilarArtist"/>
<convert converter="bean" match="org.libresonic.player.ajax.ArtistInfo"/>
<convert converter="bean" match="org.libresonic.player.ajax.TopSong"/>
</allow>
</dwr>
@@ -0,0 +1,22 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<html>
<head>
<%@ include file="head.jsp" %>
</head>
<body class="mainframe bgcolor1">
<h1>
<img src="<spring:theme code="errorImage"/>" alt=""/>
<span style="vertical-align: middle"><fmt:message key="accessDenied.title"/></span>
</h1>
<p>
<fmt:message key="accessDenied.text"/>
</p>
<div class="back"><a href="javascript:history.go(-1)"><fmt:message key="common.back"/></a></div>
</body>
</html>
@@ -0,0 +1,100 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<%--@elvariable id="command" type="org.libresonic.player.command.AdvancedSettingsCommand"--%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript" src="<c:url value="/script/scripts-2.0.js"/>"></script>
<script type="text/javascript" language="javascript">
function enableLdapFields() {
$("#ldap").is(":checked") ? $("#ldapTable").show() : $("#ldapTable").hide();
}
</script>
</head>
<body class="mainframe bgcolor1" onload="enableLdapFields()">
<script type="text/javascript" src="<c:url value="/script/wz_tooltip.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/tip_balloon.js"/>"></script>
<c:import url="settingsHeader.jsp">
<c:param name="cat" value="advanced"/>
<c:param name="toast" value="${command.toast}"/>
</c:import>
<form:form method="post" action="advancedSettings.view" commandName="command">
<table style="white-space:nowrap" class="indent">
<tr>
<td><fmt:message key="advancedsettings.downloadlimit"/></td>
<td>
<form:input path="downloadLimit" size="8"/>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="downloadlimit"/></c:import>
</td>
</tr>
<tr>
<td><fmt:message key="advancedsettings.uploadlimit"/></td>
<td>
<form:input path="uploadLimit" size="8"/>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="uploadlimit"/></c:import>
</td>
</tr>
</table>
<table class="indent"><tr><td>
<form:checkbox path="ldapEnabled" id="ldap" cssClass="checkbox" onclick="enableLdapFields()"/>
<label for="ldap"><fmt:message key="advancedsettings.ldapenabled"/></label>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="ldap"/></c:import>
</td></tr></table>
<table class="indent" id="ldapTable" style="padding-left:2em;padding-bottom: 1em">
<tr>
<td><fmt:message key="advancedsettings.ldapurl"/></td>
<td colspan="3">
<form:input path="ldapUrl" size="70"/>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="ldapurl"/></c:import>
</td>
</tr>
<tr>
<td><fmt:message key="advancedsettings.ldapsearchfilter"/></td>
<td colspan="3">
<form:input path="ldapSearchFilter" size="70"/>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="ldapsearchfilter"/></c:import>
</td>
</tr>
<tr>
<td><fmt:message key="advancedsettings.ldapmanagerdn"/></td>
<td>
<form:input path="ldapManagerDn" size="20"/>
</td>
<td><fmt:message key="advancedsettings.ldapmanagerpassword"/></td>
<td>
<form:password path="ldapManagerPassword" size="20"/>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="ldapmanagerdn"/></c:import>
</td>
</tr>
<tr>
<td colspan="5">
<form:checkbox path="ldapAutoShadowing" id="ldapAutoShadowing" cssClass="checkbox"/>
<label for="ldapAutoShadowing"><fmt:message key="advancedsettings.ldapautoshadowing"><fmt:param value="${command.brand}"/></fmt:message></label>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="ldapautoshadowing"/></c:import>
</td>
</tr>
</table>
<input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
<input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
</form:form>
<c:if test="${command.reloadNeeded}">
<script language="javascript" type="text/javascript">
parent.frames.left.location.href="left.view?";
parent.frames.playQueue.location.href="playQueue.view?";
</script>
</c:if>
</body></html>
@@ -0,0 +1,512 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%--@elvariable id="model" type="java.util.Map"--%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript" src="<c:url value="/script/scripts-2.0.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/interface/starService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/interface/playlistService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/interface/multiService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/fancyzoom/FancyZoom.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/fancyzoom/FancyZoomHTML.js"/>"></script>
</head><body class="mainframe bgcolor1" onload="init();">
<sub:url value="createShare.view" var="shareUrl">
<sub:param name="id" value="${model.dir.id}"/>
</sub:url>
<sub:url value="download.view" var="downloadUrl">
<sub:param name="id" value="${model.dir.id}"/>
</sub:url>
<script type="text/javascript" language="javascript">
function init() {
setupZoom('<c:url value="/"/>');
$("#dialog-select-playlist").dialog({resizable: true, height: 350, autoOpen: false,
buttons: {
"<fmt:message key="common.cancel"/>": function() {
$(this).dialog("close");
}
}});
<c:if test="${model.showArtistInfo}">
loadArtistInfo();
</c:if>
}
function loadArtistInfo() {
multiService.getArtistInfo(${model.dir.id}, 8, 0, function (artistInfo) {
if (artistInfo.similarArtists.length > 0) {
var html = "";
for (var i = 0; i < artistInfo.similarArtists.length; i++) {
html += "<a href='main.view?id=" + artistInfo.similarArtists[i].mediaFileId + "' target='main'>" +
escapeHtml(artistInfo.similarArtists[i].artistName) + "</a>";
if (i < artistInfo.similarArtists.length - 1) {
html += " <span class='similar-artist-divider'>|</span> ";
}
}
$("#similarArtists").append(html);
$("#similarArtists").show();
$("#similarArtistsTitle").show();
$("#similarArtistsRadio").show();
}
});
}
<!-- actionSelected() is invoked when the users selects from the "More actions..." combo box. -->
function actionSelected(id) {
var selectedIndexes = getSelectedIndexes();
if (id == "top") {
return;
} else if (id == "selectAll") {
selectAll(true);
} else if (id == "selectNone") {
selectAll(false);
} else if (id == "share" && selectedIndexes != "") {
location.href = "${shareUrl}&" + selectedIndexes;
} else if (id == "download" && selectedIndexes != "") {
location.href = "${downloadUrl}&" + selectedIndexes;
} else if (id == "appendPlaylist" && selectedIndexes != "") {
onAppendPlaylist();
}
$("#moreActions").prop("selectedIndex", 0);
}
function getSelectedIndexes() {
var result = "";
for (var i = 0; i < ${fn:length(model.files)}; i++) {
var checkbox = $("#songIndex" + i);
if (checkbox != null && checkbox.is(":checked")) {
result += "i=" + i + "&";
}
}
return result;
}
function selectAll(b) {
for (var i = 0; i < ${fn:length(model.files)}; i++) {
var checkbox = $("#songIndex" + i);
if (checkbox != null) {
if (b) {
checkbox.attr("checked", "checked");
} else {
checkbox.removeAttr("checked");
}
}
}
}
function toggleStar(mediaFileId, imageId) {
if ($(imageId).attr("src").indexOf("<spring:theme code="ratingOnImage"/>") != -1) {
$(imageId).attr("src", "<spring:theme code="ratingOffImage"/>");
starService.unstar(mediaFileId);
}
else if ($(imageId).attr("src").indexOf("<spring:theme code="ratingOffImage"/>") != -1) {
$(imageId).attr("src", "<spring:theme code="ratingOnImage"/>");
starService.star(mediaFileId);
}
}
function playAll() {
top.playQueue.onPlay(${model.dir.id});
}
function playRandom() {
top.playQueue.onPlayRandom(${model.dir.id}, 40);
}
function addAll() {
top.playQueue.onAdd(${model.dir.id});
}
function playSimilar() {
top.playQueue.onPlaySimilar(${model.dir.id}, 50);
}
function onAppendPlaylist() {
playlistService.getWritablePlaylists(playlistCallback);
}
function playlistCallback(playlists) {
$("#dialog-select-playlist-list").empty();
for (var i = 0; i < playlists.length; i++) {
var playlist = playlists[i];
$("<p class='dense'><b><a href='#' onclick='appendPlaylist(" + playlist.id + ")'>" + escapeHtml(playlist.name)
+ "</a></b></p>").appendTo("#dialog-select-playlist-list");
}
$("#dialog-select-playlist").dialog("open");
}
function appendPlaylist(playlistId) {
$("#dialog-select-playlist").dialog("close");
var mediaFileIds = new Array();
for (var i = 0; i < ${fn:length(model.files)}; i++) {
var checkbox = $("#songIndex" + i);
if (checkbox && checkbox.is(":checked")) {
mediaFileIds.push($("#songId" + i).html());
}
}
playlistService.appendToPlaylist(playlistId, mediaFileIds, function (){
top.left.updatePlaylists();
$().toastmessage("showSuccessToast", "<fmt:message key="playlist.toast.appendtoplaylist"/>");
});
}
function showAllAlbums() {
$("#showAllButton").hide();
$(".albumThumb").show();
}
</script>
<div style="float:left">
<h1>
<img id="starImage" src="<spring:theme code="${not empty model.dir.starredDate ? 'ratingOnImage' : 'ratingOffImage'}"/>"
onclick="toggleStar(${model.dir.id}, '#starImage'); return false;" style="cursor:pointer" alt="">
<span style="vertical-align: middle">
<c:forEach items="${model.ancestors}" var="ancestor">
<sub:url value="main.view" var="ancestorUrl">
<sub:param name="id" value="${ancestor.id}"/>
</sub:url>
<a href="${ancestorUrl}">${fn:escapeXml(ancestor.name)}</a> &raquo;
</c:forEach>
${fn:escapeXml(model.dir.name)}
</span>
<c:if test="${model.averageRating gt 0}">
&nbsp;&nbsp;
<c:import url="rating.jsp">
<c:param name="readonly" value="true"/>
<c:param name="rating" value="${model.averageRating}"/>
</c:import>
</c:if>
</h1>
<c:if test="${not model.partyMode}">
<h2>
<c:if test="${model.navigateUpAllowed}">
<sub:url value="main.view" var="upUrl">
<sub:param name="id" value="${model.parent.id}"/>
</sub:url>
<span class="header"><a href="${upUrl}"><fmt:message key="main.up"/></a></span>
<c:set var="needSep" value="true"/>
</c:if>
<c:if test="${model.user.streamRole}">
<c:if test="${needSep}">|</c:if>
<span class="header"><a href="javascript:playAll()"><fmt:message key="main.playall"/></a></span> |
<span class="header"><a href="javascript:playRandom()"><fmt:message key="main.playrandom"/></a></span> |
<span class="header"><a href="javascript:addAll()"><fmt:message key="main.addall"/></a></span>
<c:set var="needSep" value="true"/>
</c:if>
<c:if test="${model.user.downloadRole}">
<c:if test="${needSep}">|</c:if>
<span class="header"><a href="${downloadUrl}"><fmt:message key="main.downloadall"/></a></span>
<c:set var="needSep" value="true"/>
</c:if>
<c:if test="${model.user.coverArtRole}">
<sub:url value="editTags.view" var="editTagsUrl">
<sub:param name="id" value="${model.dir.id}"/>
</sub:url>
<c:if test="${needSep}">|</c:if>
<span class="header"><a href="${editTagsUrl}"><fmt:message key="main.tags"/></a></span>
<c:set var="needSep" value="true"/>
</c:if>
<c:if test="${model.user.commentRole}">
<c:if test="${needSep}">|</c:if>
<span class="header"><a href="javascript:toggleComment()"><fmt:message key="main.comment"/></a></span>
</c:if>
</h2>
</c:if>
</div>
<%@ include file="viewSelector.jsp" %>
<div style="clear:both"></div>
<div class="detail">
<c:if test="${model.user.commentRole}">
<c:import url="rating.jsp">
<c:param name="id" value="${model.dir.id}"/>
<c:param name="readonly" value="false"/>
<c:param name="rating" value="${model.userRating}"/>
</c:import>
</c:if>
<c:if test="${model.user.shareRole}">
<span class="header"><a href="${shareUrl}"><img src="<spring:theme code="shareSmallImage"/>" alt=""></a>
<a href="${shareUrl}"><fmt:message key="main.sharealbum"/></a> </span> |
</c:if>
<c:if test="${not empty model.artist and not empty model.album}">
<sub:url value="http://www.google.com/search" var="googleUrl" encoding="UTF-8">
<sub:param name="q" value="\"${model.artist}\" \"${model.album}\""/>
</sub:url>
<sub:url value="http://en.wikipedia.org/wiki/Special:Search" var="wikipediaUrl" encoding="UTF-8">
<sub:param name="search" value="\"${model.album}\""/>
<sub:param name="go" value="Go"/>
</sub:url>
<sub:url value="allmusic.view" var="allmusicUrl">
<sub:param name="album" value="${model.album}"/>
</sub:url>
<sub:url value="http://www.last.fm/search" var="lastFmUrl" encoding="UTF-8">
<sub:param name="q" value="\"${model.artist}\" \"${model.album}\""/>
<sub:param name="type" value="album"/>
</sub:url>
<span class="header"><fmt:message key="top.search"/> <a target="_blank" href="${googleUrl}">Google</a></span> |
<span class="header"><a target="_blank" href="${wikipediaUrl}">Wikipedia</a></span> |
<span class="header"><a target="_blank" href="${allmusicUrl}">allmusic</a></span> |
<span class="header"><a target="_blank" href="${lastFmUrl}">Last.fm</a></span> |
<span class="header">
<fmt:message key="main.playcount"><fmt:param value="${model.dir.playCount}"/></fmt:message>
<c:if test="${not empty model.dir.lastPlayed}">
<fmt:message key="main.lastplayed">
<fmt:param><fmt:formatDate type="date" dateStyle="long" value="${model.dir.lastPlayed}"/></fmt:param>
</fmt:message>
</c:if>
</span>
</c:if>
</div>
<div id="comment" class="albumComment"><sub:wiki text="${model.dir.comment}"/></div>
<div id="commentForm" style="display:none">
<form method="post" action="setMusicFileInfo.view">
<input type="hidden" name="action" value="comment">
<input type="hidden" name="id" value="${model.dir.id}">
<textarea name="comment" rows="6" cols="70">${model.dir.comment}</textarea>
<input type="submit" value="<fmt:message key="common.save"/>">
</form>
<fmt:message key="main.wiki"/>
</div>
<script type='text/javascript'>
function toggleComment() {
$("#commentForm").toggle();
$("#comment").toggle();
}
</script>
<table cellpadding="0" style="width:100%;padding-top: 0.3em;padding-bottom: 1em">
<tr style="vertical-align:top;">
<td style="vertical-align:top;padding-bottom: 1em">
<table class="music" style="width: 100%">
<c:forEach items="${model.files}" var="song" varStatus="loopStatus">
<%--@elvariable id="song" type="org.libresonic.player.domain.MediaFile"--%>
<tr style="margin:0;padding:0;border:0">
<c:import url="playButtons.jsp">
<c:param name="id" value="${song.id}"/>
<c:param name="video" value="${song.video and model.player.web}"/>
<c:param name="playEnabled" value="${model.user.streamRole and not model.partyMode}"/>
<c:param name="addEnabled" value="${model.user.streamRole and (not model.partyMode or not song.directory)}"/>
<c:param name="starEnabled" value="true"/>
<c:param name="starred" value="${not empty song.starredDate}"/>
<c:param name="asTable" value="true"/>
</c:import>
<td class="fit"><input type="checkbox" class="checkbox" id="songIndex${loopStatus.count - 1}">
<span id="songId${loopStatus.count - 1}" style="display: none">${song.id}</span></td>
<c:if test="${model.visibility.trackNumberVisible}">
<td class="fit rightalign">
<span class="detail">${song.trackNumber}</span>
</td>
</c:if>
<td class="truncate">
<span class="songTitle" title="${fn:escapeXml(song.title)}">${fn:escapeXml(song.title)}</span>
</td>
<c:if test="${model.visibility.albumVisible}">
<td class="truncate">
<span class="detail" title="${fn:escapeXml(song.albumName)}">${fn:escapeXml(song.albumName)}</span>
</td>
</c:if>
<c:if test="${model.visibility.artistVisible}">
<td class="truncate">
<span class="detail" title="${fn:escapeXml(song.artist)}">${fn:escapeXml(song.artist)}</span>
</td>
</c:if>
<c:if test="${model.visibility.genreVisible}">
<td class="fit rightalign">
<span class="detail">${fn:escapeXml(song.genre)}</span>
</td>
</c:if>
<c:if test="${model.visibility.yearVisible}">
<td class="fit rightalign">
<span class="detail">${song.year}</span>
</td>
</c:if>
<c:if test="${model.visibility.formatVisible}">
<td class="fit rightalign">
<span class="detail">${fn:toLowerCase(song.format)}</span>
</td>
</c:if>
<c:if test="${model.visibility.fileSizeVisible}">
<td class="fit rightalign">
<span class="detail"><sub:formatBytes bytes="${song.fileSize}"/></span>
</td>
</c:if>
<c:if test="${model.visibility.durationVisible}">
<td class="fit rightalign">
<span class="detail">${song.durationString}</span>
</td>
</c:if>
<c:if test="${model.visibility.bitRateVisible}">
<td class="fit rightalign">
<span class="detail">
<c:if test="${not empty song.bitRate}">
${song.bitRate} Kbps ${song.variableBitRate ? "vbr" : ""}
</c:if>
<c:if test="${song.video and not empty song.width and not empty song.height}">
(${song.width}x${song.height})
</c:if>
</span>
</td>
</c:if>
</tr>
</c:forEach>
</table>
</td>
<td class="fit" style="vertical-align:top;" rowspan="3">
<div class="albumThumb">
<c:import url="coverArt.jsp">
<c:param name="albumId" value="${model.dir.id}"/>
<c:param name="coverArtSize" value="${model.coverArtSizeLarge}"/>
<c:param name="showZoom" value="true"/>
<c:param name="showChange" value="${model.user.coverArtRole}"/>
</c:import>
</div>
</td>
<c:if test="${model.showAd}">
<td style="vertical-align:top;width:160px" rowspan="3">
<h2 style="padding-bottom: 1em">Subsonic Premium</h2>
<p style="font-size: 90%">
Upgrade to Subsonic Premium and get:
</p>
<div style="font-size: 90%;padding-bottom: 1em">
<p><a href="http://libresonic.org/pages/apps.jsp" target="_blank">Apps</a> for Android, iPhone, Windows Phone ++.</p>
<p>Video streaming.</p>
<p>Chromecast and Sonos support.</p>
<p>DLNA/UPnP support</p>
<p>Share on Facebook, Twitter, Google+</p>
<p>No ads.</p>
<p>Your personal server address: <em>you</em>.libresonic.org</p>
<p>Podcast receiver.</p>
</div>
<p class="forward" style="white-space: nowrap"><a href="http://libresonic.org/pages/premium.jsp" target="_blank">Get Subsonic Premium</a></p>
</td>
</c:if>
</tr>
<tr>
<td>
<select id="moreActions" onchange="actionSelected(this.options[selectedIndex].id);" style="margin-bottom:1.0em">
<option id="top" selected="selected"><fmt:message key="main.more.selection"/></option>
<option id="selectAll">&nbsp;&nbsp;<fmt:message key="playlist.more.selectall"/></option>
<option id="selectNone">&nbsp;&nbsp;<fmt:message key="playlist.more.selectnone"/></option>
<c:if test="${model.user.downloadRole}">
<option id="download">&nbsp;&nbsp;<fmt:message key="common.download"/></option>
</c:if>
<c:if test="${model.user.shareRole}">
<option id="share">&nbsp;&nbsp;<fmt:message key="main.more.share"/></option>
</c:if>
<option id="appendPlaylist">&nbsp;&nbsp;<fmt:message key="playlist.append"/></option>
</select>
</td>
</tr>
<tr>
<td style="vertical-align:top;height: 100%">
<table class="music indent">
<c:forEach items="${model.subDirs}" var="child" varStatus="loopStatus">
<sub:url value="main.view" var="albumUrl">
<sub:param name="id" value="${child.id}"/>
</sub:url>
<tr>
<c:import url="playButtons.jsp">
<c:param name="id" value="${child.id}"/>
<c:param name="playEnabled" value="${model.user.streamRole and not model.partyModeEnabled}"/>
<c:param name="addEnabled" value="${model.user.streamRole and not model.partyModeEnabled}"/>
<c:param name="asTable" value="true"/>
</c:import>
<td class="truncate"><a href="${albumUrl}" title="${fn:escapeXml(child.name)}">${fn:escapeXml(child.name)}</a></td>
<td></td>
</tr>
</c:forEach>
<c:if test="${model.viewAsList}">
<c:forEach items="${model.sieblingAlbums}" var="album" varStatus="loopStatus">
<tr>
<c:import url="playButtons.jsp">
<c:param name="id" value="${album.id}"/>
<c:param name="playEnabled" value="${model.user.streamRole and not model.partyModeEnabled}"/>
<c:param name="addEnabled" value="${model.user.streamRole and not model.partyModeEnabled}"/>
<c:param name="asTable" value="true"/>
</c:import>
<td class="truncate"><a href="main.view?id=${album.id}" title="${fn:escapeXml(album.name)}">${fn:escapeXml(album.name)}</a></td>
<td class="fit rightalign detail">${album.year}</td>
</tr>
</c:forEach>
</c:if>
</table>
</td>
</tr>
</table>
<c:if test="${not model.viewAsList}">
<div style="float: left">
<c:forEach items="${model.sieblingAlbums}" var="album" varStatus="loopStatus">
<div class="albumThumb" style="display:${loopStatus.count < 10 ? 'inline-block' : 'none'}">
<c:import url="coverArt.jsp">
<c:param name="albumId" value="${album.id}"/>
<c:param name="caption1" value="${fn:escapeXml(album.name)}"/>
<c:param name="caption2" value="${album.year}"/>
<c:param name="captionCount" value="2"/>
<c:param name="coverArtSize" value="${model.coverArtSizeMedium}"/>
<c:param name="showLink" value="true"/>
<c:param name="appearAfter" value="${loopStatus.count * 30}"/>
</c:import>
</div>
</c:forEach>
<c:if test="${fn:length(model.sieblingAlbums) >= 10}">
<input id="showAllButton" class="albumOverflowButton" type="button" value="<fmt:message key="main.showall"/>" onclick="showAllAlbums()">
</c:if>
</div>
</c:if>
<table style="width: 90%">
<tr><td>
<span id="similarArtistsTitle" style="padding-right: 0.5em; display: none"><fmt:message key="main.similarartists"/>:</span>
<span id="similarArtists"></span>
</td></tr>
<tr><td style="padding-bottom: 0.5em">
<div id="similarArtistsRadio" class="forward" style="display: none">
<a href="javascript:playSimilar()"><fmt:message key="main.startradio"/></a>
</div>
</td></tr>
<tr><td style="height: 100%"></td></tr>
</table>
<div id="dialog-select-playlist" title="<fmt:message key="main.addtoplaylist.title"/>" style="display: none;">
<p><fmt:message key="main.addtoplaylist.text"/></p>
<div id="dialog-select-playlist-list"></div>
</div>
</body>
</html>
@@ -0,0 +1,16 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<html><head>
<%@ include file="head.jsp" %>
</head>
<body onload="document.allmusic.submit();" class="mainframe bgcolor1">
<h2><fmt:message key="allmusic.text"><fmt:param value="${album}"/></fmt:message></h2>
<form name="allmusic" action="http://www.allmusic.com/search" method="POST" accept-charset="iso-8859-1">
<input type="hidden" name="search_term" value="${album}"/>
<input type="hidden" name="search_type" value="album"/>
</form>
</body>
</html>
@@ -0,0 +1,337 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<%--
~ This file is part of Libresonic.
~
~ Libresonic is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ Libresonic is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
~
~ Copyright 2014 (C) Sindre Mehus
--%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%--@elvariable id="model" type="java.util.Map"--%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript" src="<c:url value="/script/scripts-2.0.js"/>"></script>
<script type="text/javascript" src="<c:url value='/dwr/util.js'/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/interface/starService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/interface/multiService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/fancyzoom/FancyZoom.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/fancyzoom/FancyZoomHTML.js"/>"></script>
</head><body class="mainframe bgcolor1" onload="init();">
<script type="text/javascript" language="javascript">
var topSongs;
function init() {
setupZoom('<c:url value="/"/>');
<c:if test="${model.showArtistInfo}">
loadArtistInfo();
</c:if>
}
function loadArtistInfo() {
multiService.getArtistInfo(${model.dir.id}, 8, 50, function (artistInfo) {
if (artistInfo.similarArtists.length > 0) {
var html = "";
for (var i = 0; i < artistInfo.similarArtists.length; i++) {
html += "<a href='main.view?id=" + artistInfo.similarArtists[i].mediaFileId + "' target='main'>" +
escapeHtml(artistInfo.similarArtists[i].artistName) + "</a>";
if (i < artistInfo.similarArtists.length - 1) {
html += " <span class='similar-artist-divider'>|</span> ";
}
}
$("#similarArtists").append(html);
$("#similarArtists").show();
$("#similarArtistsTitle").show();
$("#similarArtistsRadio").show();
$("#artistInfoTable").show();
}
if (artistInfo.artistBio && artistInfo.artistBio.biography) {
$("#artistBio").append(artistInfo.artistBio.biography);
if (artistInfo.artistBio.largeImageUrl) {
$("#artistImage").attr("src", artistInfo.artistBio.largeImageUrl);
$("#artistImageZoom").attr("href", artistInfo.artistBio.largeImageUrl);
$("#artistImage").show();
$("#artistInfoTable").show();
}
}
this.topSongs = artistInfo.topSongs;
if (topSongs.length > 0) {
$("#topSongsHeader").show();
$("#playTopSongs").show();
// Delete all the rows except for the "pattern" row
dwr.util.removeAllRows("topSongsBody", { filter:function(tr) {
return (tr.id != "pattern");
}});
// Create a new set cloned from the pattern row
for (var i = 0; i < topSongs.length; i++) {
var song = topSongs[i];
var id = i + 1;
dwr.util.cloneNode("pattern", { idSuffix:id });
if (song.starred) {
$("#starSong" + id).attr("src", "<spring:theme code='ratingOnImage'/>");
} else {
$("#starSong" + id).attr("src", "<spring:theme code='ratingOffImage'/>");
}
$("#rank" + id).html(i + 1);
$("#title" + id).html(song.title);
$("#title" + id).attr("title", song.title);
$("#album" + id).html(song.album);
$("#album" + id).attr("title", song.album);
$("#albumUrl" + id).attr("href", "main.view?id=" + song.id);
$("#artist" + id).html(song.artist);
$("#artist" + id).attr("title", song.artist);
$("#songDuration" + id).html(song.durationAsString);
// Note: show() method causes page to scroll to top.
$("#pattern" + id).css("display", "table-row");
}
}
});
}
function toggleStarTopSong(index, imageId) {
toggleStar(topSongs[index].id, imageId);
}
function toggleStar(mediaFileId, imageId) {
if ($(imageId).attr("src").indexOf("<spring:theme code="ratingOnImage"/>") != -1) {
$(imageId).attr("src", "<spring:theme code="ratingOffImage"/>");
starService.unstar(mediaFileId);
}
else if ($(imageId).attr("src").indexOf("<spring:theme code="ratingOffImage"/>") != -1) {
$(imageId).attr("src", "<spring:theme code="ratingOnImage"/>");
starService.star(mediaFileId);
}
}
function playAll() {
top.playQueue.onPlay(${model.dir.id});
}
function playRandom() {
top.playQueue.onPlayRandom(${model.dir.id}, 40);
}
function addAll() {
top.playQueue.onAdd(${model.dir.id});
}
function playSimilar() {
top.playQueue.onPlaySimilar(${model.dir.id}, 50);
}
function playAllTopSongs() {
top.playQueue.onPlayTopSong(${model.dir.id});
}
function playTopSong(index) {
top.playQueue.onPlayTopSong(${model.dir.id}, index);
}
function addTopSong(index) {
top.playQueue.onAdd(topSongs[index].id);
$().toastmessage('showSuccessToast', '<fmt:message key="main.addlast.toast"/>')
}
function addNextTopSong(index) {
top.playQueue.onAddNext(topSongs[index].id);
$().toastmessage('showSuccessToast', '<fmt:message key="main.addnext.toast"/>')
}
function showAllAlbums() {
$("#showAllButton").hide();
$(".albumThumb").show();
}
</script>
<div style="float:left">
<h1>
<img id="starImage" src="<spring:theme code="${not empty model.dir.starredDate ? 'ratingOnImage' : 'ratingOffImage'}"/>"
onclick="toggleStar(${model.dir.id}, '#starImage'); return false;" style="cursor:pointer" alt="">
<span style="vertical-align: middle">
<c:forEach items="${model.ancestors}" var="ancestor">
<sub:url value="main.view" var="ancestorUrl">
<sub:param name="id" value="${ancestor.id}"/>
</sub:url>
<a href="${ancestorUrl}">${fn:escapeXml(ancestor.name)}</a> &raquo;
</c:forEach>
${fn:escapeXml(model.dir.name)}
</span>
</h1>
<c:if test="${not model.partyMode}">
<h2>
<c:if test="${model.navigateUpAllowed}">
<sub:url value="main.view" var="upUrl">
<sub:param name="id" value="${model.parent.id}"/>
</sub:url>
<span class="header"><a href="${upUrl}"><fmt:message key="main.up"/></a></span>
<c:set var="needSep" value="true"/>
</c:if>
<c:if test="${model.user.streamRole}">
<c:if test="${needSep}">|</c:if>
<span class="header"><a href="javascript:playAll()"><fmt:message key="main.playall"/></a></span> |
<span class="header"><a href="javascript:playRandom(0)"><fmt:message key="main.playrandom"/></a></span> |
<span class="header"><a href="javascript:addAll(0)"><fmt:message key="main.addall"/></a></span>
<c:set var="needSep" value="true"/>
</c:if>
<c:if test="${model.user.commentRole}">
<c:if test="${needSep}">|</c:if>
<span class="header"><a href="javascript:toggleComment()"><fmt:message key="main.comment"/></a></span>
</c:if>
</h2>
</c:if>
</div>
<%@ include file="viewSelector.jsp" %>
<div style="clear:both"></div>
<div id="comment" class="albumComment"><sub:wiki text="${model.dir.comment}"/></div>
<div id="commentForm" style="display:none">
<form method="post" action="setMusicFileInfo.view">
<input type="hidden" name="action" value="comment">
<input type="hidden" name="id" value="${model.dir.id}">
<textarea name="comment" rows="6" cols="70">${model.dir.comment}</textarea>
<input type="submit" value="<fmt:message key="common.save"/>">
</form>
<fmt:message key="main.wiki"/>
</div>
<script type='text/javascript'>
function toggleComment() {
$("#commentForm").toggle();
$("#comment").toggle();
}
</script>
<c:choose>
<c:when test="${model.viewAsList}">
<table class="music indent">
<c:forEach items="${model.subDirs}" var="subDir">
<tr>
<c:import url="playButtons.jsp">
<c:param name="id" value="${subDir.id}"/>
<c:param name="playEnabled" value="${model.user.streamRole and not model.partyModeEnabled}"/>
<c:param name="addEnabled" value="${model.user.streamRole and not model.partyModeEnabled}"/>
<c:param name="asTable" value="true"/>
</c:import>
<td class="truncate"><a href="main.view?id=${subDir.id}" title="${fn:escapeXml(subDir.name)}">${fn:escapeXml(subDir.name)}</a></td>
<td class="fit rightalign detail">${subDir.year}</td>
</tr>
</c:forEach>
</table>
</c:when>
<c:otherwise>
<table class="music indent">
<c:forEach items="${model.subDirs}" var="subDir">
<c:if test="${not subDir.album}">
<tr>
<c:import url="playButtons.jsp">
<c:param name="id" value="${subDir.id}"/>
<c:param name="playEnabled" value="${model.user.streamRole and not model.partyModeEnabled}"/>
<c:param name="addEnabled" value="${model.user.streamRole and not model.partyModeEnabled}"/>
<c:param name="asTable" value="true"/>
</c:import>
<td class="truncate"><a href="main.view?id=${subDir.id}" title="${fn:escapeXml(subDir.name)}">${fn:escapeXml(subDir.name)}</a></td>
<td class="fit rightalign detail">${subDir.year}</td>
</tr>
</c:if>
</c:forEach>
</table>
<div style="float: left;padding-top: 1.5em">
<c:set var="albumCount" value="0"/>
<c:forEach items="${model.subDirs}" var="subDir" varStatus="loopStatus">
<c:if test="${subDir.album}">
<c:set var="albumCount" value="${albumCount + 1}"/>
<div class="albumThumb" style="display:${loopStatus.count < 40 ? 'inline-block' : 'none'}">
<c:import url="coverArt.jsp">
<c:param name="albumId" value="${subDir.id}"/>
<c:param name="caption1" value="${fn:escapeXml(subDir.name)}"/>
<c:param name="caption2" value="${subDir.year}"/>
<c:param name="captionCount" value="2"/>
<c:param name="coverArtSize" value="${model.coverArtSizeMedium}"/>
<c:param name="showLink" value="true"/>
<c:param name="appearAfter" value="${loopStatus.count * 30}"/>
</c:import>
</div>
</c:if>
</c:forEach>
<c:if test="${albumCount >= 40}">
<input id="showAllButton" class="albumOverflowButton" type="button" value="<fmt:message key="main.showall"/>" onclick="showAllAlbums()">
</c:if>
</div>
</c:otherwise>
</c:choose>
<table id="artistInfoTable" style="padding:2em;clear:both;display:none" class="bgcolor2 dropshadow">
<tr>
<td rowspan="5" style="vertical-align: top">
<a id="artistImageZoom" rel="zoom" href="void">
<img id="artistImage" class="dropshadow" alt="" style="margin-right:2em; display:none; max-width:300px; max-height:300px">
</a>
</td>
<td style="text-align:center"><h2>${fn:escapeXml(model.dir.name)}</h2></td>
</tr>
<tr>
<td id="artistBio" style="padding-bottom: 0.5em"></td>
</tr>
<tr><td style="padding-bottom: 0.5em">
<span id="similarArtistsTitle" style="padding-right: 0.5em; display: none"><fmt:message key="main.similarartists"/>:</span>
<span id="similarArtists"></span>
</td></tr>
<tr><td style="text-align:center">
<input id="similarArtistsRadio" style="display:none;margin-top:1em;margin-right:0.3em;cursor:pointer" type="button" value="<fmt:message key="main.startradio"/>" onclick="playSimilar()">
<input id="playTopSongs" style="display:none;margin-top:1em;margin-left:0.3em;cursor:pointer" type="button" value="<fmt:message key="main.playtopsongs"/>" onclick="playAllTopSongs()">
</td></tr>
<tr><td style="height: 100%"></td></tr>
</table>
<h2 id="topSongsHeader" style="display:none; padding-top:1em"><fmt:message key="main.topsongs"/></h2>
<table class="music indent">
<tbody id="topSongsBody">
<tr id="pattern" style="display:none;margin:0;padding:0;border:0">
<td class="fit">
<img id="starSong" onclick="toggleStarTopSong(this.id.substring(8) - 1, '#starSong' + this.id.substring(8))" src="<spring:theme code="ratingOffImage"/>"
style="cursor:pointer" alt="" title=""></td>
<td class="fit">
<img id="play" src="<spring:theme code="playImage"/>" alt="<fmt:message key="common.play"/>" title="<fmt:message key="common.play"/>"
style="padding-right:0.1em;cursor:pointer" onclick="playTopSong(this.id.substring(4) - 1)"></td>
<td class="fit">
<img id="add" src="<spring:theme code="addImage"/>" alt="<fmt:message key="common.add"/>" title="<fmt:message key="common.add"/>"
style="padding-right:0.1em;cursor:pointer" onclick="addTopSong(this.id.substring(3) - 1)"></td>
<td class="fit" style="padding-right:30px">
<img id="addNext" src="<spring:theme code="addNextImage"/>" alt="<fmt:message key="main.addnext"/>" title="<fmt:message key="main.addnext"/>"
style="padding-right:0.1em;cursor:pointer" onclick="addNextTopSong(this.id.substring(7) - 1)"></td>
<td class="fit rightalign"><span id="rank" class="detail">Rank</span></td>
<td class="truncate"><span id="title" class="songTitle">Title</span></td>
<td class="truncate"><a id="albumUrl" target="main"><span id="album" class="detail">Album</span></a></td>
<td class="truncate"><span id="artist" class="detail">Artist</span></td>
<td class="fit rightalign"><span id="songDuration" class="detail">Duration</span></td>
</tr>
</tbody>
</table>
</body>
</html>
@@ -0,0 +1,36 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<html>
<head>
<%@ include file="head.jsp" %>
</head>
<body class="mainframe bgcolor1">
<h1>
<img src="<spring:theme code="settingsImage"/>" alt=""/>
<span style="vertical-align: middle"><fmt:message key="avataruploadresult.title"/></span>
</h1>
<c:choose>
<c:when test="${empty model.error}">
<p>
<fmt:message key="avataruploadresult.success"><fmt:param value="${model.avatar.name}"/></fmt:message>
<sub:url value="avatar.view" var="avatarUrl">
<sub:param name="username" value="${model.username}"/>
<sub:param name="forceCustom" value="true"/>
</sub:url>
<img src="${avatarUrl}" alt="${model.avatar.name}" width="${model.avatar.width}"
height="${model.avatar.height}" style="padding-left:2em"/>
</p>
</c:when>
<c:otherwise>
<p class="warning">
<fmt:message key="avataruploadresult.failure"/>
</p>
</c:otherwise>
</c:choose>
<div class="back"><a href="personalSettings.view?"><fmt:message key="common.back"/></a></div>
</body>
</html>
@@ -0,0 +1,209 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<html><head>
<%@ include file="head.jsp" %>
<script type="text/javascript" src="<c:url value="/dwr/interface/coverArtService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/util.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/prototype.js"/>"></script>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript" language="javascript">
dwr.engine.setErrorHandler(null);
google.load('search', '1');
var imageSearch;
function setImage(imageUrl) {
$("wait").show();
$("result").hide();
$("success").hide();
$("error").hide();
$("errorDetails").hide();
$("noImagesFound").hide();
var id = dwr.util.getValue("id");
coverArtService.setCoverArtImage(id, imageUrl, setImageComplete);
}
function setImageComplete(errorDetails) {
$("wait").hide();
if (errorDetails != null) {
dwr.util.setValue("errorDetails", "<br/>" + errorDetails, { escapeHtml:false });
$("error").show();
$("errorDetails").show();
} else {
$("success").show();
}
}
function searchComplete() {
$("wait").hide();
if (imageSearch.results && imageSearch.results.length > 0) {
var images = $("images");
images.innerHTML = "";
var results = imageSearch.results;
for (var i = 0; i < results.length; i++) {
var result = results[i];
var node = $("template").cloneNode(true);
// Rename results to https to avoid mixed contents.
result.tbUrl = result.tbUrl.replace('http://', 'https://');
var link = node.getElementsByClassName("search-result-link")[0];
link.href = "javascript:setImage('" + result.url + "');";
var thumbnail = node.getElementsByClassName("search-result-thumbnail")[0];
thumbnail.src = result.tbUrl;
var title = node.getElementsByClassName("search-result-title")[0];
title.innerHTML = result.contentNoFormatting.truncate(30);
var dimension = node.getElementsByClassName("search-result-dimension")[0];
dimension.innerHTML = result.width + " × " + result.height;
var url = node.getElementsByClassName("search-result-url")[0];
url.innerHTML = result.visibleUrl;
node.show();
images.appendChild(node);
}
$("result").show();
addPaginationLinks(imageSearch);
} else {
$("noImagesFound").show();
}
}
function addPaginationLinks() {
// To paginate search results, use the cursor function.
var cursor = imageSearch.cursor;
var curPage = cursor.currentPageIndex; // check what page the app is on
var pagesDiv = document.createElement("div");
for (var i = 0; i < cursor.pages.length; i++) {
var page = cursor.pages[i];
var label;
if (curPage == i) {
// If we are on the current page, then don"t make a link.
label = document.createElement("b");
} else {
// Create links to other pages using gotoPage() on the searcher.
label = document.createElement("a");
label.href = "javascript:imageSearch.gotoPage(" + i + ");";
}
label.innerHTML = page.label;
label.style.marginRight = "1em";
pagesDiv.appendChild(label);
}
// Create link to next page.
if (curPage < cursor.pages.length - 1) {
var next = document.createElement("a");
next.href = "javascript:imageSearch.gotoPage(" + (curPage + 1) + ");";
next.innerHTML = "<fmt:message key="common.next"/>";
next.style.marginLeft = "1em";
pagesDiv.appendChild(next);
}
var pages = $("pages");
pages.innerHTML = "";
pages.appendChild(pagesDiv);
}
function search() {
$("wait").show();
$("result").hide();
$("success").hide();
$("error").hide();
$("errorDetails").hide();
$("noImagesFound").hide();
var query = dwr.util.getValue("query");
imageSearch.execute(query);
}
function onLoad() {
imageSearch = new google.search.ImageSearch();
imageSearch.setSearchCompleteCallback(this, searchComplete, null);
imageSearch.setNoHtmlGeneration();
imageSearch.setResultSetSize(8);
google.search.Search.getBranding("branding");
$("template").hide();
search();
}
google.setOnLoadCallback(onLoad);
</script>
</head>
<body class="mainframe bgcolor1">
<h1><fmt:message key="changecoverart.title"/></h1>
<form action="javascript:search()">
<table class="indent"><tr>
<td><input id="query" name="query" size="70" type="text" value="${model.artist} ${model.album}" onclick="select()"/></td>
<td style="padding-left:0.5em"><input type="submit" value="<fmt:message key="changecoverart.search"/>"/></td>
</tr></table>
</form>
<form action="javascript:setImage(dwr.util.getValue('url'))">
<table><tr>
<input id="id" type="hidden" name="id" value="${model.id}"/>
<td><label for="url"><fmt:message key="changecoverart.address"/></label></td>
<td style="padding-left:0.5em"><input type="text" name="url" size="50" id="url" value="http://" onclick="select()"/></td>
<td style="padding-left:0.5em"><input type="submit" value="<fmt:message key="common.ok"/>"></td>
</tr></table>
</form>
<sub:url value="main.view" var="backUrl"><sub:param name="id" value="${model.id}"/></sub:url>
<div style="padding-top:0.5em;padding-bottom:0.5em">
<div class="back"><a href="${backUrl}"><fmt:message key="common.back"/></a></div>
</div>
<h2 id="wait" style="display:none"><fmt:message key="changecoverart.wait"/></h2>
<h2 id="noImagesFound" style="display:none"><fmt:message key="changecoverart.noimagesfound"/></h2>
<h2 id="success" style="display:none"><fmt:message key="changecoverart.success"/></h2>
<h2 id="error" style="display:none"><fmt:message key="changecoverart.error"/></h2>
<div id="errorDetails" class="warning" style="display:none">
</div>
<div id="result">
<div id="pages" style="float:left;padding-left:0.5em;padding-top:0.5em">
</div>
<div id="branding" style="float:right;padding-right:1em;padding-top:0.5em">
</div>
<div style="clear:both;">
</div>
<div id="images" style="width:100%;padding-bottom:2em">
</div>
<div style="clear:both;">
</div>
</div>
<div id="template" style="float:left; height:190px; width:220px;padding:0.5em;position:relative">
<div style="position:absolute;bottom:0">
<a class="search-result-link"><img class="search-result-thumbnail" style="padding:1px; border:1px solid #021a40; background-color:white;"></a>
<div class="search-result-title"></div>
<div class="search-result-dimension detail"></div>
<div class="search-result-url detail"></div>
</div>
</div>
</body></html>
@@ -0,0 +1,143 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<%@ include file="include.jsp" %>
<%--
PARAMETERS
albumId: ID of album.
playlistId: ID of playlist.
podcastChannelId: ID of podcast channel
coverArtSize: Height and width of cover art.
caption1: Caption line 1
caption2: Caption line 2
caption3: Caption line 3
captionCount: Number of caption lines to display (default 0)
showLink: Whether to make the cover art image link to the album page.
showZoom: Whether to display a link for zooming the cover art.
showChange: Whether to display a link for changing the cover art.
appearAfter: Fade in after this many milliseconds, or nil if no fading in should happen.
--%>
<c:choose>
<c:when test="${empty param.coverArtSize}">
<c:set var="size" value="auto"/>
</c:when>
<c:otherwise>
<c:set var="size" value="${param.coverArtSize}px"/>
</c:otherwise>
</c:choose>
<c:set var="captionCount" value="${empty param.captionCount ? 0 : param.captionCount}"/>
<str:randomString count="5" type="alphabet" var="divId"/>
<str:randomString count="5" type="alphabet" var="imgId"/>
<str:randomString count="5" type="alphabet" var="playId"/>
<div class="coverart dropshadow">
<div style="width:${size};max-width:${size};height:${size};max-height:${size};cursor:pointer" title="${param.caption1}" id="${divId}">
<c:if test="${not empty param.albumId}">
<c:url value="main.view" var="targetUrl">
<c:param name="id" value="${param.albumId}"/>
</c:url>
</c:if>
<c:if test="${not empty param.playlistId}">
<c:url value="playlist.view" var="targetUrl">
<c:param name="id" value="${param.playlistId}"/>
</c:url>
</c:if>
<c:if test="${not empty param.podcastChannelId}">
<c:url value="podcastChannel.view" var="targetUrl">
<c:param name="id" value="${param.podcastChannelId}"/>
</c:url>
</c:if>
<c:url value="/coverArt.view" var="coverArtUrl">
<c:if test="${not empty param.coverArtSize}">
<c:param name="size" value="${param.coverArtSize}"/>
</c:if>
<c:if test="${not empty param.albumId}">
<c:param name="id" value="${param.albumId}"/>
</c:if>
<c:if test="${not empty param.podcastChannelId}">
<c:param name="id" value="pod-${param.podcastChannelId}"/>
</c:if>
<c:if test="${not empty param.playlistId}">
<c:param name="id" value="pl-${param.playlistId}"/>
</c:if>
</c:url>
<c:url value="/coverArt.view" var="zoomCoverArtUrl">
<c:param name="id" value="${param.albumId}"/>
</c:url>
<div style="position: relative; width: 0; height: 0">
<img src="<spring:theme code="playOverlayImage"/>" id="${playId}"
style="position: relative; top: 8px; left: 8px; z-index: 2; display:none" >
</div>
<c:choose>
<c:when test="${param.showLink}"><a href="${targetUrl}" title="${param.caption1}"></c:when>
<c:when test="${param.showZoom}"><a href="${zoomCoverArtUrl}" rel="zoom" title="${param.caption1}"></c:when>
</c:choose>
<img src="${coverArtUrl}" id="${imgId}" alt="${param.caption1}"
style="display:none">
<c:if test="${param.showLink or param.showZoom}"></a></c:if>
</div>
<c:if test="${captionCount gt 0}">
<div class="caption1" style="width:${param.coverArtSize - 16}px"><a href="${targetUrl}" title="${param.caption1}">${param.caption1}</a></div>
</c:if>
<c:if test="${captionCount gt 1}">
<div class="caption2" style="width:${param.coverArtSize - 16}px">${param.caption2}&nbsp;</div>
</c:if>
<c:if test="${captionCount gt 2}">
<div class="caption3" style="width:${param.coverArtSize - 16}px">${param.caption3}&nbsp;</div>
</c:if>
</div>
<c:if test="${param.showChange or param.showZoom}">
<div style="padding-top:6px;text-align:right">
<c:if test="${param.showChange}">
<c:url value="/changeCoverArt.view" var="changeCoverArtUrl">
<c:param name="id" value="${param.albumId}"/>
</c:url>
<a class="detail" href="${changeCoverArtUrl}"><fmt:message key="coverart.change"/></a>
</c:if>
<c:if test="${param.showZoom and param.showChange}">
|
</c:if>
<c:if test="${param.showZoom}">
<a class="detail" rel="zoom" title="${param.caption1}" href="${zoomCoverArtUrl}"><fmt:message key="coverart.zoom"/></a>
</c:if>
</div>
</c:if>
<script type="text/javascript">
$(document).ready(function () {
setTimeout("$('#${imgId}').fadeIn(500)", ${empty param.appearAfter ? 0 : param.appearAfter});
});
$("#${divId}").mouseenter(function () {
$("#${playId}").show();
$("#${imgId}").stop();
$("#${imgId}").animate({opacity: 0.7}, 150);
});
$("#${divId}").mouseleave(function () {
$("#${playId}").hide();
$("#${imgId}").stop();
$("#${imgId}").animate({opacity: 1.0}, 150);
});
$("#${playId}").click(function () {
<c:if test="${not empty param.albumId}">
top.playQueue.onPlay(${param.albumId});
</c:if>
<c:if test="${not empty param.playlistId}">
top.playQueue.onPlayPlaylist(${param.playlistId});
</c:if>
<c:if test="${not empty param.podcastChannelId}">
top.playQueue.onPlayPodcastChannel(${param.podcastChannelId});
</c:if>
});
</script>
@@ -0,0 +1,56 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<html>
<head>
<%@ include file="head.jsp" %>
<script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script>
</head>
<body class="mainframe bgcolor1">
<h1>
<img src="<spring:theme code="shareImage"/>" alt="">
<span style="vertical-align: middle"><fmt:message key="share.title"/></span>
</h1>
<c:choose>
<c:when test="${model.urlRedirectionEnabled}">
<fmt:message key="share.warning"/>
<p>
<a href="http://www.facebook.com/sharer.php?u=${model.playUrl}" target="_blank"><img src="<spring:theme code="shareFacebookImage"/>" alt=""></a>&nbsp;
<a href="http://www.facebook.com/sharer.php?u=${model.playUrl}" target="_blank"><fmt:message key="share.facebook"/></a>
</p>
<p>
<a href="http://twitter.com/?status=Listening to ${model.playUrl}" target="_blank"><img src="<spring:theme code="shareTwitterImage"/>" alt=""></a>&nbsp;
<a href="http://twitter.com/?status=Listening to ${model.playUrl}" target="_blank"><fmt:message key="share.twitter"/></a>
</p>
<p>
<g:plusone size="small" annotation="none" href="${model.playUrl}"></g:plusone>&nbsp;<fmt:message key="share.googleplus"/>
</p>
<p>
<fmt:message key="share.link">
<fmt:param>${model.playUrl}</fmt:param>
</fmt:message>
</p>
</c:when>
<c:otherwise>
<p>
<fmt:message key="share.disabled"/>
</p>
</c:otherwise>
</c:choose>
<c:set var="licenseInfo" value="${model.licenseInfo}"/>
<%@ include file="licenseNotice.jsp" %>
<div style="padding-top:1em">
<c:if test="${not empty model.dir}">
<sub:url value="main.view" var="backUrl"><sub:param name="path" value="${model.dir.path}"/></sub:url>
<div class="back" style="float:left;padding-right:10pt"><a href="${backUrl}"><fmt:message key="common.back"/></a></div>
</c:if>
<c:if test="${model.user.settingsRole}">
<div class="forward" style="float:left"><a href="shareSettings.view"><fmt:message key="share.manage"/></a></div>
</c:if>
</div>
</body>
</html>
@@ -0,0 +1,45 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<html><head>
<%@ include file="head.jsp" %>
</head><body class="mainframe bgcolor1" onload="document.getElementById('query').focus()">
<h1>Database query</h1>
<form method="post" action="db.view">
<textarea rows="10" cols="80" id="query" name="query" style="margin-top:1em">${model.query}</textarea>
<input type="submit" value="<fmt:message key="common.ok"/>">
</form>
<c:if test="${not empty model.result}">
<h1 style="margin-top:2em">Result</h1>
<table class="indent ruleTable">
<c:forEach items="${model.result}" var="row" varStatus="loopStatus">
<c:if test="${loopStatus.count == 1}">
<tr>
<c:forEach items="${row}" var="entry">
<td class="ruleTableHeader">${entry.key}</td>
</c:forEach>
</tr>
</c:if>
<tr>
<c:forEach items="${row}" var="entry">
<td class="ruleTableCell">${entry.value}</td>
</c:forEach>
</tr>
</c:forEach>
</table>
</c:if>
<c:if test="${not empty model.error}">
<h1 style="margin-top:2em">Error</h1>
<p class="warning">
${model.error}
</p>
</c:if>
</body></html>
@@ -0,0 +1,66 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<%--
~ This file is part of Libresonic.
~
~ Libresonic is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ Libresonic is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
~
~ Copyright 2013 (C) Sindre Mehus
--%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript" src="<c:url value="/script/scripts-2.0.js"/>"></script>
</head>
<body class="mainframe bgcolor1">
<script type="text/javascript" src="<c:url value="/script/wz_tooltip.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/tip_balloon.js"/>"></script>
<c:import url="settingsHeader.jsp">
<c:param name="cat" value="dlna"/>
<c:param name="toast" value="${model.toast}"/>
</c:import>
<form method="post" action="dlnaSettings.view">
<div>
<input type="checkbox" name="dlnaEnabled" id="dlnaEnabled" class="checkbox"
<c:if test="${model.dlnaEnabled}">checked="checked"</c:if>/>
<label for="dlnaEnabled"><fmt:message key="dlnasettings.enabled"/></label>
</div>
<p class="detail" style="width:60%;white-space:normal">
<fmt:message key="dlnasettings.description"/>
</p>
<div>
<fmt:message key="dlnasettings.servername"/>
<input name="dlnaServerName" id="dlnaServerName" size="40"
value="<c:out value="${model.dlnaServerName}" escapeXml="true"/>"/>
</div>
<p class="detail" style="width:60%;white-space:normal;padding-top:0">
<fmt:message key="dlnasettings.servername.description"/>
</p>
<c:set var="licenseInfo" value="${model.licenseInfo}"/>
<%@ include file="licenseNotice.jsp" %>
<p>
<input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
<input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
</p>
</form>
</body></html>
@@ -0,0 +1,164 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<html><head>
<%@ include file="head.jsp" %>
<script type="text/javascript" src="<c:url value="/dwr/interface/tagService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/util.js"/>"></script>
</head>
<body class="mainframe bgcolor1">
<script type="text/javascript" language="javascript">
var index = 0;
var fileCount = ${fn:length(model.songs)};
function setArtist() {
var artist = dwr.util.getValue("artistAll");
for (i = 0; i < fileCount; i++) {
dwr.util.setValue("artist" + i, artist);
}
}
function setAlbum() {
var album = dwr.util.getValue("albumAll");
for (i = 0; i < fileCount; i++) {
dwr.util.setValue("album" + i, album);
}
}
function setYear() {
var year = dwr.util.getValue("yearAll");
for (i = 0; i < fileCount; i++) {
dwr.util.setValue("year" + i, year);
}
}
function setGenre() {
var genre = dwr.util.getValue("genreAll");
for (i = 0; i < fileCount; i++) {
dwr.util.setValue("genre" + i, genre);
}
}
function suggestTitle() {
for (i = 0; i < fileCount; i++) {
var title = dwr.util.getValue("suggestedTitle" + i);
dwr.util.setValue("title" + i, title);
}
}
function resetTitle() {
for (i = 0; i < fileCount; i++) {
var title = dwr.util.getValue("originalTitle" + i);
dwr.util.setValue("title" + i, title);
}
}
function suggestTrack() {
for (i = 0; i < fileCount; i++) {
var track = dwr.util.getValue("suggestedTrack" + i);
dwr.util.setValue("track" + i, track);
}
}
function resetTrack() {
for (i = 0; i < fileCount; i++) {
var track = dwr.util.getValue("originalTrack" + i);
dwr.util.setValue("track" + i, track);
}
}
function updateTags() {
document.getElementById("save").disabled = true;
index = 0;
dwr.util.setValue("errors", "");
for (i = 0; i < fileCount; i++) {
dwr.util.setValue("status" + i, "");
}
updateNextTag();
}
function updateNextTag() {
var id = dwr.util.getValue("id" + index);
var artist = dwr.util.getValue("artist" + index);
var track = dwr.util.getValue("track" + index);
var album = dwr.util.getValue("album" + index);
var title = dwr.util.getValue("title" + index);
var year = dwr.util.getValue("year" + index);
var genre = dwr.util.getValue("genre" + index);
dwr.util.setValue("status" + index, "<fmt:message key="edittags.working"/>");
tagService.setTags(id, track, artist, album, title, year, genre, setTagsCallback);
}
function setTagsCallback(result) {
var message;
if (result == "SKIPPED") {
message = "<fmt:message key="edittags.skipped"/>";
} else if (result == "UPDATED") {
message = "<b><fmt:message key="edittags.updated"/></b>";
} else {
message = "<div class='warning'><fmt:message key="edittags.error"/></div>"
var errors = dwr.util.getValue("errors");
errors += "<br>" + result + "<br>";
dwr.util.setValue("errors", errors, { escapeHtml:false });
}
dwr.util.setValue("status" + index, message, { escapeHtml:false });
index++;
if (index < fileCount) {
updateNextTag();
} else {
document.getElementById("save").disabled = false;
}
}
</script>
<h1><fmt:message key="edittags.title"/></h1>
<sub:url value="main.view" var="backUrl"><sub:param name="id" value="${model.id}"/></sub:url>
<div class="back"><a href="${backUrl}"><fmt:message key="common.back"/></a></div>
<table class="ruleTable indent">
<tr>
<th class="ruleTableHeader"><fmt:message key="edittags.file"/></th>
<th class="ruleTableHeader"><fmt:message key="edittags.track"/></th>
<th class="ruleTableHeader"><fmt:message key="edittags.songtitle"/></th>
<th class="ruleTableHeader"><fmt:message key="edittags.artist"/></th>
<th class="ruleTableHeader"><fmt:message key="edittags.album"/></th>
<th class="ruleTableHeader"><fmt:message key="edittags.year"/></th>
<th class="ruleTableHeader"><fmt:message key="edittags.genre"/></th>
<th class="ruleTableHeader" width="60pt"><fmt:message key="edittags.status"/></th>
</tr>
<tr>
<th class="ruleTableHeader"/>
<th class="ruleTableHeader"><a href="javascript:suggestTrack()"><fmt:message key="edittags.suggest.short"/></a> |
<a href="javascript:resetTrack()"><fmt:message key="edittags.reset.short"/></a></th>
<th class="ruleTableHeader"><a href="javascript:suggestTitle()"><fmt:message key="edittags.suggest"/></a> |
<a href="javascript:resetTitle()"><fmt:message key="edittags.reset"/></a></th>
<th class="ruleTableHeader" style="white-space: nowrap"><input type="text" name="artistAll" size="15" onkeypress="dwr.util.onReturn(event, setArtist)" value="${model.defaultArtist}"/>&nbsp;<a href="javascript:setArtist()"><fmt:message key="edittags.set"/></a></th>
<th class="ruleTableHeader" style="white-space: nowrap"><input type="text" name="albumAll" size="15" onkeypress="dwr.util.onReturn(event, setAlbum)" value="${model.defaultAlbum}"/>&nbsp;<a href="javascript:setAlbum()"><fmt:message key="edittags.set"/></a></th>
<th class="ruleTableHeader" style="white-space: nowrap"><input type="text" name="yearAll" size="5" onkeypress="dwr.util.onReturn(event, setYear)" value="${model.defaultYear}"/>&nbsp;<a href="javascript:setYear()"><fmt:message key="edittags.set"/></a></th>
<th class="ruleTableHeader" style="white-space: nowrap">
<select name="genreAll" style="width:7em">
<option value=""/>
<c:forEach items="${model.allGenres}" var="genre">
<option ${genre eq model.defaultGenre ? "selected" : ""} value="${genre}">${genre}</option>
</c:forEach>
</select>
<a href="javascript:setGenre()"><fmt:message key="edittags.set"/></a>
</th>
<th class="ruleTableHeader"/>
</tr>
<c:forEach items="${model.songs}" var="song" varStatus="loopStatus">
<tr>
<str:truncateNicely lower="25" upper="25" var="fileName">${song.fileName}</str:truncateNicely>
<input type="hidden" name="id${loopStatus.count - 1}" value="${song.id}"/>
<input type="hidden" name="suggestedTitle${loopStatus.count - 1}" value="${song.suggestedTitle}"/>
<input type="hidden" name="originalTitle${loopStatus.count - 1}" value="${song.title}"/>
<input type="hidden" name="suggestedTrack${loopStatus.count - 1}" value="${song.suggestedTrack}"/>
<input type="hidden" name="originalTrack${loopStatus.count - 1}" value="${song.track}"/>
<td class="ruleTableCell" title="${song.fileName}">${fileName}</td>
<td class="ruleTableCell"><input type="text" size="5" name="track${loopStatus.count - 1}" value="${song.track}"/></td>
<td class="ruleTableCell"><input type="text" size="30" name="title${loopStatus.count - 1}" value="${song.title}"/></td>
<td class="ruleTableCell"><input type="text" size="15" name="artist${loopStatus.count - 1}" value="${song.artist}"/></td>
<td class="ruleTableCell"><input type="text" size="15" name="album${loopStatus.count - 1}" value="${song.album}"/></td>
<td class="ruleTableCell"><input type="text" size="5" name="year${loopStatus.count - 1}" value="${song.year}"/></td>
<td class="ruleTableCell"><input type="text" name="genre${loopStatus.count - 1}" value="${song.genre}" style="width:7em"/></td>
<td class="ruleTableCell"><div id="status${loopStatus.count - 1}"/></td>
</tr>
</c:forEach>
</table>
<p><input type="submit" id="save" value="<fmt:message key="common.save"/>" onclick="javascript:updateTags()"/></p>
<div class="warning" id="errors"/>
</body></html>
@@ -0,0 +1,106 @@
<%--@elvariable id="model" type="java.util.Map"--%>
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<html>
<head>
<%@ include file="head.jsp" %>
<script type="text/javascript" src="<c:url value="/script/swfobject.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/prototype.js"/>"></script>
<meta name="og:type" content="album"/>
<c:if test="${not empty model.songs}">
<sub:url value="/coverArt.view" var="coverArtUrl">
<sub:param name="id" value="${model.songs[0].id}"/>
<sub:param name="size" value="500"/>
</sub:url>
<meta name="og:title" content="${fn:escapeXml(model.songs[0].artist)} &mdash; ${fn:escapeXml(model.songs[0].albumName)}"/>
<meta name="og:image" content="${model.redirectUrl}${coverArtUrl}"/>
</c:if>
<script type="text/javascript">
function init() {
var flashvars = {
id:"player1",
screencolor:"000000",
frontcolor:"<spring:theme code="textColor"/>",
backcolor:"<spring:theme code="backgroundColor"/>",
"playlist.position": "bottom",
"playlist.size": 300,
repeat: "list"
};
var params = {
allowfullscreen:"true",
allowscriptaccess:"always"
};
var attributes = {
id:"player1",
name:"player1"
};
swfobject.embedSWF("<c:url value="/flash/jw-player-5.10.swf"/>", "placeholder", "500", "600", "9.0.0", false, flashvars, params, attributes);
}
function playerReady(thePlayer) {
var player = $("player1");
var list = new Array();
<c:forEach items="${model.songs}" var="song" varStatus="loopStatus">
<%--@elvariable id="song" type="org.libresonic.player.domain.MediaFile"--%>
<sub:url value="/stream" var="streamUrl">
<sub:param name="id" value="${song.id}"/>
<sub:param name="player" value="${model.player}"/>
<sub:param name="maxBitRate" value="1200"/>
</sub:url>
<sub:url value="/coverArt.view" var="coverUrl">
<sub:param name="id" value="${song.id}"/>
<sub:param name="size" value="500"/>
</sub:url>
// TODO: Use video provider for aac, m4a
list[${loopStatus.count - 1}] = {
file: "${streamUrl}",
image: "${coverUrl}",
title: "${fn:escapeXml(song.title)}",
provider: "${song.video ? "video" : "sound"}",
description: "${fn:escapeXml(song.artist)}"
};
<c:if test="${not empty song.durationSeconds}">
list[${loopStatus.count-1}].duration = ${song.durationSeconds};
</c:if>
</c:forEach>
player.sendEvent("LOAD", list);
player.sendEvent("PLAY");
}
</script>
</head>
<body class="mainframe bgcolor1" style="padding-top:2em" onload="init();">
<div style="margin:auto;width:500px">
<h1>
<c:choose>
<c:when test="${empty model.share or empty model.songs}">
Sorry, the content is not available.
</c:when>
<c:otherwise>
${empty model.share.description ? model.songs[0].artist : fn:escapeXml(model.share.description)}
</c:otherwise>
</c:choose>
</h1>
<div style="float:left;padding-right:1.5em">
<h2 style="margin:0;">${empty model.share.description ? model.songs[0].albumName : fn:escapeXml(model.share.username)}</h2>
</div>
<div class="detail" style="float:right">Streaming by <a href="http://libresonic.org/" target="_blank"><b>Libresonic</b></a></div>
<div style="clear:both;padding-top:1em">
<div id="placeholder">
<a href="http://www.adobe.com/go/getflashplayer" target="_blank"><fmt:message key="playlist.getflash"/></a>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,175 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<%--@elvariable id="command" type="org.libresonic.player.command.GeneralSettingsCommand"--%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript" src="<c:url value="/script/scripts-2.0.js"/>"></script>
</head>
<body class="mainframe bgcolor1">
<script type="text/javascript" src="<c:url value="/script/wz_tooltip.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/tip_balloon.js"/>"></script>
<c:import url="settingsHeader.jsp">
<c:param name="cat" value="general"/>
<c:param name="toast" value="${command.toast}"/>
</c:import>
<form:form method="post" action="generalSettings.view" commandName="command">
<table style="white-space:nowrap" class="indent">
<tr>
<td><fmt:message key="generalsettings.musicmask"/></td>
<td>
<form:input path="musicFileTypes" size="70"/>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="musicmask"/></c:import>
</td>
</tr>
<tr>
<td><fmt:message key="generalsettings.videomask"/></td>
<td>
<form:input path="videoFileTypes" size="70"/>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="videomask"/></c:import>
</td>
</tr>
<tr>
<td><fmt:message key="generalsettings.coverartmask"/></td>
<td>
<form:input path="coverArtFileTypes" size="70"/>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="coverartmask"/></c:import>
</td>
</tr>
<tr>
<td><fmt:message key="generalsettings.playlistfolder"/></td>
<td>
<form:input path="playlistFolder" size="70"/>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="playlistfolder"/></c:import>
</td>
</tr>
<tr><td colspan="2">&nbsp;</td></tr>
<tr>
<td><fmt:message key="generalsettings.index"/></td>
<td>
<form:input path="index" size="70"/>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="index"/></c:import>
</td>
</tr>
<tr>
<td><fmt:message key="generalsettings.ignoredarticles"/></td>
<td>
<form:input path="ignoredArticles" size="70"/>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="ignoredarticles"/></c:import>
</td>
</tr>
<tr>
<td><fmt:message key="generalsettings.shortcuts"/></td>
<td>
<form:input path="shortcuts" size="70"/>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="shortcuts"/></c:import>
</td>
</tr>
<tr><td colspan="2">&nbsp;</td></tr>
<tr>
<td><fmt:message key="generalsettings.language"/></td>
<td>
<form:select path="localeIndex" cssStyle="width:15em">
<c:forEach items="${command.locales}" var="locale" varStatus="loopStatus">
<form:option value="${loopStatus.count - 1}" label="${locale}"/>
</c:forEach>
</form:select>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="language"/></c:import>
</td>
</tr>
<tr>
<td><fmt:message key="generalsettings.theme"/></td>
<td>
<form:select path="themeIndex" cssStyle="width:15em">
<c:forEach items="${command.themes}" var="theme" varStatus="loopStatus">
<form:option value="${loopStatus.count - 1}" label="${theme.name}"/>
</c:forEach>
</form:select>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="theme"/></c:import>
</td>
</tr>
<tr><td colspan="2">&nbsp;</td></tr>
<tr>
<td>
</td>
<td>
<form:checkbox path="sortAlbumsByYear" id="sortAlbumsByYear"/>
<label for="sortAlbumsByYear"><fmt:message key="generalsettings.sortalbumsbyyear"/></label>
</td>
</tr>
<tr>
<td>
</td>
<td>
<form:checkbox path="gettingStartedEnabled" id="gettingStartedEnabled"/>
<label for="gettingStartedEnabled"><fmt:message key="generalsettings.showgettingstarted"/></label>
</td>
</tr>
<tr><td colspan="2">&nbsp;</td></tr>
<tr>
<td><fmt:message key="generalsettings.welcometitle"/></td>
<td>
<form:input path="welcomeTitle" size="70"/>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="welcomemessage"/></c:import>
</td>
</tr>
<tr>
<td><fmt:message key="generalsettings.welcomesubtitle"/></td>
<td>
<form:input path="welcomeSubtitle" size="70"/>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="welcomemessage"/></c:import>
</td>
</tr>
<tr>
<td style="vertical-align:top;"><fmt:message key="generalsettings.welcomemessage"/></td>
<td>
<form:textarea path="welcomeMessage" rows="5" cols="70"/>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="welcomemessage"/></c:import>
</td>
</tr>
<tr>
<td style="vertical-align:top;"><fmt:message key="generalsettings.loginmessage"/></td>
<td>
<form:textarea path="loginMessage" rows="5" cols="70"/>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="loginmessage"/></c:import>
<fmt:message key="main.wiki"/>
</td>
</tr>
<tr>
<td colspan="2" style="padding-top:1.5em">
<input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
<input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
</td>
</tr>
</table>
</form:form>
<c:if test="${command.reloadNeeded}">
<script language="javascript" type="text/javascript">
parent.frames.left.location.href="left.view?";
parent.frames.playQueue.location.href="playQueue.view?";
</script>
</c:if>
</body></html>
@@ -0,0 +1,53 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<%@ include file="head.jsp" %>
<script type="text/javascript" language="javascript">
function hideGettingStarted() {
alert("<fmt:message key="gettingStarted.hidealert"/>");
location.href = "gettingStarted.view?hide";
}
</script>
</head>
<body class="mainframe bgcolor1">
<h1 style="padding-bottom:0.5em">
<img src="<spring:theme code="homeImage"/>" alt="">
<fmt:message key="gettingStarted.title"/>
</h1>
<fmt:message key="gettingStarted.text"/>
<c:if test="${model.runningAsRoot}">
<h2 class="warning"><fmt:message key="gettingStarted.root"/></h2>
</c:if>
<table style="padding-top:1em;padding-bottom:2em;width:60%">
<tr>
<td style="font-size:26pt;padding:20pt">1</td>
<td>
<div style="font-size:14pt"><a href="userSettings.view?userIndex=0"><fmt:message key="gettingStarted.step1.title"/></a></div>
<div style="padding-top:5pt"><fmt:message key="gettingStarted.step1.text"/></div>
</td>
</tr>
<tr>
<td style="font-size:26pt;padding:20pt">2</td>
<td>
<div style="font-size:14pt"><a href="musicFolderSettings.view"><fmt:message key="gettingStarted.step2.title"/></a></div>
<div style="padding-top:5pt"><fmt:message key="gettingStarted.step2.text"/></div>
</td>
</tr>
<tr>
<td style="font-size:26pt;padding:20pt">3</td>
<td>
<div style="font-size:14pt"><a href="networkSettings.view"><fmt:message key="gettingStarted.step3.title"/></a></div>
<div style="padding-top:5pt"><fmt:message key="gettingStarted.step3.text"/></div>
</td>
</tr>
</table>
<div class="forward"><a href="javascript:hideGettingStarted()"><fmt:message key="gettingStarted.hide"/></a></div>
</body></html>
@@ -0,0 +1,12 @@
<%@ include file="include.jsp" %>
<!--[if lt IE 7.]>
<script defer type="text/javascript" src="<c:url value="/script/pngfix.js"/>"></script>
<![endif]-->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<c:set var="styleSheet"><spring:theme code="styleSheet"/></c:set>
<link rel="icon" type="image/png" href="<c:url value="/icons/favicon.png"/>"/>
<link rel="stylesheet" href="<c:url value="/${styleSheet}"/>" type="text/css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Ubuntu&subset=latin,cyrillic-ext,greek-ext,greek,latin-ext,cyrillic" type="text/css"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:400,300,400italic,500,300italic,500italic,700,700italic,100,100italic" type="text/css"/>
<title>Libresonic</title>
@@ -0,0 +1,69 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<%@ include file="head.jsp" %>
<script type="text/javascript" src="<c:url value="/script/scripts-2.0.js"/>"></script>
</head>
<body class="mainframe bgcolor1">
<c:choose>
<c:when test="${empty model.buildDate}">
<fmt:message key="common.unknown" var="buildDateString"/>
</c:when>
<c:otherwise>
<fmt:formatDate value="${model.buildDate}" dateStyle="long" var="buildDateString"/>
</c:otherwise>
</c:choose>
<c:choose>
<c:when test="${empty model.localVersion}">
<fmt:message key="common.unknown" var="versionString"/>
</c:when>
<c:otherwise>
<c:set var="versionString" value="${model.localVersion}"/>
</c:otherwise>
</c:choose>
<h1>
<img src="<spring:theme code="helpImage"/>" alt="">
<span style="vertical-align: middle"><fmt:message key="help.title"><fmt:param value="${model.brand}"/></fmt:message></span>
</h1>
<c:if test="${model.newVersionAvailable}">
<p class="warning"><fmt:message key="help.upgrade"><fmt:param value="${model.brand}"/><fmt:param value="${model.latestVersion}"/></fmt:message></p>
</c:if>
<table width="75%" class="ruleTable indent">
<tr><td class="ruleTableHeader"><fmt:message key="help.version.title"/></td><td class="ruleTableCell">${versionString} &ndash; ${buildDateString}</td></tr>
<tr><td class="ruleTableHeader"><fmt:message key="help.server.title"/></td><td class="ruleTableCell">${model.serverInfo} (<sub:formatBytes bytes="${model.usedMemory}"/> / <sub:formatBytes bytes="${model.totalMemory}"/>)</td></tr>
<tr><td class="ruleTableHeader"><fmt:message key="help.license.title"/></td><td class="ruleTableCell">
<a href="http://www.gnu.org/copyleft/gpl.html" target="_blank"><img style="float:right;margin-left: 10px" alt="GPL 3.0" src="<c:url value="/icons/default_light/gpl.png"/>"></a>
<fmt:message key="help.license.text"><fmt:param value="${model.brand}"/></fmt:message></td></tr>
<tr><td class="ruleTableHeader"><fmt:message key="help.homepage.title"/></td><td class="ruleTableCell"><a target="_blank" href="https://www.libresonic.org/">Libresonic website</a></td></tr>
<tr><td class="ruleTableHeader"><fmt:message key="help.forum.title"/></td><td class="ruleTableCell"><a target="_blank" href="https://www.reddit.com/r/libresonic">Libresonic on Reddit</a></td></tr>
<tr><td class="ruleTableHeader"><fmt:message key="help.contact.title"/></td><td class="ruleTableCell"><fmt:message key="help.contact.text"><fmt:param value="${model.brand}"/></fmt:message></td></tr>
</table>
<p></p>
<h2>
<img src="<spring:theme code="logImage"/>" alt="">
<span style="vertical-align: middle"><fmt:message key="help.log"/></span>
</h2>
<table cellpadding="2" class="log indent">
<c:forEach items="${model.logEntries}" var="entry">
<tr>
<td>[<fmt:formatDate value="${entry.date}" dateStyle="short" timeStyle="long" type="both"/>]</td>
<td>${entry.level}</td><td>${entry.category}</td><td>${fn:escapeXml(entry.message)}</td>
</tr>
</c:forEach>
</table>
<p><fmt:message key="help.logfile"><fmt:param value="${model.logFile}"/></fmt:message> </p>
<div class="forward"><a href="help.view?"><fmt:message key="common.refresh"/></a></div>
</body></html>
@@ -0,0 +1,18 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<%@ include file="include.jsp" %>
<%--
Shows online help as a balloon tool tip.
PARAMETERS
topic: Refers to a key in the resource bundle containing the text to display in the tool tip.
--%>
<spring:theme code="helpPopupImage" var="imageUrl"/>
<fmt:message key="common.help" var="help"/>
<div id="placeholder-${param.topic}" style="display:none">
<div style="font-weight:bold;"><fmt:message key="helppopup.${param.topic}.title"><fmt:param value="Libresonic"/></fmt:message></div>
<div><fmt:message key="helppopup.${param.topic}.text"><fmt:param value="Libresonic"/></fmt:message></div>
</div>
<img src="${imageUrl}" alt="${help}" title="${help}" onmouseover="TagToTip('placeholder-${param.topic}', BALLOON, true, ABOVE, true, OFFSETX, -17, PADDING, 8, WIDTH, -240, CLICKSTICKY, true, CLICKCLOSE, true)" onmouseout="UnTip()"/>
@@ -0,0 +1,128 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript" language="javascript">
function init() {
setTimeout("refresh()", 60000);
<c:if test="${not model.musicFoldersExist}">
$().toastmessage("showNoticeToast", "<fmt:message key="top.missing"/>");
</c:if>
<c:if test="${model.isIndexBeingCreated}">
$().toastmessage("showNoticeToast", "<fmt:message key="home.scan"/>");
</c:if>
}
function refresh() {
top.main.location.href = top.main.location.href;
}
function playShuffle() {
top.playQueue.onPlayShuffle('${model.listType}', ${model.listOffset}, ${model.listSize}, '${model.genre}', '${model.decade}')
}
</script>
</head>
<body class="mainframe bgcolor1" onload="init();">
<h1>
<img src="<spring:theme code="homeImage"/>" alt="">
<span style="vertical-align: middle">${model.welcomeTitle}</span>
</h1>
<c:if test="${not empty model.welcomeSubtitle}">
<h2>${model.welcomeSubtitle}</h2>
</c:if>
<h2>
<c:forTokens items="random newest starred highest frequent recent decade genre alphabetical" delims=" " var="cat" varStatus="loopStatus">
<c:if test="${loopStatus.count > 1}">&nbsp;|&nbsp;</c:if>
<sub:url var="url" value="home.view">
<sub:param name="listType" value="${cat}"/>
</sub:url>
<c:choose>
<c:when test="${model.listType eq cat}">
<span class="headerSelected"><fmt:message key="home.${cat}.title"/></span>
</c:when>
<c:otherwise>
<span class="header"><a href="${url}"><fmt:message key="home.${cat}.title"/></a></span>
</c:otherwise>
</c:choose>
</c:forTokens>
</h2>
<%@ include file="homePager.jsp" %>
<c:if test="${not empty model.welcomeMessage}">
<div style="width:15em;float:right;padding:0 1em 0 1em;border-left:1px solid #<spring:theme code="detailColor"/>">
<sub:wiki text="${model.welcomeMessage}"/>
</div>
</c:if>
<c:forEach items="${model.albums}" var="album" varStatus="loopStatus">
<c:set var="albumTitle">
<c:choose>
<c:when test="${empty album.albumTitle}">
<fmt:message key="common.unknown"/>
</c:when>
<c:otherwise>
${album.albumTitle}
</c:otherwise>
</c:choose>
</c:set>
<c:set var="captionCount" value="2"/>
<c:if test="${not empty album.playCount}">
<c:set var="caption3"><fmt:message key="home.playcount"><fmt:param value="${album.playCount}"/></fmt:message></c:set>
<c:set var="captionCount" value="3"/>
</c:if>
<c:if test="${not empty album.lastPlayed}">
<fmt:formatDate value="${album.lastPlayed}" dateStyle="short" var="lastPlayedDate"/>
<c:set var="caption3"><fmt:message key="home.lastplayed"><fmt:param value="${lastPlayedDate}"/></fmt:message></c:set>
<c:set var="captionCount" value="3"/>
</c:if>
<c:if test="${not empty album.created}">
<fmt:formatDate value="${album.created}" dateStyle="short" var="creationDate"/>
<c:set var="caption3"><fmt:message key="home.created"><fmt:param value="${creationDate}"/></fmt:message></c:set>
<c:set var="captionCount" value="3"/>
</c:if>
<c:if test="${not empty album.year}">
<c:set var="caption3" value="${album.year}"/>
<c:set var="captionCount" value="3"/>
</c:if>
<div class="albumThumb">
<c:import url="coverArt.jsp">
<c:param name="albumId" value="${album.id}"/>
<c:param name="caption1" value="${fn:escapeXml(album.albumTitle)}"/>
<c:param name="caption2" value="${fn:escapeXml(album.artist)}"/>
<c:param name="caption3" value="${caption3}"/>
<c:param name="captionCount" value="${captionCount}"/>
<c:param name="coverArtSize" value="${model.coverArtSize}"/>
<c:param name="showLink" value="true"/>
<c:param name="appearAfter" value="${loopStatus.count * 30}"/>
</c:import>
<c:if test="${not empty album.rating}">
<c:import url="rating.jsp">
<c:param name="readonly" value="true"/>
<c:param name="rating" value="${album.rating}"/>
</c:import>
</c:if>
</div>
</c:forEach>
<c:if test="${model.listSize eq fn:length(model.albums)}">
<%@ include file="homePager.jsp" %>
</c:if>
</body></html>
@@ -0,0 +1,79 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<table>
<tr>
<c:if test="${not empty model.musicFolder}">
<td style="padding-right: 2em">
<div style="border:1px solid #<spring:theme code="detailColor"/>; padding-left: 0.5em;padding-right: 0.5em">
${fn:escapeXml(model.musicFolder.name)}
</div>
</td>
</c:if>
<c:choose>
<c:when test="${model.listType eq 'random'}">
<td><div class="forward"><a href="home.view?listType=random"><fmt:message key="common.more"/></a></div></td>
</c:when>
<c:otherwise>
<sub:url value="home.view" var="previousUrl">
<sub:param name="listType" value="${model.listType}"/>
<sub:param name="listOffset" value="${model.listOffset - model.listSize}"/>
<sub:param name="genre" value="${model.genre}"/>
<sub:param name="decade" value="${model.decade}"/>
</sub:url>
<sub:url value="home.view" var="nextUrl">
<sub:param name="listType" value="${model.listType}"/>
<sub:param name="listOffset" value="${model.listOffset + model.listSize}"/>
<sub:param name="genre" value="${model.genre}"/>
<sub:param name="decade" value="${model.decade}"/>
</sub:url>
<c:if test="${fn:length(model.albums) gt 0}">
<td style="padding-right:0.5em">
<fmt:message key="home.albums">
<fmt:param value="${model.listOffset + 1}"/>
<fmt:param value="${model.listOffset + fn:length(model.albums)}"/>
</fmt:message>
</td>
</c:if>
<c:if test="${model.listOffset gt 0}">
<td><a href="${previousUrl}"><img src="<spring:theme code="backImage"/>" alt=""></a></td>
</c:if>
<c:if test="${fn:length(model.albums) eq model.listSize}">
<td><a href="${nextUrl}"><img src="<spring:theme code="forwardImage"/>" alt=""></a></td>
</c:if>
<c:if test="${model.listType eq 'decade'}">
<td style="padding-left: 2em">
<fmt:message key="home.decade.text"/>
</td>
<td>
<select name="decade" onchange="location='home.view?listType=${model.listType}&amp;decade=' + options[selectedIndex].value">
<c:forEach items="${model.decades}" var="decade">
<option
${decade eq model.decade ? "selected" : ""} value="${decade}">${decade}</option>
</c:forEach>
</select>
</td>
</c:if>
<c:if test="${model.listType eq 'genre'}">
<td style="padding-left: 2em">
<fmt:message key="home.genre.text"/>
</td>
<td>
<select name="genre" onchange="location='home.view?listType=${model.listType}&amp;genre=' + encodeURIComponent(options[selectedIndex].value)">
<c:forEach items="${model.genres}" var="genre">
<option ${genre.name eq model.genre ? "selected" : ""} value="${genre.name}">${genre.name} (${genre.albumCount})</option>
</c:forEach>
</select>
</td>
</c:if>
</c:otherwise>
</c:choose>
<c:if test="${not empty model.albums}">
<td style="padding-left: 2em">
<a href="javascript:playShuffle()"><img src="<spring:theme code="shuffleImage"/>" alt="">&nbsp;<fmt:message key="home.shuffle"/></a></td>
</c:if>
</tr>
</table>
@@ -0,0 +1,38 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<%@ include file="head.jsp" %>
</head>
<body class="mainframe bgcolor1">
<h1 style="padding-bottom:0.5em">
<fmt:message key="importPlaylist.title"/>
</h1>
<c:if test="${not empty model.playlist}">
<p>
<fmt:message key="importPlaylist.success"><fmt:param value="${model.playlist.name}"/></fmt:message>
<script type="text/javascript" language="javascript">
top.left.updatePlaylists();
top.main.location.href = "playlist.view?id=${model.playlist.id}";
</script>
</p>
</c:if>
<c:if test="${not empty model.error}">
<p class="warning">
<fmt:message key="importPlaylist.error"><fmt:param value="${model.error}"/></fmt:message>
</p>
</c:if>
<div style="padding-bottom: 0.25em">
<fmt:message key="importPlaylist.text"/>
</div>
<form method="post" enctype="multipart/form-data" action="importPlaylist.view">
<input type="file" id="file" name="file" size="40"/>
<input type="submit" value="<fmt:message key="common.ok"/>"/>
</form>
</body></html>
@@ -0,0 +1,8 @@
<%@ page session="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="sub" uri="http://libresonic.org/taglib/sub" %>
<%@ taglib prefix="str" uri="http://jakarta.apache.org/taglibs/string-1.1" %>
@@ -0,0 +1,26 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<html><head>
<%@ include file="head.jsp" %>
<link rel="alternate" type="application/rss+xml" title="Libresonic Podcast" href="podcast.view?suffix=.rss">
</head>
<frameset rows="80,*,0" border="0" framespacing="0" frameborder="0">
<frame name="upper" src="top.view?" class="bgcolor2">
<frameset id="mainFrameset" cols=${model.showSideBar ? "230,*" : "0,*"} border="0" framespacing="0" frameborder="0">
<frame name="left" src="left.view?" marginwidth="0" marginheight="0" class="bgcolor2">
<frameset id="playQueueFrameset" rows=${model.autoHidePlayQueue ? "*,50" : "75%,25%"} border="0" framespacing="0" frameborder="0">
<frameset cols="*,${model.showRight ? 235 : 0}" border="0" framespacing="0" frameborder="0">
<frame name="main" src="nowPlaying.view?" marginwidth="0" marginheight="0" class="bgcolor1">
<frame name="right" src="right.view?" class="bgcolor1">
</frameset>
<frame name="playQueue" src="playQueue.view?" class="bgcolor2">
</frameset>
</frameset>
<frame name="hidden" frameborder="0" noresize="noresize">
</frameset>
</html>
@@ -0,0 +1,66 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
</head>
<body class="mainframe bgcolor1">
<c:import url="settingsHeader.jsp">
<c:param name="cat" value="internetRadio"/>
<c:param name="toast" value="${model.reload}"/>
</c:import>
<form method="post" action="internetRadioSettings.view">
<table class="indent">
<tr>
<th><fmt:message key="internetradiosettings.name"/></th>
<th><fmt:message key="internetradiosettings.streamurl"/></th>
<th><fmt:message key="internetradiosettings.homepageurl"/></th>
<th style="padding-left:1em"><fmt:message key="internetradiosettings.enabled"/></th>
<th style="padding-left:1em"><fmt:message key="common.delete"/></th>
</tr>
<c:forEach items="${model.internetRadios}" var="radio">
<tr>
<td><input type="text" name="name[${radio.id}]" size="20" value="${radio.name}"/></td>
<td><input type="text" name="streamUrl[${radio.id}]" size="40" value="${radio.streamUrl}"/></td>
<td><input type="text" name="homepageUrl[${radio.id}]" size="40" value="${radio.homepageUrl}"/></td>
<td align="center" style="padding-left:1em"><input type="checkbox" ${radio.enabled ? "checked" : ""} name="enabled[${radio.id}]" class="checkbox"/></td>
<td align="center" style="padding-left:1em"><input type="checkbox" name="delete[${radio.id}]" class="checkbox"/></td>
</tr>
</c:forEach>
<c:if test="${not empty model.internetRadios}">
<tr>
<th colspan="5" align="left" style="padding-top:1em"><fmt:message key="internetradiosettings.add"/></th>
</tr>
</c:if>
<tr>
<td><input type="text" name="name" size="20" placeholder="<fmt:message key="internetradiosettings.name"/>"/></td>
<td><input type="text" name="streamUrl" size="40" placeholder="<fmt:message key="internetradiosettings.streamurl"/>"/></td>
<td><input type="text" name="homepageUrl" size="40" placeholder="<fmt:message key="internetradiosettings.homepageurl"/>"/></td>
<td align="center" style="padding-left:1em"><input name="enabled" checked type="checkbox" class="checkbox"/></td>
<td/>
</tr>
<tr>
<td style="padding-top:1.5em" colspan="5">
<input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
<input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
</td>
</tr>
</table>
</form>
<c:if test="${not empty model.error}">
<p class="warning"><fmt:message key="${model.error}"/></p>
</c:if>
<c:if test="${model.reload}">
<script language="javascript" type="text/javascript">parent.frames.left.location.href="left.view?"</script>
</c:if>
</body></html>
@@ -0,0 +1,7 @@
<link rel="stylesheet" href="<c:url value="/style/smoothness/jquery-ui-1.8.18.custom.css"/>" type="text/css">
<link rel="stylesheet" href="<c:url value="/script/jquery.toastmessage/css/jquery.toastmessage.css" />" type="text/css" >
<link rel="stylesheet" href="<c:url value="/style/jquery.contextMenu.css"/>" type="text/css" >
<script type="text/javascript" src="<c:url value='/script/jquery-1.7.1.min.js'/>"></script>
<script type="text/javascript" src="<c:url value='/script/jquery-ui-1.11.1.min.js'/>"></script>
<script type="text/javascript" src="<c:url value="/script/jquery.toastmessage/jquery.toastmessage.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/jquery.contextMenu.js"/>"></script>
@@ -0,0 +1,202 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript" src="<c:url value="/script/scripts-2.0.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/interface/playlistService.js"/>"></script>
<script type="text/javascript" language="javascript">
var playlists;
function init() {
dwr.engine.setErrorHandler(null);
updatePlaylists();
var mainLocation = top.main.location.href;
if (${model.musicFolderChanged}) {
if (mainLocation.indexOf("/home.view") != -1) {
top.main.location.href = mainLocation;
}
}
}
function updatePlaylists() {
playlistService.getReadablePlaylists(playlistCallback);
}
function createEmptyPlaylist() {
showAllPlaylists();
playlistService.createEmptyPlaylist(playlistCallback);
}
function showAllPlaylists() {
$('#playlistOverflow').show('blind');
$('#showAllPlaylists').hide('blind');
}
function playlistCallback(playlists) {
this.playlists = playlists;
$("#playlists").empty();
$("#playlistOverflow").empty();
for (var i = 0; i < playlists.length; i++) {
var playlist = playlists[i];
var overflow = i > 9;
$("<p class='dense'><a target='main' href='playlist.view?id=" +
playlist.id + "'>" + escapeHtml(playlist.name) + "&nbsp;(" + playlist.fileCount + ")</a></p>").appendTo(overflow ? "#playlistOverflow" : "#playlists");
}
if (playlists.length > 10 && !$('#playlistOverflow').is(":visible")) {
$('#showAllPlaylists').show();
}
}
</script>
</head>
<body class="bgcolor2 leftframe" onload="init()">
<a name="top"></a>
<div style="padding-bottom:1.5em">
<a href="help.view?" target="main"><img src="<spring:theme code="logoImage"/>" title="<fmt:message key="top.help"/>" alt=""></a>
</div>
<c:if test="${fn:length(model.musicFolders) > 1}">
<div style="padding-bottom:1.0em">
<select name="musicFolderId" style="width:100%" onchange="location='left.view?musicFolderId=' + options[selectedIndex].value;">
<option value="-1"><fmt:message key="left.allfolders"/></option>
<c:forEach items="${model.musicFolders}" var="musicFolder">
<option ${model.selectedMusicFolder.id == musicFolder.id ? "selected" : ""} value="${musicFolder.id}">${fn:escapeXml(musicFolder.name)}</option>
</c:forEach>
</select>
</div>
</c:if>
<div style="margin-bottom:0.5em;padding-left: 2px" class="bgcolor1">
<c:forEach items="${model.indexes}" var="index">
<a href="#${index.index}" accesskey="${index.index}">${index.index}</a>
</c:forEach>
</div>
<div style="padding-bottom:0.5em">
<div class="forward">
<c:choose>
<c:when test="${model.scanning}">
<a href="left.view"><fmt:message key="common.refresh"/></a>
</c:when>
<c:otherwise>
<a href="left.view?refresh=true"><fmt:message key="common.refresh"/></a>
</c:otherwise>
</c:choose>
</div>
</div>
<c:if test="${not empty model.shortcuts}">
<h2 class="bgcolor1" style="padding-left: 2px"><fmt:message key="left.shortcut"/></h2>
<c:forEach items="${model.shortcuts}" var="shortcut">
<p class="dense" style="padding-left:2px">
<sub:url value="main.view" var="mainUrl">
<sub:param name="id" value="${shortcut.id}"/>
</sub:url>
<a target="main" href="${mainUrl}">${fn:escapeXml(shortcut.name)}</a>
</p>
</c:forEach>
</c:if>
<h2 class="bgcolor1" style="padding-left: 2px"><fmt:message key="left.playlists"/></h2>
<div id="playlistWrapper" style='padding-left:2px'>
<div id="playlists"></div>
<div id="playlistOverflow" style="display:none"></div>
<div style="padding-top: 0.3em"></div>
<div class="forward" id="showAllPlaylists" style="display: none"><a href="javascript:noop()" onclick="showAllPlaylists()"><fmt:message key="left.showallplaylists"/></a></div>
<div class="forward"><a href="javascript:noop()" onclick="createEmptyPlaylist()"><fmt:message key="left.createplaylist"/></a></div>
<div class="forward"><a href="importPlaylist.view" target="main"><fmt:message key="left.importplaylist"/></a></div>
</div>
<c:if test="${not empty model.radios}">
<h2 class="bgcolor1" style="padding-left: 2px"><fmt:message key="left.radio"/></h2>
<c:forEach items="${model.radios}" var="radio">
<p class="dense" style="padding-left: 2px">
<a target="hidden" href="${radio.streamUrl}">
<img src="<spring:theme code="playImage"/>" alt="<fmt:message key="common.play"/>" title="<fmt:message key="common.play"/>"></a>
<span style="vertical-align: middle">
<c:choose>
<c:when test="${empty radio.homepageUrl}">
${fn:escapeXml(radio.name)}
</c:when>
<c:otherwise>
<a target="main" href="${radio.homepageUrl}">${fn:escapeXml(radio.name)}</a>
</c:otherwise>
</c:choose>
</span>
</p>
</c:forEach>
</c:if>
<c:forEach items="${model.indexedArtists}" var="entry">
<table class="bgcolor1" style="width:100%;padding:0;margin:1em 0 0 0;border:0">
<tr style="padding:0;margin:0;border:0">
<th style="text-align:left;padding:0;margin:0;border:0"><a name="${fn:escapeXml(entry.key.index)}"></a>
<h2 style="padding:0;margin:0;border:0">${fn:escapeXml(entry.key.index)}</h2>
</th>
<th style="text-align:right;">
<a href="#top"><img src="<spring:theme code="upImage"/>" alt=""></a>
</th>
</tr>
</table>
<c:forEach items="${entry.value}" var="artist">
<p class="dense" style="padding-left:2px">
<span title="${artist.name}">
<sub:url value="main.view" var="mainUrl">
<c:forEach items="${artist.mediaFiles}" var="mediaFile">
<sub:param name="id" value="${mediaFile.id}"/>
</c:forEach>
</sub:url>
<a target="main" href="${mainUrl}"><str:truncateNicely upper="${35}">${fn:escapeXml(artist.name)}</str:truncateNicely></a>
</span>
</p>
</c:forEach>
</c:forEach>
<div style="padding-top:1em"></div>
<c:forEach items="${model.singleSongs}" var="song">
<p class="dense" style="padding-left:2px">
<span class="songTitle" title="${fn:escapeXml(song.title)}">
<c:import url="playButtons.jsp">
<c:param name="id" value="${song.id}"/>
<c:param name="playEnabled" value="${model.user.streamRole and not model.partyMode}"/>
<c:param name="addEnabled" value="${model.user.streamRole}"/>
<c:param name="downloadEnabled" value="${model.user.downloadRole and not model.partyMode}"/>
<c:param name="video" value="${song.video and model.player.web}"/>
</c:import>
<str:truncateNicely upper="${35}">${fn:escapeXml(song.title)}</str:truncateNicely>
</span>
</p>
</c:forEach>
<c:if test="${model.statistics.songCount gt 0}">
<div class="detail" style="padding-top: 0.6em; padding-left: 2px">
<fmt:message key="left.statistics">
<fmt:param value="${model.statistics.artistCount}"/>
<fmt:param value="${model.statistics.albumCount}"/>
<fmt:param value="${model.statistics.songCount}"/>
<fmt:param value="${model.bytes}"/>
<fmt:param value="${model.hours}"/>
</fmt:message>
</div>
</c:if>
<div style="height:5em"></div>
<div class="bgcolor2" style="opacity: 1.0; clear: both; position: fixed; bottom: 0; right: 0; left: 0;
padding: 0.25em 0.75em 0.25em 0.75em; border-top:1px solid black; max-width: 850px;">
<c:forEach items="${model.indexes}" var="index">
<a href="#${index.index}">${index.index}</a>
</c:forEach>
</div>
</body></html>
@@ -0,0 +1,17 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<c:if test="${licenseInfo.trial}">
<fmt:formatDate value="${licenseInfo.trialExpires}" dateStyle="long" var="expiryDate"/>
<p class="warning" style="padding-top:1em">
<c:choose>
<c:when test="${licenseInfo.trialExpired}">
<fmt:message key="common.trialexpired"><fmt:param>${expiryDate}</fmt:param></fmt:message>
</c:when>
<c:otherwise>
<fmt:message key="common.trialnotexpired"><fmt:param>${expiryDate}</fmt:param></fmt:message>
</c:otherwise>
</c:choose>
</p>
</c:if>
@@ -0,0 +1,64 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<%@ include file="head.jsp" %>
<script type="text/javascript">
if (window != window.top) {
top.location.href = location.href;
}
</script>
</head>
<body class="mainframe bgcolor1" onload="document.getElementById('j_username').focus()">
<form action="<c:url value="/j_acegi_security_check"/>" method="POST">
<div class="bgcolor2 shadow" align="center" style="padding:20px 50px 20px 50px; margin-top:100px;margin-left:50px;margin-right:50px">
<div style="margin-bottom:1em;max-width:50em;text-align:left;"><sub:wiki text="${model.loginMessage}"/></div>
<table>
<tr>
<td colspan="2" align="left" style="padding-bottom:10px">
<img src="<spring:theme code="logoImage"/>" alt="">
</td>
</tr>
<tr>
<td align="left" style="padding-right:10px"><fmt:message key="login.username"/></td>
<td align="left"><input type="text" id="j_username" name="j_username" style="width:12em" tabindex="1"></td>
</tr>
<tr>
<td align="left" style="padding-bottom:10px"><fmt:message key="login.password"/></td>
<td align="left" style="padding-bottom:10px"><input type="password" name="j_password" style="width:12em" tabindex="2"></td>
</tr>
<tr>
<td align="left"><input name="submit" type="submit" value="<fmt:message key="login.login"/>" tabindex="4"></td>
<td align="left" class="detail">
<input type="checkbox" name="_acegi_security_remember_me" id="remember" class="checkbox" tabindex="3">
<label for="remember"><fmt:message key="login.remember"/></label>
</td>
</tr>
<tr>
<td></td>
<td align="left" class="detail"><a href="recover.view"><fmt:message key="login.recover"/></a></td>
</tr>
<c:if test="${model.logout}">
<tr><td colspan="2" style="padding-top:10px"><b><fmt:message key="login.logout"/></b></td></tr>
</c:if>
<c:if test="${model.error}">
<tr><td colspan="2" style="padding-top:10px"><span class="warning"><fmt:message key="login.error"/></span></td></tr>
</c:if>
</table>
<c:if test="${model.insecure}">
<p class="warning"><fmt:message key="login.insecure"><fmt:param value="${model.brand}"/></fmt:message></p>
</c:if>
</div>
</form>
</body>
</html>
@@ -0,0 +1,88 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<html><head>
<%@ include file="head.jsp" %>
<title><fmt:message key="lyrics.title"/></title>
<script type="text/javascript" src="<c:url value="/dwr/interface/lyricsService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/util.js"/>"></script>
<script type="text/javascript" language="javascript">
dwr.engine.setErrorHandler(null);
function init() {
getLyrics('${model.artist}', '${model.song}');
}
function getLyrics(artist, song) {
$("wait").style.display = "inline";
$("lyrics").style.display = "none";
$("noLyricsFound").style.display = "none";
$("tryLater").style.display = "none";
lyricsService.getLyrics(artist, song, getLyricsCallback);
}
function getLyricsCallback(lyricsInfo) {
dwr.util.setValue("lyricsHeader", lyricsInfo.artist + " - " + lyricsInfo.title);
var lyrics;
if (lyricsInfo.lyrics != null) {
lyrics = lyricsInfo.lyrics.replace(/\n/g, "<br>");
}
dwr.util.setValue("lyricsText", lyrics, { escapeHtml:false });
$("wait").style.display = "none";
if (lyricsInfo.tryLater) {
$("tryLater").style.display = "inline";
} else if (lyrics != null) {
$("lyrics").style.display = "inline";
} else {
$("noLyricsFound").style.display = "inline";
}
}
function search() {
getLyrics(dwr.util.getValue('artist'), dwr.util.getValue('song'));
}
</script>
</head>
<body class="mainframe bgcolor1" onload="init();">
<form action="#" onsubmit="search();return false;">
<table>
<tr>
<td><fmt:message key="lyrics.artist"/></td>
<td style="padding-left:0.50em"><input id="artist" type="text" size="40" value="${model.artist}" tabindex="1"/></td>
<td style="padding-left:0.75em"><input type="submit" value="<fmt:message key="lyrics.search"/>" style="width:6em"
tabindex="3"/></td>
</tr>
<tr>
<td><fmt:message key="lyrics.song"/></td>
<td style="padding-left:0.50em"><input id="song" type="text" size="40" value="${model.song}" tabindex="2"/></td>
<td style="padding-left:0.75em"><input type="button" value="<fmt:message key="common.close"/>" style="width:6em"
onclick="self.close()" tabindex="4"/></td>
</tr>
</table>
</form>
<hr/>
<h2 id="wait"><fmt:message key="lyrics.wait"/></h2>
<h2 id="noLyricsFound" style="display:none"><fmt:message key="lyrics.nolyricsfound"/></h2>
<p id="tryLater" style="display:none"><b><fmt:message key="lyrics.trylater"/></b></p>
<div id="lyrics" style="display:none;">
<h2 id="lyricsHeader" style="text-align:center;margin-bottom:1em"></h2>
<div id="lyricsText"></div>
<p class="detail" style="text-align:right">
<fmt:message key="lyrics.courtesy"/>
</p>
</div>
<hr/>
<p style="text-align:center">
<a href="javascript:self.close()">[<fmt:message key="common.close"/>]</a>
</p>
</body>
</html>
@@ -0,0 +1,190 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<html><head>
<%@ include file="head.jsp" %>
<style type="text/css">
#progressBar {width: 350px; height: 10px; border: 1px solid black; display:none;}
#progressBarContent {width: 0; height: 10px; background: url("<c:url value="/icons/default_light/progress.png"/>") repeat;}
</style>
<script type="text/javascript" src="<c:url value="/dwr/interface/transferService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/util.js"/>"></script>
<script type="text/javascript">
function refreshProgress() {
transferService.getUploadInfo(updateProgress);
}
function updateProgress(uploadInfo) {
var progressBar = document.getElementById("progressBar");
var progressBarContent = document.getElementById("progressBarContent");
var progressText = document.getElementById("progressText");
if (uploadInfo.bytesTotal > 0) {
var percent = Math.ceil((uploadInfo.bytesUploaded / uploadInfo.bytesTotal) * 100);
var width = parseInt(percent * 3.5) + 'px';
progressBarContent.style.width = width;
progressText.innerHTML = percent + "<fmt:message key="more.upload.progress"/>";
progressBar.style.display = "block";
progressText.style.display = "block";
window.setTimeout("refreshProgress()", 1000);
} else {
progressBar.style.display = "none";
progressText.style.display = "none";
window.setTimeout("refreshProgress()", 5000);
}
}
</script>
</head>
<body class="mainframe bgcolor1" onload="${model.user.uploadRole ? "refreshProgress()" : ""}">
<h1>
<img src="<spring:theme code="moreImage"/>" alt=""/>
<span style="vertical-align: middle"><fmt:message key="more.title"/></span>
</h1>
<c:if test="${model.user.streamRole}">
<h2>
<img src="<spring:theme code="shuffleImage"/>" alt=""/>
<span style="vertical-align: middle"><fmt:message key="more.random.title"/></span>
</h2>
<form method="post" action="randomPlayQueue.view?">
<table>
<tr>
<td><fmt:message key="more.random.text"/></td>
<td>
<select name="size">
<option value="10"><fmt:message key="more.random.songs"><fmt:param value="10"/></fmt:message></option>
<option value="20" selected><fmt:message key="more.random.songs"><fmt:param value="20"/></fmt:message></option>
<option value="30"><fmt:message key="more.random.songs"><fmt:param value="30"/></fmt:message></option>
<option value="40"><fmt:message key="more.random.songs"><fmt:param value="40"/></fmt:message></option>
<option value="50"><fmt:message key="more.random.songs"><fmt:param value="50"/></fmt:message></option>
</select>
</td>
<td><fmt:message key="more.random.genre"/></td>
<td>
<select name="genre">
<option value="any"><fmt:message key="more.random.anygenre"/></option>
<c:forEach items="${model.genres}" var="genre">
<option value="${genre.name}"><str:truncateNicely upper="20">${genre.name} (${genre.songCount})</str:truncateNicely></option>
</c:forEach>
</select>
</td>
<td><fmt:message key="more.random.year"/></td>
<td>
<select name="year">
<option value="any"><fmt:message key="more.random.anyyear"/></option>
<c:forEach begin="0" end="${model.currentYear - 2010}" var="yearOffset">
<c:set var="year" value="${model.currentYear - yearOffset}"/>
<option value="${year} ${year}">${year}</option>
</c:forEach>
<option value="2010 2015">2010 &ndash; 2015</option>
<option value="2005 2010">2005 &ndash; 2010</option>
<option value="2000 2005">2000 &ndash; 2005</option>
<option value="1990 2000">1990 &ndash; 2000</option>
<option value="1980 1990">1980 &ndash; 1990</option>
<option value="1970 1980">1970 &ndash; 1980</option>
<option value="1960 1970">1960 &ndash; 1970</option>
<option value="1950 1960">1950 &ndash; 1960</option>
<option value="0 1949">&lt; 1950</option>
</select>
</td>
<td><fmt:message key="more.random.folder"/></td>
<td>
<select name="musicFolderId">
<option value="-1"><fmt:message key="more.random.anyfolder"/></option>
<c:forEach items="${model.musicFolders}" var="musicFolder">
<option value="${musicFolder.id}">${musicFolder.name}</option>
</c:forEach>
</select>
</td>
<td>
<input type="submit" value="<fmt:message key="more.random.ok"/>">
</td>
</tr>
<c:if test="${not model.clientSidePlaylist}">
<tr>
<td colspan="9">
<input type="checkbox" name="autoRandom" id="autoRandom" class="checkbox"/>
<label for="autoRandom"><fmt:message key="more.random.auto"/></label>
</td>
</tr>
</c:if>
</table>
</form>
</c:if>
<a href="http://libresonic.org/pages/apps.jsp" target="_blank"><img alt="Apps" src="<c:url value="/icons/default_light/apps.png"/>" style="float: right;margin-left: 3em; margin-right: 3em"/></a>
<h2>
<img src="<spring:theme code="androidImage"/>" alt=""/>
<span style="vertical-align: middle"><fmt:message key="more.apps.title"/></span>
</h2>
<fmt:message key="more.apps.text"/>
<a href="<c:url value="${model.jamstashUrl}"/>" target="_blank">
<img alt="Jamstash" src="<c:url value="/icons/default_light/jamstash.png"/>" style="float: right;margin-left: 3em; margin-right: 3em"/>
</a>
<h2>
<img src="<spring:theme code="html5Image"/>" alt=""/>
<span style="vertical-align: middle"><fmt:message key="more.jamstash.title"/></span>
</h2>
<fmt:message key="more.jamstash.text"><fmt:param>${model.jamstashUrl}</fmt:param></fmt:message>
<h2>
<img src="<spring:theme code="statusSmallImage"/>" alt=""/>
<span style="vertical-align: middle"><fmt:message key="more.status.title"/></span>
</h2>
<fmt:message key="more.status.text"/>
<h2>
<img src="<spring:theme code="podcastImage"/>" alt=""/>
<span style="vertical-align: middle"><fmt:message key="more.podcast.title"/></span>
</h2>
<fmt:message key="more.podcast.text"/>
<c:if test="${model.user.uploadRole}">
<h2>
<img src="<spring:theme code="uploadImage"/>" alt=""/>
<span style="vertical-align: middle"><fmt:message key="more.upload.title"/></span>
</h2>
<form method="post" enctype="multipart/form-data" action="upload.view">
<table>
<tr>
<td><fmt:message key="more.upload.source"/></td>
<td colspan="2"><input type="file" id="file" name="file" size="40"/></td>
</tr>
<tr>
<td><fmt:message key="more.upload.target"/></td>
<td><input type="text" id="dir" name="dir" size="37" value="${model.uploadDirectory}"/></td>
<td><input type="submit" value="<fmt:message key="more.upload.ok"/>"/></td>
</tr>
<tr>
<td colspan="2">
<input type="checkbox" checked name="unzip" id="unzip" class="checkbox"/>
<label for="unzip"><fmt:message key="more.upload.unzip"/></label>
</td>
</tr>
</table>
</form>
<p class="detail" id="progressText"/>
<div id="progressBar">
<div id="progressBarContent"/>
</div>
</c:if>
</body></html>
@@ -0,0 +1,128 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<%--@elvariable id="command" type="org.libresonic.player.command.MusicFolderSettingsCommand"--%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript">
function init() {
$("#newMusicFolderName").attr("placeholder", "<fmt:message key="musicfoldersettings.name"/>");
$("#newMusicFolderPath").attr("placeholder", "<fmt:message key="musicfoldersettings.path"/>");
<c:if test="${command.reload}">
parent.frames.upper.location.href="top.view?";
parent.frames.left.location.href="left.view?";
parent.frames.right.location.href="right.view?";
</c:if>
}
</script>
</head>
<body class="mainframe bgcolor1" onload="init()">
<c:import url="settingsHeader.jsp">
<c:param name="cat" value="musicFolder"/>
<c:param name="toast" value="${command.reload}"/>
</c:import>
<form:form commandName="command" action="musicFolderSettings.view" method="post">
<table class="indent">
<tr>
<th><fmt:message key="musicfoldersettings.name"/></th>
<th><fmt:message key="musicfoldersettings.path"/></th>
<th style="padding-left:1em"><fmt:message key="musicfoldersettings.enabled"/></th>
<th style="padding-left:1em"><fmt:message key="common.delete"/></th>
<th></th>
</tr>
<c:forEach items="${command.musicFolders}" var="folder" varStatus="loopStatus">
<tr>
<td><form:input path="musicFolders[${loopStatus.count-1}].name" size="20"/></td>
<td><form:input path="musicFolders[${loopStatus.count-1}].path" size="40"/></td>
<td align="center" style="padding-left:1em"><form:checkbox path="musicFolders[${loopStatus.count-1}].enabled" cssClass="checkbox"/></td>
<td align="center" style="padding-left:1em"><form:checkbox path="musicFolders[${loopStatus.count-1}].delete" cssClass="checkbox"/></td>
<td><c:if test="${not folder.existing}"><span class="warning"><fmt:message key="musicfoldersettings.notfound"/></span></c:if></td>
</tr>
</c:forEach>
<c:if test="${not empty command.musicFolders}">
<tr>
<th colspan="4" align="left" style="padding-top:1em"><fmt:message key="musicfoldersettings.add"/></th>
</tr>
</c:if>
<tr>
<td><form:input id="newMusicFolderName" path="newMusicFolder.name" size="20"/></td>
<td><form:input id="newMusicFolderPath" path="newMusicFolder.path" size="40"/></td>
<td align="center" style="padding-left:1em"><form:checkbox path="newMusicFolder.enabled" cssClass="checkbox"/></td>
<td></td>
</tr>
</table>
<p class="forward"><a href="userSettings.view"><fmt:message key="musicfoldersettings.access"/></a></p>
<p class="detail" style="width:60%;white-space:normal;margin-top:-10px;">
<fmt:message key="musicfoldersettings.access.description"/>
</p>
<div style="padding-top: 0.5em;padding-bottom: 0.3em">
<span style="white-space: nowrap">
<fmt:message key="musicfoldersettings.scan"/>
<form:select path="interval">
<fmt:message key="musicfoldersettings.interval.never" var="never"/>
<fmt:message key="musicfoldersettings.interval.one" var="one"/>
<form:option value="-1" label="${never}"/>
<form:option value="1" label="${one}"/>
<c:forTokens items="2 3 7 14 30 60" delims=" " var="interval">
<fmt:message key="musicfoldersettings.interval.many" var="many"><fmt:param value="${interval}"/></fmt:message>
<form:option value="${interval}" label="${many}"/>
</c:forTokens>
</form:select>
<form:select path="hour">
<c:forEach begin="0" end="23" var="hour">
<fmt:message key="musicfoldersettings.hour" var="hourLabel"><fmt:param value="${hour}"/></fmt:message>
<form:option value="${hour}" label="${hourLabel}"/>
</c:forEach>
</form:select>
</span>
</div>
<p class="forward"><a href="musicFolderSettings.view?scanNow"><fmt:message key="musicfoldersettings.scannow"/></a></p>
<c:if test="${command.scanning}">
<p style="width:60%"><b><fmt:message key="musicfoldersettings.nowscanning"/></b></p>
</c:if>
<div>
<form:checkbox path="fastCache" cssClass="checkbox" id="fastCache"/>
<form:label path="fastCache"><fmt:message key="musicfoldersettings.fastcache"/></form:label>
</div>
<p class="detail" style="width:60%;white-space:normal;">
<fmt:message key="musicfoldersettings.fastcache.description"/>
</p>
<p class="forward"><a href="musicFolderSettings.view?expunge"><fmt:message key="musicfoldersettings.expunge"/></a></p>
<p class="detail" style="width:60%;white-space:normal;margin-top:-10px;">
<fmt:message key="musicfoldersettings.expunge.description"/>
</p>
<%--<div>--%>
<%--<form:checkbox path="organizeByFolderStructure" cssClass="checkbox" id="organizeByFolderStructure"/>--%>
<%--<form:label path="organizeByFolderStructure"><fmt:message key="musicfoldersettings.organizebyfolderstructure"/></form:label>--%>
<%--</div>--%>
<%--<p class="detail" style="width:60%;white-space:normal;">--%>
<%--<fmt:message key="musicfoldersettings.organizebyfolderstructure.description"/>--%>
<%--</p>--%>
<p >
<input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
<input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
</p>
</form:form>
</body></html>
@@ -0,0 +1,103 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<%--@elvariable id="command" type="org.libresonic.player.command.NetworkSettingsCommand"--%>
<html>
<head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript" src="<c:url value="/dwr/interface/multiService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
<script type="text/javascript" language="javascript">
function init() {
enableUrlRedirectionFields();
refreshStatus();
}
function refreshStatus() {
multiService.getNetworkStatus(updateStatus);
}
function updateStatus(networkStatus) {
$("#portForwardingStatus").html(networkStatus.portForwardingStatusText);
$("#urlRedirectionStatus").html(networkStatus.urlRedirectionStatusText);
window.setTimeout("refreshStatus()", 1000);
}
function enableUrlRedirectionFields() {
var urlRedirectionEnabled = $("#urlRedirectionEnabled").is(":checked");
var normal = $("#urlRedirectTypeNormal").is(":checked");
var custom = $("#urlRedirectTypeCustom").is(":checked");
$("#urlRedirectFrom").prop("disabled", !urlRedirectionEnabled || !normal);
$("#urlRedirectCustomUrl").prop("disabled", !urlRedirectionEnabled || !custom);
$("#urlRedirectTypeNormal").prop("disabled", !urlRedirectionEnabled);
$("#urlRedirectTypeCustom").prop("disabled", !urlRedirectionEnabled);
}
</script>
</head>
<body class="mainframe bgcolor1" onload="init()">
<script type="text/javascript" src="<c:url value="/script/wz_tooltip.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/tip_balloon.js"/>"></script>
<c:import url="settingsHeader.jsp">
<c:param name="cat" value="network"/>
<c:param name="toast" value="${command.toast}"/>
</c:import>
<p style="padding-top:1em"><fmt:message key="networksettings.text"/></p>
<form:form commandName="command" action="networkSettings.view" method="post">
<p style="padding-top:1em">
<form:checkbox id="portForwardingEnabled" path="portForwardingEnabled"/>
<label for="portForwardingEnabled"><fmt:message key="networksettings.portforwardingenabled"/></label>
</p>
<div style="padding-left:2em;max-width:60em">
<p>
<fmt:message key="networksettings.portforwardinghelp"><fmt:param>${command.port}</fmt:param></fmt:message>
</p>
<p class="detail">
<fmt:message key="networksettings.status"/>
<span id="portForwardingStatus" style="margin-left:0.25em"></span>
</p>
</div>
<p style="padding-top:1em"><form:checkbox id="urlRedirectionEnabled" path="urlRedirectionEnabled"
onclick="enableUrlRedirectionFields()"/>
<label for="urlRedirectionEnabled"><fmt:message key="networksettings.urlredirectionenabled"/></label>
</p>
<div style="padding-left:2em">
<p>
<form:radiobutton id="urlRedirectTypeNormal" path="urlRedirectType" value="NORMAL" onclick="enableUrlRedirectionFields()"/>
<label for="urlRedirectTypeNormal"><fmt:message key="networksettings.normalurl"/></label>
http://<form:input id="urlRedirectFrom" path="urlRedirectFrom" size="16" cssStyle="margin-left:0.25em"/>.libresonic.org
</p>
<p>
<form:radiobutton id="urlRedirectTypeCustom" path="urlRedirectType" value="CUSTOM" onclick="enableUrlRedirectionFields()"/>
<label for="urlRedirectTypeCustom"><fmt:message key="networksettings.customurl"/></label>
<form:input id="urlRedirectCustomUrl" path="urlRedirectCustomUrl" size="50" cssStyle="margin-left:0.5em"/>
</p>
<p class="detail">
<fmt:message key="networksettings.status"/>
<span id="urlRedirectionStatus" style="margin-left:0.25em"></span>
<span id="urlRedirectionTestStatus" style="margin-left:0.25em"></span>
</p>
</div>
<c:set var="licenseInfo" value="${command.licenseInfo}"/>
<%@ include file="licenseNotice.jsp" %>
<p style="padding-top:1em">
<input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
<input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
</p>
</form:form>
</body>
</html>
@@ -0,0 +1,21 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<html>
<head>
<%@ include file="head.jsp" %>
</head>
<body class="mainframe bgcolor1">
<h1>
<img src="<spring:theme code="errorImage"/>" alt=""/>
<span style="vertical-align: middle"><fmt:message key="notFound.title"/></span>
</h1>
<fmt:message key="notFound.text"/>
<div class="forward" style="float:left;padding-right:10pt"><a href="javascript:top.location.reload(true)"><fmt:message key="notFound.reload"/></a></div>
<div class="forward" style="float:left"><a href="musicFolderSettings.view"><fmt:message key="notFound.scan"/></a></div>
</body>
</html>
@@ -0,0 +1,49 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<%--@elvariable id="command" type="org.libresonic.player.command.passwordsettingscommand"--%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
</head>
<body class="mainframe bgcolor1">
<c:import url="settingsHeader.jsp">
<c:param name="cat" value="password"/>
<c:param name="toast" value="${command.toast}"/>
<c:param name="restricted" value="true"/>
</c:import>
<c:choose>
<c:when test="${command.ldapAuthenticated}">
<p><fmt:message key="usersettings.passwordnotsupportedforldap"/></p>
</c:when>
<c:otherwise>
<fmt:message key="passwordsettings.title" var="title"><fmt:param>${command.username}</fmt:param></fmt:message>
<h2>${fn:escapeXml(title)}</h2>
<form:form method="post" action="passwordSettings.view" commandName="command">
<table class="indent">
<tr>
<td><fmt:message key="usersettings.newpassword"/></td>
<td><form:password path="password"/></td>
<td class="warning"><form:errors path="password"/></td>
</tr>
<tr>
<td><fmt:message key="usersettings.confirmpassword"/></td>
<td><form:password path="confirmPassword"/></td>
<td/>
</tr>
<tr>
<td colspan="3" style="padding-top:1.5em">
<input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
<input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
</td>
</tr>
</table>
</form:form>
</c:otherwise>
</c:choose>
</body></html>
@@ -0,0 +1,249 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<%--@elvariable id="command" type="org.libresonic.player.command.PersonalSettingsCommand"--%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript" src="<c:url value="/script/scripts-2.0.js"/>"></script>
<script type="text/javascript" language="javascript">
function enableLastFmFields() {
$("#lastFm").is(":checked") ? $("#lastFmTable").show() : $("#lastFmTable").hide();
}
</script>
</head>
<body class="mainframe bgcolor1" onload="enableLastFmFields()">
<script type="text/javascript" src="<c:url value="/script/wz_tooltip.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/tip_balloon.js"/>"></script>
<c:import url="settingsHeader.jsp">
<c:param name="cat" value="personal"/>
<c:param name="restricted" value="${not command.user.adminRole}"/>
<c:param name="toast" value="${command.reloadNeeded}"/>
</c:import>
<fmt:message key="personalsettings.title" var="title"><fmt:param>${command.user.username}</fmt:param></fmt:message>
<h2>${fn:escapeXml(title)}</h2>
<fmt:message key="common.default" var="defaultTitle"/>
<form:form method="post" action="personalSettings.view" commandName="command">
<table style="white-space:nowrap" class="indent">
<tr>
<td><fmt:message key="personalsettings.language"/></td>
<td>
<form:select path="localeIndex" cssStyle="width:15em">
<form:option value="-1" label="${defaultTitle}"/>
<c:forEach items="${command.locales}" var="locale" varStatus="loopStatus">
<form:option value="${loopStatus.count - 1}" label="${locale}"/>
</c:forEach>
</form:select>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="language"/></c:import>
</td>
</tr>
<tr>
<td><fmt:message key="personalsettings.theme"/></td>
<td>
<form:select path="themeIndex" cssStyle="width:15em">
<form:option value="-1" label="${defaultTitle}"/>
<c:forEach items="${command.themes}" var="theme" varStatus="loopStatus">
<form:option value="${loopStatus.count - 1}" label="${theme.name}"/>
</c:forEach>
</form:select>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="theme"/></c:import>
</td>
</tr>
<tr>
<td><fmt:message key="personalsettings.albumlist"/></td>
<td>
<form:select path="albumListId" cssStyle="width:15em">
<c:forEach items="${command.albumLists}" var="albumList" varStatus="loopStatus">
<c:set var="label">
<fmt:message key="home.${albumList.id}.title"/>
</c:set>
<form:option value="${albumList.id}" label="${label}"/>
</c:forEach>
</form:select>
</td>
</tr>
</table>
<table class="indent">
<tr>
<th style="padding:0 0.5em 0.5em 0;text-align:left;"><fmt:message key="personalsettings.display"/></th>
<th style="padding:0 0.5em 0.5em 0.5em;text-align:center;"><fmt:message key="personalsettings.browse"/></th>
<th style="padding:0 0 0.5em 0.5em;text-align:center;"><fmt:message key="personalsettings.playlist"/></th>
<th style="padding:0 0 0.5em 0.5em">
<c:import url="helpToolTip.jsp"><c:param name="topic" value="visibility"/></c:import>
</th>
</tr>
<tr>
<td><fmt:message key="personalsettings.tracknumber"/></td>
<td style="text-align:center"><form:checkbox path="mainVisibility.trackNumberVisible" cssClass="checkbox"/></td>
<td style="text-align:center"><form:checkbox path="playlistVisibility.trackNumberVisible" cssClass="checkbox"/></td>
</tr>
<tr>
<td><fmt:message key="personalsettings.artist"/></td>
<td style="text-align:center"><form:checkbox path="mainVisibility.artistVisible" cssClass="checkbox"/></td>
<td style="text-align:center"><form:checkbox path="playlistVisibility.artistVisible" cssClass="checkbox"/></td>
</tr>
<tr>
<td><fmt:message key="personalsettings.album"/></td>
<td style="text-align:center"><form:checkbox path="mainVisibility.albumVisible" cssClass="checkbox"/></td>
<td style="text-align:center"><form:checkbox path="playlistVisibility.albumVisible" cssClass="checkbox"/></td>
</tr>
<tr>
<td><fmt:message key="personalsettings.genre"/></td>
<td style="text-align:center"><form:checkbox path="mainVisibility.genreVisible" cssClass="checkbox"/></td>
<td style="text-align:center"><form:checkbox path="playlistVisibility.genreVisible" cssClass="checkbox"/></td>
</tr>
<tr>
<td><fmt:message key="personalsettings.year"/></td>
<td style="text-align:center"><form:checkbox path="mainVisibility.yearVisible" cssClass="checkbox"/></td>
<td style="text-align:center"><form:checkbox path="playlistVisibility.yearVisible" cssClass="checkbox"/></td>
</tr>
<tr>
<td><fmt:message key="personalsettings.bitrate"/></td>
<td style="text-align:center"><form:checkbox path="mainVisibility.bitRateVisible" cssClass="checkbox"/></td>
<td style="text-align:center"><form:checkbox path="playlistVisibility.bitRateVisible" cssClass="checkbox"/></td>
</tr>
<tr>
<td><fmt:message key="personalsettings.duration"/></td>
<td style="text-align:center"><form:checkbox path="mainVisibility.durationVisible" cssClass="checkbox"/></td>
<td style="text-align:center"><form:checkbox path="playlistVisibility.durationVisible" cssClass="checkbox"/></td>
</tr>
<tr>
<td><fmt:message key="personalsettings.format"/></td>
<td style="text-align:center"><form:checkbox path="mainVisibility.formatVisible" cssClass="checkbox"/></td>
<td style="text-align:center"><form:checkbox path="playlistVisibility.formatVisible" cssClass="checkbox"/></td>
</tr>
<tr>
<td><fmt:message key="personalsettings.filesize"/></td>
<td style="text-align:center"><form:checkbox path="mainVisibility.fileSizeVisible" cssClass="checkbox"/></td>
<td style="text-align:center"><form:checkbox path="playlistVisibility.fileSizeVisible" cssClass="checkbox"/></td>
</tr>
</table>
<table class="indent">
<tr>
<td><form:checkbox path="showNowPlayingEnabled" id="nowPlaying" cssClass="checkbox"/></td>
<td><label for="nowPlaying"><fmt:message key="personalsettings.shownowplaying"/></label></td>
<td style="padding-left:2em"><form:checkbox path="showChatEnabled" id="chat" cssClass="checkbox"/></td>
<td><label for="chat"><fmt:message key="personalsettings.showchat"/></label></td>
<td style="padding-left:2em"><form:checkbox path="showArtistInfoEnabled" id="artistInfo" cssClass="checkbox"/></td>
<td><label for="artistInfo"><fmt:message key="personalsettings.showartistinfo"/></label></td>
</tr>
<tr>
<td><form:checkbox path="nowPlayingAllowed" id="nowPlayingAllowed" cssClass="checkbox"/></td>
<td><label for="nowPlayingAllowed"><fmt:message key="personalsettings.nowplayingallowed"/></label></td>
<td style="padding-left:2em"><form:checkbox path="autoHidePlayQueue" id="autoHidePlayQueue" cssClass="checkbox"/></td>
<td><label for="autoHidePlayQueue"><fmt:message key="personalsettings.autohideplayqueue"/></label></td>
<td style="padding-left:2em"><form:checkbox path="partyModeEnabled" id="partyModeEnabled" cssClass="checkbox"/></td>
<td><label for="partyModeEnabled"><fmt:message key="personalsettings.partymode"/></label>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="partymode"/></c:import>
</td>
</tr>
<tr>
<td><form:checkbox path="queueFollowingSongs" id="queueFollowingSongs" cssClass="checkbox"/></td>
<td><label for="queueFollowingSongs"><fmt:message key="personalsettings.queuefollowingsongs"/></label></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<table class="indent">
<tr>
<td><form:checkbox path="finalVersionNotificationEnabled" id="final" cssClass="checkbox"/></td>
<td><label for="final"><fmt:message key="personalsettings.finalversionnotification"/></label></td>
</tr>
<tr>
<td><form:checkbox path="betaVersionNotificationEnabled" id="beta" cssClass="checkbox"/></td>
<td><label for="beta"><fmt:message key="personalsettings.betaversionnotification"/></label></td>
</tr>
<tr>
<td><form:checkbox path="songNotificationEnabled" id="song" cssClass="checkbox"/></td>
<td><label for="song"><fmt:message key="personalsettings.songnotification"/></label></td>
</tr>
</table>
<table class="indent">
<tr>
<td><form:checkbox path="lastFmEnabled" id="lastFm" cssClass="checkbox" onclick="enableLastFmFields()"/></td>
<td><label for="lastFm"><fmt:message key="personalsettings.lastfmenabled"/></label></td>
</tr>
</table>
<table id="lastFmTable" style="padding-left:2em">
<tr>
<td><fmt:message key="personalsettings.lastfmusername"/></td>
<td><form:input path="lastFmUsername" size="24"/></td>
</tr>
<tr>
<td><fmt:message key="personalsettings.lastfmpassword"/></td>
<td><form:password path="lastFmPassword" size="24"/></td>
</tr>
</table>
<p style="padding-top:1em;padding-bottom:1em">
<input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em"/>
<input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
</p>
<h2><fmt:message key="personalsettings.avatar.title"/></h2>
<p style="padding-top:1em">
<c:forEach items="${command.avatars}" var="avatar">
<c:url value="avatar.view" var="avatarUrl">
<c:param name="id" value="${avatar.id}"/>
</c:url>
<span style="white-space:nowrap;">
<form:radiobutton id="avatar-${avatar.id}" path="avatarId" value="${avatar.id}"/>
<label for="avatar-${avatar.id}"><img src="${avatarUrl}" alt="${avatar.name}" width="${avatar.width}" height="${avatar.height}" style="padding-right:2em;padding-bottom:1em"/></label>
</span>
</c:forEach>
</p>
<p>
<form:radiobutton id="noAvatar" path="avatarId" value="-1"/>
<label for="noAvatar"><fmt:message key="personalsettings.avatar.none"/></label>
</p>
<p>
<form:radiobutton id="customAvatar" path="avatarId" value="-2"/>
<label for="customAvatar"><fmt:message key="personalsettings.avatar.custom"/>
<c:if test="${not empty command.customAvatar}">
<sub:url value="avatar.view" var="avatarUrl">
<sub:param name="username" value="${command.user.username}"/>
<sub:param name="forceCustom" value="true"/>
</sub:url>
<img src="${avatarUrl}" alt="${command.customAvatar.name}" width="${command.customAvatar.width}" height="${command.customAvatar.height}" style="padding-right:2em"/>
</c:if>
</label>
</p>
</form:form>
<form method="post" enctype="multipart/form-data" action="avatarUpload.view">
<table>
<tr>
<td style="padding-right:1em"><fmt:message key="personalsettings.avatar.changecustom"/></td>
<td style="padding-right:1em"><input type="file" id="file" name="file" size="40"/></td>
<td style="padding-right:1em"><input type="submit" value="<fmt:message key="personalsettings.avatar.upload"/>"/></td>
</tr>
</table>
</form>
<p class="detail" style="text-align:right">
<fmt:message key="personalsettings.avatar.courtesy"/>
</p>
<c:if test="${command.reloadNeeded}">
<script language="javascript" type="text/javascript">
parent.location.href="index.view?";
</script>
</c:if>
</body></html>
@@ -0,0 +1,80 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<%@ include file="include.jsp" %>
<%--
PARAMETERS
id: ID of file.
video: Whether the file is a video (default false).
playEnabled: Whether to show play button (default true).
addEnabled: Whether to show add next/last buttons (default true).
downloadEnabled: Whether to show download button (default false).
starEnabled: Whether to show star/unstar controls (default false).
starred: Whether the file is currently starred.
asTable: Whether to put the images in td tags.
onPlay: Overrides the javascript used for the play action.
--%>
<c:if test="${param.starEnabled}">
<c:if test="${param.asTable}"><td class="fit"></c:if>
<c:choose>
<c:when test="${param.starred}">
<img id="starImage${param.id}" src="<spring:theme code="ratingOnImage"/>" alt="" style="cursor:pointer"
onclick="toggleStar(${param.id}, '#starImage${param.id}'); return false;">
</c:when>
<c:otherwise>
<img id="starImage${param.id}" src="<spring:theme code="ratingOffImage"/>" alt="" style="cursor:pointer"
onclick="toggleStar(${param.id}, '#starImage${param.id}'); return false;">
</c:otherwise>
</c:choose>
<c:if test="${param.asTable}"></td></c:if>
</c:if>
<c:if test="${param.asTable}"><td class="fit"></c:if>
<c:if test="${empty param.playEnabled or param.playEnabled}">
<c:choose>
<c:when test="${param.video}">
<sub:url value="/videoPlayer.view" var="videoUrl">
<sub:param name="id" value="${param.id}"/>
</sub:url>
<a href="${videoUrl}" target="main">
<img src="<spring:theme code="playImage"/>" alt="<fmt:message key="common.play"/>"
title="<fmt:message key="common.play"/>"></a>
</c:when>
<c:when test="${not empty param.onPlay}">
<img src="<spring:theme code="playImage"/>" alt="<fmt:message key="common.play"/>" style="cursor:pointer"
onclick="${param.onPlay}; return false;" title="<fmt:message key="common.play"/>">
</c:when>
<c:otherwise>
<img src="<spring:theme code="playImage"/>" alt="<fmt:message key="common.play"/>" style="cursor:pointer"
onclick="top.playQueue.onPlay(${param.id}); return false;" title="<fmt:message key="common.play"/>">
</c:otherwise>
</c:choose>
</c:if>
<c:if test="${param.asTable}"></td></c:if>
<c:if test="${param.asTable}"><td class="fit"></c:if>
<c:if test="${(empty param.addEnabled or param.addEnabled) and not param.video}">
<img id="add${param.id}" src="<spring:theme code="addImage"/>" alt="<fmt:message key="main.addlast"/>"
onclick="top.playQueue.onAdd(${param.id}); $().toastmessage('showSuccessToast', '<fmt:message key="main.addlast.toast"/>'); return false;"
style="cursor:pointer" title="<fmt:message key="main.addlast"/>">
</c:if>
<c:if test="${param.asTable}"></td></c:if>
<c:if test="${param.asTable}"><td class="fit"></c:if>
<c:if test="${(empty param.addEnabled or param.addEnabled) and not param.video}">
<img id="add${param.id}" src="<spring:theme code="addNextImage"/>" alt="<fmt:message key="main.addnext"/>"
onclick="top.playQueue.onAddNext(${param.id}); $().toastmessage('showSuccessToast', '<fmt:message key="main.addnext.toast"/>'); return false;"
style="cursor:pointer" title="<fmt:message key="main.addnext"/>">
</c:if>
<c:if test="${param.asTable}"></td></c:if>
<c:if test="${param.asTable}"><td class="fit"></c:if>
<c:if test="${param.downloadEnabled}">
<sub:url value="/download.view" var="downloadUrl">
<sub:param name="id" value="${param.id}"/>
</sub:url>
<a href="${downloadUrl}">
<img src="<spring:theme code="downloadImage"/>" alt="<fmt:message key="common.download"/>"
title="<fmt:message key="common.download"/>" ></a>
</c:if>
<c:if test="${param.asTable}"></td></c:if>
@@ -0,0 +1,749 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<link type="text/css" rel="stylesheet" href="<c:url value="/script/webfx/luna.css"/>">
<script type="text/javascript" src="<c:url value="/script/scripts-2.0.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/interface/nowPlayingService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/interface/playQueueService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/interface/playlistService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/util.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/jwplayer-5.10.min.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/cast_sender-v1.js"/>"></script>
<%@ include file="playQueueCast.jsp" %>
<link type="text/css" rel="stylesheet" href="<c:url value="/script/webfx/luna.css"/>">
<style type="text/css">
.ui-slider .ui-slider-handle {
width: 11px;
height: 11px;
cursor: pointer;
}
.ui-slider a {
outline:none;
}
.ui-slider {
cursor: pointer;
}
</style>
</head>
<body class="bgcolor2 playlistframe" onload="init()">
<span id="dummy-animation-target" style="max-width:50px;display: none"></span>
<script type="text/javascript" language="javascript">
var songs = null;
var currentStreamUrl = null;
var repeatEnabled = false;
var CastPlayer = new CastPlayer();
var ignore = false;
function init() {
<c:if test="${model.autoHide}">initAutoHide();</c:if>
dwr.engine.setErrorHandler(null);
startTimer();
$("#dialog-select-playlist").dialog({resizable: true, height: 220, autoOpen: false,
buttons: {
"<fmt:message key="common.cancel"/>": function() {
$(this).dialog("close");
}
}});
<c:if test="${model.player.web}">createPlayer();</c:if>
$("#playlistBody").sortable({
stop: function(event, ui) {
var indexes = [];
$("#playlistBody").children().each(function() {
var id = $(this).attr("id").replace("pattern", "");
if (id.length > 0) {
indexes.push(parseInt(id) - 1);
}
});
onRearrange(indexes);
},
cursor: "move",
axis: "y",
containment: "parent",
helper: function(e, tr) {
var originals = tr.children();
var trclone = tr.clone();
trclone.children().each(function(index) {
// Set cloned cell sizes to match the original sizes
$(this).width(originals.eq(index).width());
$(this).css("maxWidth", originals.eq(index).width());
$(this).css("border-top", "1px solid black");
$(this).css("border-bottom", "1px solid black");
});
return trclone;
}
});
getPlayQueue();
}
function initAutoHide() {
$(window).mouseleave(function (event) {
if (event.clientY < 30) {
setFrameHeight(50);
}
});
$(window).mouseenter(function () {
var height = $("body").height() + 25;
height = Math.min(height, window.top.innerHeight * 0.8);
setFrameHeight(height);
});
}
function setFrameHeight(height) {
<%-- Disable animation in Chrome. It stopped working in Chrome 44. --%>
var duration = navigator.userAgent.indexOf("Chrome") != -1 ? 0 : 400;
$("#dummy-animation-target").stop();
$("#dummy-animation-target").animate({"max-width": height}, {
step: function (now, fx) {
top.document.getElementById("playQueueFrameset").rows = "*," + now;
},
duration: duration
});
}
function startTimer() {
<!-- Periodically check if the current song has changed. -->
nowPlayingService.getNowPlayingForCurrentPlayer(nowPlayingCallback);
setTimeout("startTimer()", 10000);
}
function nowPlayingCallback(nowPlayingInfo) {
if (nowPlayingInfo != null && nowPlayingInfo.streamUrl != currentStreamUrl) {
getPlayQueue();
<c:if test="${not model.player.web}">
currentStreamUrl = nowPlayingInfo.streamUrl;
updateCurrentImage();
</c:if>
}
}
function createPlayer() {
jwplayer("jwplayer").setup({
flashplayer: "<c:url value="/flash/jw-player-5.10.swf"/>",
height: 24,
width: 350,
controlbar: "bottom",
backcolor:"<spring:theme code="backgroundColor"/>",
frontcolor:"<spring:theme code="textColor"/>"
});
jwplayer().onComplete(function() {onNext(repeatEnabled)});
}
function getPlayQueue() {
playQueueService.getPlayQueue(playQueueCallback);
}
function onClear() {
var ok = true;
<c:if test="${model.partyMode}">
ok = confirm("<fmt:message key="playlist.confirmclear"/>");
</c:if>
if (ok) {
playQueueService.clear(playQueueCallback);
}
}
function onStart() {
playQueueService.start(playQueueCallback);
}
function onStop() {
playQueueService.stop(playQueueCallback);
}
function onGain(gain) {
playQueueService.setGain(gain);
}
function onJukeboxVolumeChanged() {
var value = parseInt($("#jukeboxVolume").slider("option", "value"));
onGain(value / 100);
}
function onCastVolumeChanged() {
var value = parseInt($("#castVolume").slider("option", "value"));
CastPlayer.setCastVolume(value / 100, false);
}
function onSkip(index) {
<c:choose>
<c:when test="${model.player.web}">
skip(index);
</c:when>
<c:otherwise>
currentStreamUrl = songs[index].streamUrl;
playQueueService.skip(index, playQueueCallback);
</c:otherwise>
</c:choose>
}
function onNext(wrap) {
var index = parseInt(getCurrentSongIndex()) + 1;
if (wrap) {
index = index % songs.length;
}
skip(index);
}
function onPrevious() {
skip(parseInt(getCurrentSongIndex()) - 1);
}
function onPlay(id) {
playQueueService.play(id, playQueueCallback);
}
function onPlayShuffle(albumListType, offset, size, genre, decade) {
playQueueService.playShuffle(albumListType, offset, size, genre, decade, playQueueCallback);
}
function onPlayPlaylist(id, index) {
playQueueService.playPlaylist(id, index, playQueueCallback);
}
function onPlayTopSong(id, index) {
playQueueService.playTopSong(id, index, playQueueCallback);
}
function onPlayPodcastChannel(id) {
playQueueService.playPodcastChannel(id, playQueueCallback);
}
function onPlayPodcastEpisode(id) {
playQueueService.playPodcastEpisode(id, playQueueCallback);
}
function onPlayNewestPodcastEpisode(index) {
playQueueService.playNewestPodcastEpisode(index, playQueueCallback);
}
function onPlayStarred() {
playQueueService.playStarred(playQueueCallback);
}
function onPlayRandom(id, count) {
playQueueService.playRandom(id, count, playQueueCallback);
}
function onPlaySimilar(id, count) {
playQueueService.playSimilar(id, count, playQueueCallback);
}
function onAdd(id) {
playQueueService.add(id, playQueueCallback);
}
function onAddNext(id) {
playQueueService.addAt(id, getCurrentSongIndex() + 1, playQueueCallback);
}
function onShuffle() {
playQueueService.shuffle(playQueueCallback);
}
function onStar(index) {
playQueueService.toggleStar(index, playQueueCallback);
}
function onRemove(index) {
playQueueService.remove(index, playQueueCallback);
}
function onRemoveSelected() {
var indexes = new Array();
var counter = 0;
for (var i = 0; i < songs.length; i++) {
var index = i + 1;
if ($("#songIndex" + index).is(":checked")) {
indexes[counter++] = i;
}
}
playQueueService.removeMany(indexes, playQueueCallback);
}
function onRearrange(indexes) {
playQueueService.rearrange(indexes, playQueueCallback);
}
function onToggleRepeat() {
playQueueService.toggleRepeat(playQueueCallback);
}
function onUndo() {
playQueueService.undo(playQueueCallback);
}
function onSortByTrack() {
playQueueService.sortByTrack(playQueueCallback);
}
function onSortByArtist() {
playQueueService.sortByArtist(playQueueCallback);
}
function onSortByAlbum() {
playQueueService.sortByAlbum(playQueueCallback);
}
function onSavePlayQueue() {
var positionMillis = jwplayer() ? Math.round(1000.0 * jwplayer().getPosition()) : 0;
playQueueService.savePlayQueue(getCurrentSongIndex(), positionMillis);
$().toastmessage("showSuccessToast", "<fmt:message key="playlist.toast.saveplayqueue"/>");
}
function onLoadPlayQueue() {
playQueueService.loadPlayQueue(playQueueCallback);
}
function onSavePlaylist() {
playlistService.createPlaylistForPlayQueue(function (playlistId) {
top.left.updatePlaylists();
top.left.showAllPlaylists();
top.main.location.href = "playlist.view?id=" + playlistId;
$().toastmessage("showSuccessToast", "<fmt:message key="playlist.toast.saveasplaylist"/>");
});
}
function onAppendPlaylist() {
playlistService.getWritablePlaylists(playlistCallback);
}
function playlistCallback(playlists) {
$("#dialog-select-playlist-list").empty();
for (var i = 0; i < playlists.length; i++) {
var playlist = playlists[i];
$("<p class='dense'><b><a href='#' onclick='appendPlaylist(" + playlist.id + ")'>" + escapeHtml(playlist.name)
+ "</a></b></p>").appendTo("#dialog-select-playlist-list");
}
$("#dialog-select-playlist").dialog("open");
}
function appendPlaylist(playlistId) {
$("#dialog-select-playlist").dialog("close");
var mediaFileIds = new Array();
for (var i = 0; i < songs.length; i++) {
if ($("#songIndex" + (i + 1)).is(":checked")) {
mediaFileIds.push(songs[i].id);
}
}
playlistService.appendToPlaylist(playlistId, mediaFileIds, function (){
top.left.updatePlaylists();
top.main.location.href = "playlist.view?id=" + playlistId;
$().toastmessage("showSuccessToast", "<fmt:message key="playlist.toast.appendtoplaylist"/>");
});
}
function playQueueCallback(playQueue) {
songs = playQueue.entries;
repeatEnabled = playQueue.repeatEnabled;
if ($("#start")) {
$("#start").toggle(!playQueue.stopEnabled);
$("#stop").toggle(playQueue.stopEnabled);
}
if ($("#toggleRepeat")) {
var text = repeatEnabled ? "<fmt:message key="playlist.repeat_on"/>" : "<fmt:message key="playlist.repeat_off"/>";
$("#toggleRepeat").html(text);
}
if (songs.length == 0) {
$("#songCountAndDuration").html("");
$("#empty").show();
} else {
$("#songCountAndDuration").html(songs.length + " <fmt:message key="playlist2.songs"/> &ndash; " + playQueue.durationAsString);
$("#empty").hide();
}
// Delete all the rows except for the "pattern" row
dwr.util.removeAllRows("playlistBody", { filter:function(tr) {
return (tr.id != "pattern");
}});
// Create a new set cloned from the pattern row
for (var i = 0; i < songs.length; i++) {
var song = songs[i];
var id = i + 1;
dwr.util.cloneNode("pattern", { idSuffix:id });
if ($("#trackNumber" + id)) {
$("#trackNumber" + id).html(song.trackNumber);
}
if (song.starred) {
$("#starSong" + id).attr("src", "<spring:theme code='ratingOnImage'/>");
} else {
$("#starSong" + id).attr("src", "<spring:theme code='ratingOffImage'/>");
}
if ($("#currentImage" + id) && song.streamUrl == currentStreamUrl) {
$("#currentImage" + id).show();
}
if ($("#title" + id)) {
$("#title" + id).html(song.title);
$("#title" + id).attr("title", song.title);
}
if ($("#titleUrl" + id)) {
$("#titleUrl" + id).html(song.title);
$("#titleUrl" + id).attr("title", song.title);
$("#titleUrl" + id).click(function () {onSkip(this.id.substring(8) - 1)});
}
if ($("#album" + id)) {
$("#album" + id).html(song.album);
$("#album" + id).attr("title", song.album);
$("#albumUrl" + id).attr("href", song.albumUrl);
}
if ($("#artist" + id)) {
$("#artist" + id).html(song.artist);
$("#artist" + id).attr("title", song.artist);
}
if ($("#genre" + id)) {
$("#genre" + id).html(song.genre);
}
if ($("#year" + id)) {
$("#year" + id).html(song.year);
}
if ($("#bitRate" + id)) {
$("#bitRate" + id).html(song.bitRate);
}
if ($("#duration" + id)) {
$("#duration" + id).html(song.durationAsString);
}
if ($("#format" + id)) {
$("#format" + id).html(song.format);
}
if ($("#fileSize" + id)) {
$("#fileSize" + id).html(song.fileSize);
}
$("#pattern" + id).addClass((i % 2 == 0) ? "bgcolor1" : "bgcolor2");
// Note: show() method causes page to scroll to top.
$("#pattern" + id).css("display", "table-row");
}
if (playQueue.sendM3U) {
parent.frames.main.location.href="play.m3u?";
}
var jukeboxVolume = $("#jukeboxVolume");
if (jukeboxVolume) {
jukeboxVolume.slider("option", "value", Math.floor(playQueue.gain * 100));
}
<c:if test="${model.player.web}">
triggerPlayer(playQueue.startPlayerAt, playQueue.startPlayerAtPosition);
</c:if>
}
function triggerPlayer(index, positionMillis) {
if (index != -1) {
if (songs.length > index) {
skip(index);
if (positionMillis != 0) {
jwplayer().seek(positionMillis / 1000);
}
}
}
updateCurrentImage();
if (songs.length == 0) {
jwplayer().stop();
jwplayer().load([]);
}
}
function skip(index, position) {
if (index < 0 || index >= songs.length) {
return;
}
var song = songs[index];
currentStreamUrl = song.streamUrl;
updateCurrentImage();
if (CastPlayer.castSession) {
CastPlayer.loadCastMedia(song, position);
} else {
jwplayer().load({
file: song.streamUrl,
provider: song.format == "aac" || song.format == "m4a" ? "video" : "sound",
duration: song.duration
});
jwplayer().play();
console.log(song.streamUrl);
}
updateWindowTitle(song);
<c:if test="${model.notify}">
showNotification(song);
</c:if>
}
function updateWindowTitle(song) {
top.document.title = song.title + " - " + song.artist + " - Libresonic";
}
function showNotification(song) {
if (!("Notification" in window)) {
return;
}
if (Notification.permission === "granted") {
createNotification(song);
}
else if (Notification.permission !== 'denied') {
Notification.requestPermission(function (permission) {
Notification.permission = permission;
if (permission === "granted") {
createNotification(song);
}
});
}
}
function createNotification(song) {
var n = new Notification(song.title, {
tag: "libresonic",
body: song.artist + " - " + song.album,
icon: "coverArt.view?id=" + song.id + "&size=110"
});
n.onshow = function() {
setTimeout(function() {n.close()}, 5000);
}
}
function updateCurrentImage() {
for (var i = 0; i < songs.length; i++) {
var song = songs[i];
var id = i + 1;
var image = $("#currentImage" + id);
if (image) {
if (song.streamUrl == currentStreamUrl) {
image.show();
} else {
image.hide();
}
}
}
}
function getCurrentSongIndex() {
for (var i = 0; i < songs.length; i++) {
if (songs[i].streamUrl == currentStreamUrl) {
return i;
}
}
return -1;
}
<!-- actionSelected() is invoked when the users selects from the "More actions..." combo box. -->
function actionSelected(id) {
var selectedIndexes = getSelectedIndexes();
if (id == "top") {
return;
} else if (id == "savePlayQueue") {
onSavePlayQueue();
} else if (id == "loadPlayQueue") {
onLoadPlayQueue();
} else if (id == "savePlaylist") {
onSavePlaylist();
} else if (id == "downloadPlaylist") {
location.href = "download.view?player=${model.player.id}";
} else if (id == "sharePlaylist") {
parent.frames.main.location.href = "createShare.view?player=${model.player.id}&" + getSelectedIndexes();
} else if (id == "sortByTrack") {
onSortByTrack();
} else if (id == "sortByArtist") {
onSortByArtist();
} else if (id == "sortByAlbum") {
onSortByAlbum();
} else if (id == "selectAll") {
selectAll(true);
} else if (id == "selectNone") {
selectAll(false);
} else if (id == "removeSelected") {
onRemoveSelected();
} else if (id == "download" && selectedIndexes != "") {
location.href = "download.view?player=${model.player.id}&" + selectedIndexes;
} else if (id == "appendPlaylist" && selectedIndexes != "") {
onAppendPlaylist();
}
$("#moreActions").prop("selectedIndex", 0);
}
function getSelectedIndexes() {
var result = "";
for (var i = 0; i < songs.length; i++) {
if ($("#songIndex" + (i + 1)).is(":checked")) {
result += "i=" + i + "&";
}
}
return result;
}
function selectAll(b) {
for (var i = 0; i < songs.length; i++) {
if (b) {
$("#songIndex" + (i + 1)).attr("checked", "checked");
} else {
$("#songIndex" + (i + 1)).removeAttr("checked");
}
}
}
</script>
<div class="bgcolor2" style="position:fixed; bottom:0; width:100%;padding-top:10px;padding-bottom: 5px">
<table style="white-space:nowrap;">
<tr style="white-space:nowrap;">
<c:if test="${model.user.settingsRole and fn:length(model.players) gt 1}">
<td style="padding-right: 5px"><select name="player" onchange="location='playQueue.view?player=' + options[selectedIndex].value;">
<c:forEach items="${model.players}" var="player">
<option ${player.id eq model.player.id ? "selected" : ""} value="${player.id}">${player.shortDescription}</option>
</c:forEach>
</select></td>
</c:if>
<c:if test="${model.player.web}">
<td>
<div id="flashPlayer" style="width:340px; height:24px;padding-right:10px">
<div id="jwplayer"><a href="http://www.adobe.com/go/getflashplayer" target="_blank"><fmt:message key="playlist.getflash"/></a></div>
</div>
<div id="castPlayer" style="display: none">
<div style="float:left">
<img id="castPlay" src="<spring:theme code="castPlayImage"/>" onclick="CastPlayer.playCast()" style="cursor:pointer">
<img id="castPause" src="<spring:theme code="castPauseImage"/>" onclick="CastPlayer.pauseCast()" style="cursor:pointer; display:none">
<img id="castMuteOn" src="<spring:theme code="volumeImage"/>" onclick="CastPlayer.castMuteOn()" style="cursor:pointer">
<img id="castMuteOff" src="<spring:theme code="muteImage"/>" onclick="CastPlayer.castMuteOff()" style="cursor:pointer; display:none">
</div>
<div style="float:left">
<div id="castVolume" style="width:80px;height:4px;margin-left:10px;margin-right:10px;margin-top:8px"></div>
<script type="text/javascript">
$("#castVolume").slider({max: 100, value: 50, animate: "fast", range: "min"});
$("#castVolume").on("slidestop", onCastVolumeChanged);
</script>
</div>
</div>
</td>
<td>
<img id="castOn" src="<spring:theme code="castIdleImage"/>" onclick="CastPlayer.launchCastApp()" style="cursor:pointer; display:none">
<img id="castOff" src="<spring:theme code="castActiveImage"/>" onclick="CastPlayer.stopCastApp()" style="cursor:pointer; display:none">
</td>
</c:if>
<c:if test="${model.user.streamRole and not model.player.web}">
<td>
<img id="start" src="<spring:theme code="castPlayImage"/>" onclick="onStart()" style="cursor:pointer">
<img id="stop" src="<spring:theme code="castPauseImage"/>" onclick="onStop()" style="cursor:pointer; display:none">
</td>
</c:if>
<c:if test="${model.player.jukebox}">
<td style="white-space:nowrap;">
<img src="<spring:theme code="volumeImage"/>" alt="">
</td>
<td style="white-space:nowrap;">
<div id="jukeboxVolume" style="width:80px;height:4px"></div>
<script type="text/javascript">
$("#jukeboxVolume").slider({max: 100, value: 50, animate: "fast", range: "min"});
$("#jukeboxVolume").on("slidestop", onJukeboxVolumeChanged);
</script>
</td>
</c:if>
<c:if test="${model.player.web}">
<td><span class="header">
<img src="<spring:theme code="backImage"/>" alt="" onclick="onPrevious()" style="cursor:pointer"></span>
</td>
<td><span class="header">
<img src="<spring:theme code="forwardImage"/>" alt="" onclick="onNext(false)" style="cursor:pointer"></span>
</td>
</c:if>
<td style="white-space:nowrap;"><span class="header"><a href="javascript:onClear()"><fmt:message key="playlist.clear"/></a></span> |</td>
<td style="white-space:nowrap;"><span class="header"><a href="javascript:onShuffle()"><fmt:message key="playlist.shuffle"/></a></span> |</td>
<c:if test="${model.player.web or model.player.jukebox or model.player.external}">
<td style="white-space:nowrap;"><span class="header"><a href="javascript:onToggleRepeat()"><span id="toggleRepeat"><fmt:message key="playlist.repeat_on"/></span></a></span> |</td>
</c:if>
<td style="white-space:nowrap;"><span class="header"><a href="javascript:onUndo()"><fmt:message key="playlist.undo"/></a></span> |</td>
<c:if test="${model.user.settingsRole}">
<td style="white-space:nowrap;"><span class="header"><a href="playerSettings.view?id=${model.player.id}" target="main"><fmt:message key="playlist.settings"/></a></span> |</td>
</c:if>
<td style="white-space:nowrap;"><select id="moreActions" onchange="actionSelected(this.options[selectedIndex].id)">
<option id="top" selected="selected"><fmt:message key="playlist.more"/></option>
<optgroup label="<fmt:message key="playlist.more.playlist"/>">
<option id="savePlayQueue"><fmt:message key="playlist.saveplayqueue"/></option>
<option id="loadPlayQueue"><fmt:message key="playlist.loadplayqueue"/></option>
<option id="savePlaylist"><fmt:message key="playlist.save"/></option>
<c:if test="${model.user.downloadRole}">
<option id="downloadPlaylist"><fmt:message key="common.download"/></option>
</c:if>
<c:if test="${model.user.shareRole}">
<option id="sharePlaylist"><fmt:message key="main.more.share"/></option>
</c:if>
<option id="sortByTrack"><fmt:message key="playlist.more.sortbytrack"/></option>
<option id="sortByAlbum"><fmt:message key="playlist.more.sortbyalbum"/></option>
<option id="sortByArtist"><fmt:message key="playlist.more.sortbyartist"/></option>
</optgroup>
<optgroup label="<fmt:message key="playlist.more.selection"/>">
<option id="selectAll"><fmt:message key="playlist.more.selectall"/></option>
<option id="selectNone"><fmt:message key="playlist.more.selectnone"/></option>
<option id="removeSelected"><fmt:message key="playlist.remove"/></option>
<c:if test="${model.user.downloadRole}">
<option id="download"><fmt:message key="common.download"/></option>
</c:if>
<option id="appendPlaylist"><fmt:message key="playlist.append"/></option>
</optgroup>
</select>
</td>
</tr></table>
</div>
<h2 style="float:left"><fmt:message key="playlist.more.playlist"/></h2>
<h2 id="songCountAndDuration" style="float:right;padding-right:1em"></h2>
<div style="clear:both"></div>
<p id="empty"><em><fmt:message key="playlist.empty"/></em></p>
<table class="music indent" style="cursor:pointer">
<tbody id="playlistBody">
<tr id="pattern" style="display:none;margin:0;padding:0;border:0">
<td class="fit">
<img id="starSong" onclick="onStar(this.id.substring(8) - 1)" src="<spring:theme code="ratingOffImage"/>"
style="cursor:pointer" alt="" title=""></td>
<td class="fit">
<img id="removeSong" onclick="onRemove(this.id.substring(10) - 1)" src="<spring:theme code="removeImage"/>"
style="cursor:pointer" alt="<fmt:message key="playlist.remove"/>" title="<fmt:message key="playlist.remove"/>"></td>
<td class="fit"><input type="checkbox" class="checkbox" id="songIndex"></td>
<c:if test="${model.visibility.trackNumberVisible}">
<td class="fit rightalign"><span class="detail" id="trackNumber">1</span></td>
</c:if>
<td class="truncate">
<img id="currentImage" src="<spring:theme code="currentImage"/>" alt="" style="display:none;padding-right: 0.5em">
<c:choose>
<c:when test="${model.player.externalWithPlaylist}">
<span id="title" class="songTitle">Title</span>
</c:when>
<c:otherwise>
<span class="songTitle"><a id="titleUrl" href="javascript:void(0)">Title</a></span>
</c:otherwise>
</c:choose>
</td>
<c:if test="${model.visibility.albumVisible}">
<td class="truncate"><a id="albumUrl" target="main"><span id="album" class="detail">Album</span></a></td>
</c:if>
<c:if test="${model.visibility.artistVisible}">
<td class="truncate"><span id="artist" class="detail">Artist</span></td>
</c:if>
<c:if test="${model.visibility.genreVisible}">
<td class="truncate"><span id="genre" class="detail">Genre</span></td>
</c:if>
<c:if test="${model.visibility.yearVisible}">
<td class="fit rightalign"><span id="year" class="detail">Year</span></td>
</c:if>
<c:if test="${model.visibility.formatVisible}">
<td class="fit rightalign"><span id="format" class="detail">Format</span></td>
</c:if>
<c:if test="${model.visibility.fileSizeVisible}">
<td class="fit rightalign"><span id="fileSize" class="detail">Format</span></td>
</c:if>
<c:if test="${model.visibility.durationVisible}">
<td class="fit rightalign"><span id="duration" class="detail">Duration</span></td>
</c:if>
<c:if test="${model.visibility.bitRateVisible}">
<td class="fit rightalign"><span id="bitRate" class="detail">Bit Rate</span></td>
</c:if>
</tr>
</tbody>
</table>
<div style="height:3.2em"></div>
<div id="dialog-select-playlist" title="<fmt:message key="main.addtoplaylist.title"/>" style="display: none;">
<p><fmt:message key="main.addtoplaylist.text"/></p>
<div id="dialog-select-playlist-list"></div>
</div>
</body></html>
@@ -0,0 +1,286 @@
<script type="text/javascript">
(function () {
'use strict';
var CastPlayer = function () {
this.castSession = null;
this.mediaSession = null;
this.volume = 1.0;
this.initializeCastPlayer();
};
CastPlayer.prototype.initializeCastPlayer = function () {
if (!window.chrome) {
return;
}
if (!chrome.cast || !chrome.cast.isAvailable) {
setTimeout(this.initializeCastPlayer.bind(this), 1000);
return;
}
var applicationID = "4FBFE470";
var sessionRequest = new chrome.cast.SessionRequest(applicationID);
var apiConfig = new chrome.cast.ApiConfig(sessionRequest,
this.sessionListener.bind(this),
this.receiverListener.bind(this));
chrome.cast.initialize(apiConfig, this.onInitSuccess.bind(this), this.onError.bind(this));
};
/**
* session listener during initialization
*/
CastPlayer.prototype.sessionListener = function (s) {
this.log('New session ID:' + s.sessionId);
this.castSession = s;
this.setCastControlsVisible(true);
if (this.castSession.media.length > 0) {
this.log('Found ' + this.castSession.media.length + ' existing media sessions.');
this.onMediaDiscovered('onRequestSessionSuccess_', this.castSession.media[0]);
}
this.castSession.addMediaListener(this.onMediaDiscovered.bind(this, 'addMediaListener'));
this.castSession.addUpdateListener(this.sessionUpdateListener.bind(this));
this.syncControls();
};
/**
* receiver listener during initialization
*/
CastPlayer.prototype.receiverListener = function (e) {
if (e === 'available') {
this.log("receiver found");
$("#castOn").show();
$("#castOff").hide();
}
else {
this.log("receiver list empty");
$("#castOn").hide();
$("#castOff").hide();
}
};
/**
* session update listener
*/
CastPlayer.prototype.sessionUpdateListener = function (isAlive) {
var message = isAlive ? 'Session Updated' : 'Session Removed';
message += ': ' + this.castSession.sessionId;
this.log(message);
if (!isAlive) {
this.castSession = null;
this.setCastControlsVisible(false);
}
};
CastPlayer.prototype.onInitSuccess = function () {
this.log("init success");
};
CastPlayer.prototype.onError = function () {
this.log("error");
};
CastPlayer.prototype.setCastControlsVisible = function (visible) {
$("#flashPlayer").toggle(!visible);
$("#castPlayer").toggle(visible);
$("#castOff").toggle(visible);
$("#castOn").toggle(!visible);
};
/**
* launch app and request session
*/
CastPlayer.prototype.launchCastApp = function () {
<c:choose>
<c:when test="${model.licenseInfo.licenseOrTrialValid}">
this.log("launching app...");
chrome.cast.requestSession(this.onRequestSessionSuccess.bind(this), this.onLaunchError.bind(this));
</c:when>
<c:otherwise>
$().toastmessage('showNoticeToast', "<fmt:message key="videoPlayer.getpremium"/>");
</c:otherwise>
</c:choose>
};
/**
* Stops the running receiver application associated with the session.
*/
CastPlayer.prototype.stopCastApp = function () {
this.castSession.stop(this.onStopAppSuccess.bind(this, 'Session stopped'),
this.onError.bind(this));
};
/**
* Callback function for stop app success
*/
CastPlayer.prototype.onStopAppSuccess = function (message) {
console.log(message);
this.currentMediaSession = null;
this.syncControls();
};
/**
* callback on success for requestSession call
* @param {Object} s A non-null new session.
*/
CastPlayer.prototype.onRequestSessionSuccess = function (s) {
this.log("session success: " + s.sessionId);
this.castSession = s;
var position = -1;
if (jwplayer().getState() == "PLAYING") {
position = jwplayer().getPosition();
}
this.setCastControlsVisible(true);
this.castSession.addUpdateListener(this.sessionUpdateListener.bind(this));
this.syncControls();
// Continue song at same position?
if (position != -1) {
skip(getCurrentSongIndex(), position);
}
};
CastPlayer.prototype.onLaunchError = function () {
this.log("launch error");
};
CastPlayer.prototype.loadCastMedia = function (song, position) {
if (!this.castSession) {
this.log("no session");
return;
}
this.log("loading..." + song.remoteStreamUrl);
var mediaInfo = new chrome.cast.media.MediaInfo(song.remoteStreamUrl);
mediaInfo.contentType = song.contentType;
mediaInfo.streamType = chrome.cast.media.StreamType.BUFFERED;
mediaInfo.duration = song.duration;
mediaInfo.metadata = new chrome.cast.media.MusicTrackMediaMetadata();
mediaInfo.metadata.metadataType = chrome.cast.media.MetadataType.MUSIC_TRACK;
mediaInfo.metadata.songName = song.title;
mediaInfo.metadata.title = song.title;
mediaInfo.metadata.albumName = song.album;
mediaInfo.metadata.artist = song.artist;
mediaInfo.metadata.trackNumber = song.trackNumber;
mediaInfo.metadata.images = [new chrome.cast.Image(song.remoteCoverArtUrl + "&size=384")];
mediaInfo.metadata.releaseYear = song.year;
var request = new chrome.cast.media.LoadRequest(mediaInfo);
request.autoplay = true;
request.currentTime = position;
this.castSession.loadMedia(request,
this.onMediaDiscovered.bind(this, 'loadMedia'),
this.onMediaError.bind(this));
};
/**
* callback on success for loading media
*/
CastPlayer.prototype.onMediaDiscovered = function (how, ms) {
this.mediaSession = ms;
this.log("new media session ID:" + this.mediaSession.mediaSessionId + ' (' + how + ')');
this.log(ms);
this.mediaSession.addUpdateListener(this.onMediaStatusUpdate.bind(this));
};
/**
* callback on media loading error
* @param {Object} e A non-null media object
*/
CastPlayer.prototype.onMediaError = function (e) {
this.log("media error");
};
/**
* callback for media status event
*/
CastPlayer.prototype.onMediaStatusUpdate = function () {
this.log(this.mediaSession.playerState);
if (this.mediaSession.playerState === chrome.cast.media.PlayerState.IDLE && this.mediaSession.idleReason === "FINISHED") {
onNext(repeatEnabled);
}
this.syncControls();
};
CastPlayer.prototype.playCast = function () {
if (!this.mediaSession) {
return;
}
this.mediaSession.play(null, this.mediaCommandSuccessCallback.bind(this, "playing started for " + this.mediaSession.sessionId),
this.onError.bind(this));
$("#castPlay").hide();
$("#castPause").show();
};
CastPlayer.prototype.pauseCast = function () {
if (!this.mediaSession) {
return;
}
this.mediaSession.pause(null, this.mediaCommandSuccessCallback.bind(this, "paused " + this.mediaSession.sessionId),
this.onError.bind(this));
$("#castPlay").show();
$("#castPause").hide();
};
/**
* set receiver volume
* @param {Number} level A number for volume level
* @param {Boolean} mute A true/false for mute/unmute
*/
CastPlayer.prototype.setCastVolume = function (level, mute) {
if (!this.castSession)
return;
if (!mute) {
this.castSession.setReceiverVolumeLevel(level, this.mediaCommandSuccessCallback.bind(this, 'media set-volume done'),
this.onError.bind(this));
this.volume = level;
}
else {
this.castSession.setReceiverMuted(true, this.mediaCommandSuccessCallback.bind(this, 'media set-volume done'),
this.onError.bind(this));
}
$("#castMuteOn").toggle(!mute);
$("#castMuteOff").toggle(mute);
};
CastPlayer.prototype.castMuteOn = function () {
this.setCastVolume(this.volume, true);
};
CastPlayer.prototype.castMuteOff = function () {
this.setCastVolume(this.volume, false);
};
/**
* callback on success for media commands
* @param {string} info A message string
*/
CastPlayer.prototype.mediaCommandSuccessCallback = function (info) {
this.log(info);
};
CastPlayer.prototype.syncControls = function () {
if (this.castSession && this.castSession.receiver.volume) {
this.volume = this.castSession.receiver.volume.level;
var muted = this.castSession.receiver.volume.muted;
$("#castMuteOn").toggle(!muted);
$("#castMuteOff").toggle(muted);
document.getElementById("castVolume").value = this.volume * 100;
}
var playing = this.mediaSession && this.mediaSession.playerState === chrome.cast.media.PlayerState.PLAYING;
$("#castPause").toggle(playing);
$("#castPlay").toggle(!playing);
};
CastPlayer.prototype.log = function (message) {
console.log(message);
};
window.CastPlayer = CastPlayer;
})();
</script>
@@ -0,0 +1,162 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<%--@elvariable id="command" type="org.libresonic.player.command.PlayerSettingsCommand"--%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript" src="<c:url value="/script/scripts-2.0.js"/>"></script>
</head>
<body class="mainframe bgcolor1">
<script type="text/javascript" src="<c:url value="/script/wz_tooltip.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/tip_balloon.js"/>"></script>
<c:import url="settingsHeader.jsp">
<c:param name="cat" value="player"/>
<c:param name="toast" value="${command.reloadNeeded}"/>
<c:param name="restricted" value="${not command.admin}"/>
</c:import>
<fmt:message key="common.unknown" var="unknown"/>
<c:choose>
<c:when test="${empty command.players}">
<p><fmt:message key="playersettings.noplayers"/></p>
</c:when>
<c:otherwise>
<c:url value="playerSettings.view" var="deleteUrl">
<c:param name="delete" value="${command.playerId}"/>
</c:url>
<c:url value="playerSettings.view" var="cloneUrl">
<c:param name="clone" value="${command.playerId}"/>
</c:url>
<table class="indent">
<tr>
<td><b><fmt:message key="playersettings.title"/></b></td>
<td>
<select name="player" onchange="location='playerSettings.view?id=' + options[selectedIndex].value;">
<c:forEach items="${command.players}" var="player">
<option ${player.id eq command.playerId ? "selected" : ""}
value="${player.id}">${fn:escapeXml(player.description)}</option>
</c:forEach>
</select>
</td>
</tr>
<tr>
<td style="padding-right:1em"><div class="forward"><a href="${deleteUrl}"><fmt:message key="playersettings.forget"/></a></div></td>
<td><div class="forward"><a href="${cloneUrl}"><fmt:message key="playersettings.clone"/></a></div></td>
</tr>
</table>
<form:form commandName="command" method="post" action="playerSettings.view">
<form:hidden path="playerId"/>
<table class="ruleTable indent">
<c:forEach items="${command.technologyHolders}" var="technologyHolder">
<c:set var="technologyName">
<fmt:message key="playersettings.technology.${fn:toLowerCase(technologyHolder.name)}.title"/>
</c:set>
<tr>
<td class="ruleTableHeader">
<form:radiobutton id="radio-${technologyName}" path="technologyName" value="${technologyHolder.name}"/>
<b><label for="radio-${technologyName}">${technologyName}</label></b>
</td>
<td class="ruleTableCell" style="width:40em">
<fmt:message key="playersettings.technology.${fn:toLowerCase(technologyHolder.name)}.text"/>
</td>
</tr>
</c:forEach>
</table>
<table class="indent" style="border-spacing:3pt;">
<tr>
<td style="padding-right: 1.5em"><fmt:message key="playersettings.type"/></td>
<td>
<c:choose>
<c:when test="${empty command.type}">${unknown}</c:when>
<c:otherwise>${command.type}</c:otherwise>
</c:choose>
</td>
</tr>
<tr>
<td style="padding-right: 1.5em"><fmt:message key="playersettings.lastseen"/></td>
<td><fmt:formatDate value="${command.lastSeen}" type="both" dateStyle="long" timeStyle="medium"/></td>
</tr>
</table>
<table class="indent" style="border-spacing:3pt;">
<tr>
<td><fmt:message key="playersettings.name"/></td>
<td><form:input path="name" size="16"/></td>
<td colspan="2"><c:import url="helpToolTip.jsp"><c:param name="topic" value="playername"/></c:import></td>
</tr>
<tr>
<td><fmt:message key="playersettings.maxbitrate"/></td>
<td>
<form:select path="transcodeSchemeName" cssStyle="width:8em">
<c:forEach items="${command.transcodeSchemeHolders}" var="transcodeSchemeHolder">
<form:option value="${transcodeSchemeHolder.name}" label="${transcodeSchemeHolder.description}"/>
</c:forEach>
</form:select>
</td>
<td>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="transcode"/></c:import>
</td>
<td class="warning">
<c:if test="${not command.transcodingSupported}">
<fmt:message key="playersettings.notranscoder"/>
</c:if>
</td>
</tr>
</table>
<table class="indent" style="border-spacing:3pt">
<tr>
<td>
<form:checkbox path="dynamicIp" id="dynamicIp" cssClass="checkbox"/>
<label for="dynamicIp"><fmt:message key="playersettings.dynamicip"/></label>
</td>
<td><c:import url="helpToolTip.jsp"><c:param name="topic" value="dynamicip"/></c:import></td>
</tr>
<tr>
<td>
<form:checkbox path="autoControlEnabled" id="autoControlEnabled" cssClass="checkbox"/>
<label for="autoControlEnabled"><fmt:message key="playersettings.autocontrol"/></label>
</td>
<td><c:import url="helpToolTip.jsp"><c:param name="topic" value="autocontrol"/></c:import></td>
</tr>
</table>
<c:if test="${not empty command.allTranscodings}">
<table class="indent">
<tr><td><b><fmt:message key="playersettings.transcodings"/></b></td></tr>
<c:forEach items="${command.allTranscodings}" var="transcoding" varStatus="loopStatus">
<c:if test="${loopStatus.count % 3 == 1}"><tr></c:if>
<td style="padding-right:2em">
<form:checkbox path="activeTranscodingIds" id="transcoding${transcoding.id}" value="${transcoding.id}" cssClass="checkbox"/>
<label for="transcoding${transcoding.id}">${transcoding.name}</label>
</td>
<c:if test="${loopStatus.count % 3 == 0 or loopStatus.count eq fn:length(command.allTranscodings)}"></tr></c:if>
</c:forEach>
</table>
</c:if>
<input type="submit" value="<fmt:message key="common.save"/>" style="margin-top:1em;margin-right:0.3em">
<input type="button" value="<fmt:message key="common.cancel"/>" style="margin-top:1em" onclick="location.href='nowPlaying.view'">
</form:form>
</c:otherwise>
</c:choose>
<c:if test="${command.reloadNeeded}">
<script language="javascript" type="text/javascript">parent.frames.playQueue.location.href="playQueue.view?"</script>
</c:if>
</body></html>
@@ -0,0 +1,276 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript" src="<c:url value='/dwr/util.js'/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/interface/playlistService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/interface/starService.js"/>"></script>
<script type="text/javascript" language="javascript">
var playlist;
var songs;
function init() {
dwr.engine.setErrorHandler(null);
$("#dialog-edit").dialog({resizable: true, width:400, autoOpen: false,
buttons: {
"<fmt:message key="common.save"/>": function() {
$(this).dialog("close");
var name = $("#newName").val();
var comment = $("#newComment").val();
var shared = $("#newShared").is(":checked");
$("#name").text(name);
$("#comment").text(comment);
playlistService.updatePlaylist(playlist.id, name, comment, shared, function (playlistInfo){playlistCallback(playlistInfo); top.left.updatePlaylists()});
},
"<fmt:message key="common.cancel"/>": function() {
$(this).dialog("close");
}
}});
$("#dialog-delete").dialog({resizable: false, height: 170, autoOpen: false,
buttons: {
"<fmt:message key="common.delete"/>": function() {
$(this).dialog("close");
playlistService.deletePlaylist(playlist.id, function (){top.left.updatePlaylists(); location = "playlists.view";});
},
"<fmt:message key="common.cancel"/>": function() {
$(this).dialog("close");
}
}});
$("#playlistBody").sortable({
stop: function(event, ui) {
var indexes = [];
$("#playlistBody").children().each(function() {
var id = $(this).attr("id").replace("pattern", "");
if (id.length > 0) {
indexes.push(parseInt(id) - 1);
}
});
onRearrange(indexes);
},
cursor: "move",
axis: "y",
containment: "parent",
helper: function(e, tr) {
var originals = tr.children();
var trclone = tr.clone();
trclone.children().each(function(index) {
// Set cloned cell sizes to match the original sizes
$(this).width(originals.eq(index).width());
$(this).css("maxWidth", originals.eq(index).width());
$(this).css("border-top", "1px solid black");
$(this).css("border-bottom", "1px solid black");
});
return trclone;
}
});
getPlaylist();
}
function getPlaylist() {
playlistService.getPlaylist(${model.playlist.id}, playlistCallback);
}
function playlistCallback(playlistInfo) {
this.playlist = playlistInfo.playlist;
this.songs = playlistInfo.entries;
if (songs.length == 0) {
$("#empty").show();
} else {
$("#empty").hide();
}
$("#songCount").html(playlist.fileCount);
$("#duration").html(playlist.durationAsString);
if (playlist.shared) {
$("#shared").html("<fmt:message key="playlist2.shared"/>");
} else {
$("#shared").html("<fmt:message key="playlist2.notshared"/>");
}
// Delete all the rows except for the "pattern" row
dwr.util.removeAllRows("playlistBody", { filter:function(tr) {
return (tr.id != "pattern");
}});
// Create a new set cloned from the pattern row
for (var i = 0; i < songs.length; i++) {
var song = songs[i];
var id = i + 1;
dwr.util.cloneNode("pattern", { idSuffix:id });
if (song.starred) {
$("#starSong" + id).attr("src", "<spring:theme code='ratingOnImage'/>");
} else {
$("#starSong" + id).attr("src", "<spring:theme code='ratingOffImage'/>");
}
if (!song.present) {
$("#missing" + id).show();
}
$("#index" + id).html(id);
$("#title" + id).html(song.title);
$("#title" + id).attr("title", song.title);
$("#album" + id).html(song.album);
$("#album" + id).attr("title", song.album);
$("#albumUrl" + id).attr("href", "main.view?id=" + song.id);
$("#artist" + id).html(song.artist);
$("#artist" + id).attr("title", song.artist);
$("#songDuration" + id).html(song.durationAsString);
// Note: show() method causes page to scroll to top.
$("#pattern" + id).css("display", "table-row");
}
}
function onPlay(index) {
top.playQueue.onPlayPlaylist(playlist.id, index);
}
function onPlayAll() {
top.playQueue.onPlayPlaylist(playlist.id);
}
function onAdd(index) {
top.playQueue.onAdd(songs[index].id);
$().toastmessage('showSuccessToast', '<fmt:message key="main.addlast.toast"/>')
}
function onAddNext(index) {
top.playQueue.onAddNext(songs[index].id);
$().toastmessage('showSuccessToast', '<fmt:message key="main.addnext.toast"/>')
}
function onStar(index) {
playlistService.toggleStar(playlist.id, index, playlistCallback);
}
function onRemove(index) {
playlistService.remove(playlist.id, index, function (playlistInfo){playlistCallback(playlistInfo); top.left.updatePlaylists()});
}
function onRearrange(indexes) {
playlistService.rearrange(playlist.id, indexes, playlistCallback);
}
function onEditPlaylist() {
$("#dialog-edit").dialog("open");
}
function onDeletePlaylist() {
$("#dialog-delete").dialog("open");
}
</script>
<style type="text/css">
.playlist-missing {
color: red;
border: 1px solid red;
display: none;
font-size: 90%;
padding-left: 5px;
padding-right: 5px;
margin-right: 5px;
}
</style>
</head>
<body class="mainframe bgcolor1" onload="init()">
<div style="float:left;margin-right:1.5em;margin-bottom:1.5em">
<c:import url="coverArt.jsp">
<c:param name="playlistId" value="${model.playlist.id}"/>
<c:param name="coverArtSize" value="200"/>
</c:import>
</div>
<h1><a href="playlists.view"><fmt:message key="left.playlists"/></a> &raquo; <span id="name">${fn:escapeXml(model.playlist.name)}</span></h1>
<h2>
<span class="header"><a href="javascript:void(0)" onclick="onPlayAll();"><fmt:message key="common.play"/></a></span>
<c:if test="${model.user.downloadRole}">
<c:url value="download.view" var="downloadUrl"><c:param name="playlist" value="${model.playlist.id}"/></c:url>
| <span class="header"><a href="${downloadUrl}"><fmt:message key="common.download"/></a></span>
</c:if>
<c:if test="${model.user.shareRole}">
<c:url value="createShare.view" var="shareUrl"><c:param name="playlist" value="${model.playlist.id}"/></c:url>
| <span class="header"><a href="${shareUrl}"><fmt:message key="share.title"/></a></span>
</c:if>
<c:if test="${model.editAllowed}">
| <span class="header"><a href="javascript:void(0)" onclick="onEditPlaylist();"><fmt:message key="common.edit"/></a></span>
| <span class="header"><a href="javascript:void(0)" onclick="onDeletePlaylist();"><fmt:message key="common.delete"/></a></span>
</c:if>
<c:url value="exportPlaylist.view" var="exportUrl"><c:param name="id" value="${model.playlist.id}"/></c:url>
| <span class="header"><a href="${exportUrl}"><fmt:message key="playlist2.export"/></a></span>
</h2>
<div id="comment" class="detail" style="padding-top:0.2em">${fn:escapeXml(model.playlist.comment)}</div>
<div class="detail" style="padding-top:0.2em">
<span id="songCount"></span> <fmt:message key="playlist2.songs"/> &ndash; <span id="duration"></span>
</div>
<div class="detail" style="padding-top:0.2em">
<fmt:message key="playlist2.created" var="created">
<fmt:param>${model.playlist.username}</fmt:param>
<fmt:param><fmt:formatDate type="date" dateStyle="long" value="${model.playlist.created}"/></fmt:param>
</fmt:message>
${fn:escapeXml(created)}.
</div>
<div class="detail" style="padding-top:0.2em">
<span id="shared"></span>.
</div>
<div style="height:0.7em;clear:both"></div>
<p id="empty" style="display: none;"><em><fmt:message key="playlist2.empty"/></em></p>
<table class="music" style="cursor:pointer">
<tbody id="playlistBody">
<tr id="pattern" style="display:none;margin:0;padding:0;border:0">
<td class="fit">
<img id="starSong" onclick="onStar(this.id.substring(8) - 1)" src="<spring:theme code="ratingOffImage"/>"
style="cursor:pointer" alt="" title=""></td>
<td class="fit">
<img id="play" src="<spring:theme code="playImage"/>" alt="<fmt:message key="common.play"/>" title="<fmt:message key="common.play"/>"
style="padding-right:0.1em;cursor:pointer" onclick="onPlay(this.id.substring(4) - 1)"></td>
<td class="fit">
<img id="add" src="<spring:theme code="addImage"/>" alt="<fmt:message key="common.add"/>" title="<fmt:message key="common.add"/>"
style="padding-right:0.1em;cursor:pointer" onclick="onAdd(this.id.substring(3) - 1)"></td>
<td class="fit" style="padding-right:30px">
<img id="addNext" src="<spring:theme code="addNextImage"/>" alt="<fmt:message key="main.addnext"/>" title="<fmt:message key="main.addnext"/>"
style="padding-right:0.1em;cursor:pointer" onclick="onAddNext(this.id.substring(7) - 1)"></td>
<td class="fit rightalign"><span id="index">1</span></td>
<td class="fit"><span id="missing" class="playlist-missing"><fmt:message key="playlist.missing"/></span></td>
<td class="truncate"><span id="title" class="songTitle">Title</span></td>
<td class="truncate"><a id="albumUrl" target="main"><span id="album" class="detail">Album</span></a></td>
<td class="truncate"><span id="artist" class="detail">Artist</span></td>
<td class="fit rightalign"><span id="songDuration" class="detail">Duration</span></td>
<c:if test="${model.editAllowed}">
<td class="fit">
<img id="removeSong" onclick="onRemove(this.id.substring(10) - 1)" src="<spring:theme code="removeImage"/>"
style="cursor:pointer" alt="<fmt:message key="playlist.remove"/>" title="<fmt:message key="playlist.remove"/>"></td>
</c:if>
</tr>
</tbody>
</table>
<div id="dialog-delete" title="<fmt:message key="common.confirm"/>" style="display: none;">
<p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span>
<fmt:message key="playlist2.confirmdelete"/></p>
</div>
<div id="dialog-edit" title="<fmt:message key="common.edit"/>" style="display: none;">
<form>
<label for="newName" style="display:block;"><fmt:message key="playlist2.name"/></label>
<input type="text" name="newName" id="newName" value="${fn:escapeXml(model.playlist.name)}" class="ui-widget-content"
style="display:block;width:95%;"/>
<label for="newComment" style="display:block;margin-top:1em"><fmt:message key="playlist2.comment"/></label>
<input type="text" name="newComment" id="newComment" value="${fn:escapeXml(model.playlist.comment)}" class="ui-widget-content"
style="display:block;width:95%;"/>
<input type="checkbox" name="newShared" id="newShared" ${model.playlist.shared ? "checked='checked'" : ""} style="margin-top:1.5em" class="ui-widget-content"/>
<label for="newShared"><fmt:message key="playlist2.public"/></label>
</form>
</div>
</body></html>
@@ -0,0 +1,38 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<%--@elvariable id="model" type="java.util.Map"--%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
</head><body class="mainframe bgcolor1">
<h1 style="padding-bottom: 1em">
<img src="<spring:theme code="playlistImage"/>" alt="">
<span style="vertical-align: middle"><fmt:message key="left.playlists"/></span>
</h1>
<c:if test="${empty model.playlists}">
<p><em><fmt:message key="playlist2.noplaylists"/></em></p>
</c:if>
<c:forEach items="${model.playlists}" var="playlist" varStatus="loopStatus">
<c:set var="caption2">
${playlist.fileCount} <fmt:message key="playlist2.songs"/> &ndash; ${playlist.durationAsString}
</c:set>
<div class="albumThumb">
<c:import url="coverArt.jsp">
<c:param name="playlistId" value="${playlist.id}"/>
<c:param name="coverArtSize" value="200"/>
<c:param name="caption1" value="${fn:escapeXml(playlist.name)}"/>
<c:param name="caption2" value="${caption2}"/>
<c:param name="captionCount" value="2"/>
<c:param name="showLink" value="true"/>
<c:param name="appearAfter" value="${loopStatus.count * 30}"/>
</c:import>
</div>
</c:forEach>
</body>
</html>
@@ -0,0 +1,26 @@
<%@ include file="include.jsp" %>
<%@ page language="java" contentType="text/xml; charset=utf-8" pageEncoding="iso-8859-1"%>
<rss version="2.0">
<channel>
<title>Libresonic Podcast</title>
<link>${model.url}</link>
<description>Libresonic Podcast</description>
<language>en-us</language>
<image>
<url>http://www.libresonic.org/pages/inc/img/libresonic_logo.png</url>
<title>Libresonic Podcast</title>
</image>
<c:forEach var="podcast" items="${model.podcasts}">
<item>
<title>${fn:escapeXml(podcast.name)}</title>
<link>${model.url}</link>
<description>Libresonic playlist "${fn:escapeXml(podcast.name)}"</description>
<pubDate>${podcast.publishDate}</pubDate>
<enclosure url="${podcast.enclosureUrl}" length="${podcast.length}" type="${podcast.type}"/>
</item>
</c:forEach>
</channel>
</rss>
@@ -0,0 +1,186 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<%--
~ This file is part of Libresonic.
~
~ Libresonic is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ Libresonic is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
~
~ Copyright 2015 (C) Sindre Mehus
--%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript" language="javascript">
function init() {
$("#dialog-delete").dialog({resizable: false, height: 170, autoOpen: false,
buttons: {
"<fmt:message key="common.delete"/>": function() {
location.href = "podcastReceiverAdmin.view?channelId=${model.channel.id}" +
"&deleteChannel=${model.channel.id}";
},
"<fmt:message key="common.cancel"/>": function() {
$(this).dialog("close");
}
}});
}
function downloadSelected() {
location.href = "podcastReceiverAdmin.view?channelId=${model.channel.id}" +
"&downloadEpisode=" + getSelectedEpisodes();
}
function deleteChannel() {
$("#dialog-delete").dialog("open");
}
function deleteSelected() {
location.href = "podcastReceiverAdmin.view?channelId=${model.channel.id}" +
"&deleteEpisode=" + getSelectedEpisodes();
}
function refreshChannels() {
location.href = "podcastReceiverAdmin.view?refresh&channelId=${model.channel.id}";
}
function refreshPage() {
location.href = "podcastChannel.view?id=${model.channel.id}";
}
function getSelectedEpisodes() {
var result = "";
for (var i = 0; i < ${fn:length(model.episodes)}; i++) {
var checkbox = $("#episode" + i);
if (checkbox.is(":checked")) {
result += (checkbox.val() + " ");
}
}
return result;
}
</script>
</head>
<body class="mainframe bgcolor1" onload="init()">
<div style="float:left;margin-right:1.5em;margin-bottom:1.5em">
<c:import url="coverArt.jsp">
<c:param name="podcastChannelId" value="${model.channel.id}"/>
<c:param name="coverArtSize" value="200"/>
</c:import>
</div>
<h1 id="name"><a href="podcastChannels.view"><fmt:message key="podcastreceiver.title"/></a> &raquo; ${fn:escapeXml(model.channel.title)}</h1>
<h2>
<span class="header"><a href="javascript:top.playQueue.onPlayPodcastChannel(${model.channel.id})"><fmt:message key="common.play"/></a></span>
<c:if test="${model.user.podcastRole}">
| <span class="header"><a href="javascript:deleteChannel()"><fmt:message key="common.delete"/></a></span>
| <span class="header"><a href="javascript:refreshChannels()"><fmt:message key="podcastreceiver.check"/></a></span>
</c:if>
</h2>
<div class="detail" style="padding-top:0.2em;white-space:normal;width:80%">${fn:escapeXml(model.channel.description)}</div>
<div class="detail" style="padding-top:1.0em">
<fmt:message key="podcastreceiver.episodes"><fmt:param value="${fn:length(model.episodes)}"/></fmt:message> &ndash;
<fmt:message key="podcastreceiver.status.${fn:toLowerCase(model.channel.status)}"/>
<c:if test="${model.channel.status eq 'ERROR'}">
<span class="warning">${model.channel.errorMessage}</span>
</c:if>
</div>
<div style="height:0.7em;clear:both"></div>
<table class="music">
<c:forEach items="${model.episodes}" var="episode" varStatus="i">
<tr>
<td class="fit"><input type="checkbox" class="checkbox" id="episode${i.index}" value="${episode.id}"/></td>
<c:choose>
<c:when test="${empty episode.mediaFileId or episode.status ne 'COMPLETED'}">
<td colspan="4"></td>
</c:when>
<c:otherwise>
<c:import url="playButtons.jsp">
<c:param name="id" value="${episode.mediaFileId}"/>
<c:param name="playEnabled" value="${model.user.streamRole and not model.partyMode}"/>
<c:param name="addEnabled" value="${model.user.streamRole and not model.partyMode}"/>
<c:param name="asTable" value="true"/>
<c:param name="onPlay" value="top.playQueue.onPlayPodcastEpisode(${episode.id})"/>
</c:import>
</c:otherwise>
</c:choose>
<td class="truncate">
<span title="${episode.title}" class="songTitle">${episode.title}</span>
</td>
<td class="fit">
<span class="detail">${episode.duration}</span>
</td>
<td class="fit">
<span class="detail"><fmt:formatDate value="${episode.publishDate}" dateStyle="medium"/></span>
</td>
<td class="fit" style="text-align:center">
<span class="detail">
<c:choose>
<c:when test="${episode.status eq 'DOWNLOADING'}">
<fmt:formatNumber type="percent" value="${episode.completionRate}"/>
</c:when>
<c:otherwise>
<fmt:message key="podcastreceiver.status.${fn:toLowerCase(episode.status)}"/>
</c:otherwise>
</c:choose>
</span>
</td>
<td class="truncate">
<c:choose>
<c:when test="${episode.status eq 'ERROR'}">
<span class="detail warning" title="${episode.errorMessage}">${episode.errorMessage}</span>
</c:when>
<c:otherwise>
<span class="detail" title="${episode.description}">${episode.description}</span>
</c:otherwise>
</c:choose>
</td>
</tr>
</c:forEach>
</table>
<table style="padding-top:1em"><tr>
<c:if test="${model.user.podcastRole}">
<td style="padding-right:2em"><div class="forward"><a href="javascript:downloadSelected()"><fmt:message key="podcastreceiver.downloadselected"/></a></div></td>
<td style="padding-right:2em"><div class="forward"><a href="javascript:deleteSelected()"><fmt:message key="podcastreceiver.deleteselected"/></a></div></td>
</c:if>
<td style="padding-right:2em"><div class="forward"><a href="javascript:refreshPage()"><fmt:message key="podcastreceiver.refresh"/></a></div></td>
<c:if test="${model.user.adminRole}">
<td style="padding-right:2em"><div class="forward"><a href="podcastSettings.view?"><fmt:message key="podcastreceiver.settings"/></a></div></td>
</c:if>
</tr></table>
<div id="dialog-delete" title="<fmt:message key="common.confirm"/>" style="display: none;">
<p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span>
<fmt:message key="podcastreceiver.confirmdelete"/></p>
</div>
</body></html>
@@ -0,0 +1,117 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<%--
~ This file is part of Libresonic.
~
~ Libresonic is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ Libresonic is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
~
~ Copyright 2015 (C) Sindre Mehus
--%>
<%--@elvariable id="model" type="java.util.Map"--%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
</head><body class="mainframe bgcolor1">
<h1 style="padding-bottom: 1em">
<img src="<spring:theme code="podcastLargeImage"/>" alt="">
<span style="vertical-align: middle"><fmt:message key="podcastreceiver.title"/></span>
</h1>
<c:if test="${empty model.channels}">
<p><em><fmt:message key="podcastreceiver.empty"/></em></p>
</c:if>
<c:forEach items="${model.channels}" var="channel" varStatus="loopStatus">
<c:set var="caption2">
<fmt:message key="podcastreceiver.episodes"><fmt:param value="${fn:length(channel.value)}"/></fmt:message>
</c:set>
<div class="albumThumb">
<c:import url="coverArt.jsp">
<c:param name="podcastChannelId" value="${channel.key.id}"/>
<c:param name="coverArtSize" value="200"/>
<c:param name="caption1" value="${fn:escapeXml(channel.key.title)}"/>
<c:param name="caption2" value="${caption2}"/>
<c:param name="captionCount" value="2"/>
<c:param name="showLink" value="true"/>
<c:param name="appearAfter" value="${loopStatus.count * 30}"/>
</c:import>
</div>
</c:forEach>
<c:if test="${not empty model.newestEpisodes}">
<h2 style="margin-top:1em"><fmt:message key="podcastreceiver.newestepisodes"/></h2>
<table class="music indent">
<c:forEach items="${model.newestEpisodes}" var="episode" varStatus="i">
<tr>
<c:import url="playButtons.jsp">
<c:param name="id" value="${episode.mediaFileId}"/>
<c:param name="podcastEpisodeId" value="${episode.id}"/>
<c:param name="playEnabled" value="${model.user.streamRole and not model.partyMode}"/>
<c:param name="addEnabled" value="${model.user.streamRole and not model.partyMode}"/>
<c:param name="asTable" value="true"/>
<c:param name="onPlay" value="top.playQueue.onPlayNewestPodcastEpisode(${i.index})"/>
</c:import>
<c:set var="channelTitle" value="${model.channelMap[episode.channelId].title}"/>
<td class="truncate">
<span title="${episode.title}" class="songTitle">${episode.title}</span>
</td>
<td class="truncate">
<a href="podcastChannel.view?id=${episode.channelId}"><span class="detail" title="${channelTitle}">${channelTitle}</span></a>
</td>
<td class="fit">
<span class="detail">${episode.duration}</span>
</td>
<td class="fit">
<span class="detail"><fmt:formatDate value="${episode.publishDate}" dateStyle="medium"/></span>
</td>
</tr>
</c:forEach>
</table>
</c:if>
<table style="padding-top:1em"><tr>
<c:if test="${model.user.podcastRole}">
<td style="padding-right:2em"><div class="forward"><a href="podcastReceiverAdmin.view?refresh"><fmt:message key="podcastreceiver.check"/></a></div></td>
</c:if>
<c:if test="${model.user.adminRole}">
<td style="padding-right:2em"><div class="forward"><a href="podcastSettings.view?"><fmt:message key="podcastreceiver.settings"/></a></div></td>
</c:if>
</tr></table>
<c:if test="${model.user.podcastRole}">
<form method="post" action="podcastReceiverAdmin.view?">
<table>
<tr>
<td><fmt:message key="podcastreceiver.subscribe"/></td>
<td><input type="text" name="add" value="http://" style="width:30em" onclick="select()"/></td>
<td><input type="submit" value="<fmt:message key="common.ok"/>"/></td>
</tr>
</table>
</form>
</c:if>
<c:set var="licenseInfo" value="${model.licenseInfo}"/>
<%@ include file="licenseNotice.jsp" %>
</body>
</html>
@@ -0,0 +1,91 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<%--@elvariable id="command" type="org.libresonic.player.command.PodcastSettingsCommand"--%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
</head>
<body class="mainframe bgcolor1">
<c:import url="settingsHeader.jsp">
<c:param name="cat" value="podcast"/>
<c:param name="toast" value="${command.toast}"/>
</c:import>
<form:form commandName="command" action="podcastSettings.view" method="post">
<table class="indent">
<tr>
<td><fmt:message key="podcastsettings.update"/></td>
<td>
<form:select path="interval" cssStyle="width:20em">
<fmt:message key="podcastsettings.interval.manually" var="never"/>
<fmt:message key="podcastsettings.interval.hourly" var="hourly"/>
<fmt:message key="podcastsettings.interval.daily" var="daily"/>
<fmt:message key="podcastsettings.interval.weekly" var="weekly"/>
<form:option value="-1" label="${never}"/>
<form:option value="1" label="${hourly}"/>
<form:option value="24" label="${daily}"/>
<form:option value="168" label="${weekly}"/>
</form:select>
</td>
</tr>
<tr>
<td><fmt:message key="podcastsettings.keep"/></td>
<td>
<form:select path="episodeRetentionCount" cssStyle="width:20em">
<fmt:message key="podcastsettings.keep.all" var="all"/>
<fmt:message key="podcastsettings.keep.one" var="one"/>
<form:option value="-1" label="${all}"/>
<form:option value="1" label="${one}"/>
<c:forTokens items="2 3 4 5 10 20 30 50" delims=" " var="count">
<fmt:message key="podcastsettings.keep.many" var="many"><fmt:param value="${count}"/></fmt:message>
<form:option value="${count}" label="${many}"/>
</c:forTokens>
</form:select>
</td>
</tr>
<tr>
<td><fmt:message key="podcastsettings.download"/></td>
<td>
<form:select path="episodeDownloadCount" cssStyle="width:20em">
<fmt:message key="podcastsettings.download.all" var="all"/>
<fmt:message key="podcastsettings.download.one" var="one"/>
<fmt:message key="podcastsettings.download.none" var="none"/>
<form:option value="-1" label="${all}"/>
<form:option value="1" label="${one}"/>
<c:forTokens items="2 3 4 5 10" delims=" " var="count">
<fmt:message key="podcastsettings.download.many" var="many"><fmt:param value="${count}"/></fmt:message>
<form:option value="${count}" label="${many}"/>
</c:forTokens>
<form:option value="0" label="${none}"/>
</form:select>
</td>
</tr>
<tr>
<td><fmt:message key="podcastsettings.folder"/></td>
<td><form:input path="folder" cssStyle="width:20em"/></td>
</tr>
<tr>
<td style="padding-top:1.5em" colspan="2">
<input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
<input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
</td>
</tr>
</table>
</form:form>
</body></html>
@@ -0,0 +1,93 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<%--@elvariable id="command" type="org.libresonic.player.command.PremiumSettingsCommand"--%>
<html>
<head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
</head>
<body class="mainframe bgcolor1">
<c:import url="settingsHeader.jsp">
<c:param name="cat" value="premium"/>
<c:param name="restricted" value="${not command.user.adminRole}"/>
<c:param name="toast" value="${command.toast}"/>
</c:import>
<c:if test="${not empty command.path}">
<sub:url value="main.view" var="backUrl">
<sub:param name="path" value="${command.path}"/>
</sub:url>
<div class="back"><a href="${backUrl}">
<fmt:message key="common.back"/>
</a></div>
<br/>
</c:if>
<div style="width:50em; max-width:50em">
<fmt:message key="premium.text"/>
<fmt:formatDate value="${command.licenseInfo.licenseExpires}" dateStyle="long" var="expirationDate"/>
<c:if test="${command.licenseInfo.licenseValid}">
<c:choose>
<c:when test="${empty command.licenseInfo.licenseExpires}">
<p><b><fmt:message key="premium.licensed"/></b></p>
</c:when>
<c:otherwise>
<p><b><fmt:message key="premium.licensedexpires"><fmt:param value="${expirationDate}"/></fmt:message></b></p>
</c:otherwise>
</c:choose>
<c:if test="${not command.forceChange and not command.submissionError}">
<p>
<fmt:message key="premium.licensedto"><fmt:param value="${command.licenseInfo.licenseEmail}"/></fmt:message>
</p>
<c:if test="${command.user.adminRole}">
<div class="forward"><a href="premiumSettings.view?change"><fmt:message key="premium.forcechange"/></a></div>
</c:if>
</c:if>
</c:if>
<c:if test="${not command.licenseInfo.licenseValid}">
<c:if test="${not empty command.licenseInfo.licenseExpires}">
<p><b><fmt:message key="premium.licensedexpired"><fmt:param value="${expirationDate}"/></fmt:message></b></p>
</c:if>
<p class="forward" style="font-size:1.2em;margin-left: 1em"><b><a href="http://libresonic.org/pages/premium.jsp" target="_blank">
<fmt:message key="premium.getpremium"/>
<c:if test="${command.licenseInfo.trialDaysLeft gt 0}">
&ndash; <fmt:message key="top.trialdaysleft"><fmt:param value="${command.licenseInfo.trialDaysLeft}"/></fmt:message>
</c:if>
</a></b></p>
<p><fmt:message key="premium.register"/></p>
</c:if>
<c:if test="${not command.licenseInfo.licenseValid or command.forceChange or command.submissionError}">
<form:form commandName="command" method="post" action="premiumSettings.view">
<form:hidden path="path"/>
<table>
<tr>
<td><fmt:message key="premium.register.email"/></td>
<td>
<form:input path="licenseInfo.licenseEmail" size="60"/>
</td>
</tr>
<tr>
<td><fmt:message key="premium.register.license"/></td>
<td>
<form:input path="licenseCode" size="60"/>
</td>
<td><input type="submit" value="<fmt:message key="common.ok"/>"/></td>
</tr>
<tr>
<td/>
<td class="warning"><form:errors path="licenseCode"/></td>
</tr>
</table>
</form:form>
<p><fmt:message key="premium.resend"/></p>
</c:if>
</div>
</body>
</html>
@@ -0,0 +1,51 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<%@ include file="include.jsp" %>
<%--
Creates HTML for displaying the rating stars.
PARAMETERS
id: Album ID. May be null if readonly.
readonly: Whether rating can be changed.
rating: The rating, an integer from 0 (no rating), through 10 (lowest rating), to 50 (highest rating).
--%>
<c:forEach var="i" begin="1" end="5">
<sub:url value="setRating.view" var="ratingUrl">
<sub:param name="id" value="${param.id}"/>
<sub:param name="action" value="rating"/>
<sub:param name="rating" value="${i}"/>
</sub:url>
<c:choose>
<c:when test="${param.rating ge i * 10}">
<spring:theme code="ratingOnImage" var="imageUrl"/>
</c:when>
<c:when test="${param.rating ge i*10 - 7 and param.rating le i*10 - 3}">
<spring:theme code="ratingHalfImage" var="imageUrl"/>
</c:when>
<c:otherwise>
<spring:theme code="ratingOffImage" var="imageUrl"/>
</c:otherwise>
</c:choose>
<c:choose>
<c:when test="${param.readonly}">
<img src="${imageUrl}" style="margin-right:-3px" alt="" title="<fmt:message key="rating.rating"/> ${param.rating/10}">
</c:when>
<c:otherwise>
<a href="${ratingUrl}"><img src="${imageUrl}" style="margin-right:-3px" alt="" title="<fmt:message key="rating.rating"/> ${i}"></a>
</c:otherwise>
</c:choose>
</c:forEach>
<sub:url value="setRating.view" var="clearRatingUrl">
<sub:param name="id" value="${param.id}"/>
<sub:param name="action" value="rating"/>
<sub:param name="rating" value="0"/>
</sub:url>
<c:if test="${not param.readonly}">
&nbsp;| <a href="${clearRatingUrl}"><img src="<spring:theme code="clearRatingImage"/>" alt="" title="<fmt:message key="rating.clearrating"/>" style="margin-right:5px"></a>
</c:if>
@@ -0,0 +1,43 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<%@ include file="head.jsp" %>
</head>
<body class="mainframe bgcolor1" onload="document.getElementById('usernameOrEmail').focus()">
<form action="recover.view" method="POST">
<div class="bgcolor2 shadow" style="padding:20px 50px 20px 50px; margin-top:100px;margin-left:50px;margin-right:50px">
<div style="margin-left: auto; margin-right: auto; width: 45em">
<h1><fmt:message key="recover.title"/></h1>
<p style="padding-top: 1em; padding-bottom: 0.5em"><fmt:message key="recover.text"/></p>
<c:if test="${empty model.sentTo}">
<input type="text" id="usernameOrEmail" name="usernameOrEmail" style="width:18em;margin-right: 1em">
<input name="submit" type="submit" value="<fmt:message key="recover.send"/>">
</c:if>
<c:if test="${not empty model.captcha}">
<p style="padding-top: 1em">
<c:out value="${model.captcha}" escapeXml="false"/>
</p>
</c:if>
<c:if test="${not empty model.sentTo}">
<p style="padding-top: 1em"><fmt:message key="recover.success"><fmt:param value="${model.sentTo}"/></fmt:message></p>
</c:if>
<c:if test="${not empty model.error}">
<p style="padding-top: 1em" class="warning"><fmt:message key="${model.error}"/></p>
</c:if>
<div class="back" style="margin-top: 1.5em"><a href="login.view"><fmt:message key="common.back"/></a></div>
</div>
</div>
</form>
</body>
</html>
@@ -0,0 +1,11 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<html><head>
<%@ include file="head.jsp" %>
</head><body>
<c:forEach items="${model.reloadFrames}" var="reloadFrame">
<script language="javascript" type="text/javascript">parent.frames.${reloadFrame.frame}.location.href="${reloadFrame.view}"</script>
</c:forEach>
</body></html>
@@ -0,0 +1,141 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<html>
<head>
<%@ include file="../include.jsp" %>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="../<spring:theme code="styleSheet"/>" type="text/css">
<c:url value="/rest/stream.view" var="streamUrl">
<c:param name="c" value="${model.c}"/>
<c:param name="v" value="${model.v}"/>
<c:param name="id" value="${model.id}"/>
</c:url>
<script type="text/javascript" src="<c:url value="/script/swfobject.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/prototype.js"/>"></script>
<script type="text/javascript" language="javascript">
var player;
var position;
var maxBitRate = ${model.maxBitRate};
var timeOffset = ${model.timeOffset};
function init() {
var flashvars = {
id:"player1",
skin:"<c:url value="/flash/whotube.zip"/>",
screencolor:"000000",
autostart:false,
bufferlength:4,
backcolor:"<spring:theme code="backgroundColor"/>",
frontcolor:"<spring:theme code="textColor"/>",
provider:"video"
};
var params = {
allowfullscreen:"true",
allowscriptaccess:"always"
};
var attributes = {
id:"player1",
name:"player1"
};
swfobject.embedSWF("<c:url value="/flash/jw-player-5.10.swf"/>", "placeholder1", "360", "240", "9.0.0", false, flashvars, params, attributes);
}
function playerReady(thePlayer) {
player = $("player1");
player.addModelListener("TIME", "timeListener");
<c:if test="${model.autoplay}">
play();
</c:if>
}
function play() {
var list = new Array();
list[0] = {
file:"${streamUrl}&maxBitRate=" + maxBitRate + "&timeOffset=" + timeOffset + "&p=${model.p}" + "&u=${model.u}",
duration:${model.duration} - timeOffset,
provider:"video"
};
player.sendEvent("LOAD", list);
player.sendEvent("PLAY");
}
function timeListener(obj) {
var newPosition = Math.round(obj.position);
if (newPosition != position) {
position = newPosition;
updatePosition();
}
}
function updatePosition() {
var pos = parseInt(timeOffset) + parseInt(position);
var minutes = Math.round(pos / 60);
var seconds = pos % 60;
var result = minutes + ":";
if (seconds < 10) {
result += "0";
}
result += seconds;
$("position").innerHTML = result;
}
function changeTimeOffset() {
timeOffset = $("timeOffset").getValue();
play();
}
function changeBitRate() {
maxBitRate = $("maxBitRate").getValue();
timeOffset = parseInt(timeOffset) + parseInt(position);
play();
}
</script>
</head>
<body class="mainframe bgcolor1" onload="init();">
<h1>${model.video.title}</h1>
<div id="wrapper" style="padding-top:1em">
<div id="placeholder1"><span class="warning"><a href="http://www.adobe.com/go/getflashplayer"><fmt:message key="playlist.getflash"/></a></span></div>
</div>
<div style="padding-top:1.3em;padding-bottom:0.7em;font-size:16px">
<span id="progress" style="padding-right:0.5em">0:00</span>
<select id="timeOffset" onchange="changeTimeOffset();" style="padding-left:0.25em;padding-right:0.25em;margin-right:0.5em;font-size:16px">
<c:forEach items="${model.skipOffsets}" var="skipOffset">
<c:choose>
<c:when test="${skipOffset.value eq model.timeOffset}">
<option selected="selected" value="${skipOffset.value}">${skipOffset.key}</option>
</c:when>
<c:otherwise>
<option value="${skipOffset.value}">${skipOffset.key}</option>
</c:otherwise>
</c:choose>
</c:forEach>
</select>
<select id="maxBitRate" onchange="changeBitRate();" style="padding-left:0.25em;padding-right:0.25em;margin-right:0.5em;font-size:16px">
<c:forEach items="${model.bitRates}" var="bitRate">
<c:choose>
<c:when test="${bitRate eq model.maxBitRate}">
<option selected="selected" value="${bitRate}">${bitRate} Kbps</option>
</c:when>
<c:otherwise>
<option value="${bitRate}">${bitRate} Kbps</option>
</c:otherwise>
</c:choose>
</c:forEach>
</select>
</div>
</body>
</html>
@@ -0,0 +1,199 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<html>
<head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/util.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/interface/chatService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/interface/nowPlayingService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/scripts-2.0.js"/>"></script>
<script type="text/javascript">
var chatRevision = 0;
function init() {
dwr.engine.setErrorHandler(null);
startGetScanningStatusTimer();
<c:if test="${model.showChat}">
chatService.addMessage(null);
</c:if>
<c:if test="${model.showNowPlaying}">
startGetNowPlayingTimer();
</c:if>
<c:if test="${model.showChat}">
startGetMessagesTimer();
</c:if>
}
function startGetNowPlayingTimer() {
nowPlayingService.getNowPlaying(getNowPlayingCallback);
setTimeout("startGetNowPlayingTimer()", 10000);
}
function getNowPlayingCallback(nowPlaying) {
var html = nowPlaying.length == 0 ? "" : "<h2><fmt:message key="main.nowplaying"/></h2><table style='width:100%'>";
for (var i = 0; i < nowPlaying.length; i++) {
html += "<tr><td colspan='2' class='detail' style='padding-top:1em;white-space:nowrap'>";
if (nowPlaying[i].avatarUrl != null) {
html += "<img src='" + nowPlaying[i].avatarUrl + "' style='padding-right:5pt;width:30px;height:30px'>";
}
html += "<b>" + nowPlaying[i].username + "</b></td></tr>";
html += "<tr><td class='detail' style='padding-right:1em'>" +
"<a title='" + nowPlaying[i].tooltip + "' target='main' href='" + nowPlaying[i].albumUrl + "'>";
if (nowPlaying[i].artist != null) {
html += nowPlaying[i].artist + "<br/>";
}
html += "<span class='songTitle'>" + nowPlaying[i].title + "</span></a><br/>";
if (nowPlaying[i].lyricsUrl != null) {
html += "<span class='forward'><a href='" + nowPlaying[i].lyricsUrl + "' onclick=\"return popupSize(this, 'lyrics', 500, 550)\">" +
"<fmt:message key="main.lyrics"/>" + "</a></span>";
}
html += "</td><td>" +
"<a title='" + nowPlaying[i].tooltip + "' target='main' href='" + nowPlaying[i].albumUrl + "'>" +
"<img src='" + nowPlaying[i].coverArtUrl + "' class='dropshadow' height='60' width='60'></a>" +
"</td></tr>";
var minutesAgo = nowPlaying[i].minutesAgo;
if (minutesAgo > 4) {
html += "<tr><td class='detail' colspan='2'>" + minutesAgo + " <fmt:message key="main.minutesago"/></td></tr>";
}
}
html += "</table>";
$("#nowPlaying").html(html);
}
function startGetMessagesTimer() {
chatService.getMessages(chatRevision, getMessagesCallback);
setTimeout("startGetMessagesTimer()", 10000);
}
function addMessage() {
chatService.addMessage($("#message").val());
$("#message").val(null);
setTimeout("startGetMessagesTimer()", 500);
}
function clearMessages() {
chatService.clearMessages();
setTimeout("startGetMessagesTimer()", 500);
}
function getMessagesCallback(messages) {
if (messages == null) {
return;
}
chatRevision = messages.revision;
// Delete all the rows except for the "pattern" row
dwr.util.removeAllRows("chatlog", { filter:function(div) {
return (div.id != "pattern");
}});
// Create a new set cloned from the pattern row
for (var i = 0; i < messages.messages.length; i++) {
var message = messages.messages[i];
var id = i + 1;
dwr.util.cloneNode("pattern", { idSuffix:id });
$("#user" + id).text(message.username);
$("#date" + id).text(" [" + formatDate(message.date) + "]");
$("#content" + id).text(message.content);
$("#pattern" + id).show();
}
var clearDiv = $("#clearDiv");
if (clearDiv) {
if (messages.messages.length == 0) {
clearDiv.hide();
} else {
clearDiv.show();
}
}
}
function formatDate(date) {
var hours = date.getHours();
var minutes = date.getMinutes();
var result = hours < 10 ? "0" : "";
result += hours;
result += ":";
if (minutes < 10) {
result += "0";
}
result += minutes;
return result;
}
function startGetScanningStatusTimer() {
nowPlayingService.getScanningStatus(getScanningStatusCallback);
}
function getScanningStatusCallback(scanInfo) {
$("#scanCount").text(scanInfo.count);
if (scanInfo.scanning) {
$("#scanningStatus").show();
setTimeout("startGetScanningStatusTimer()", 1000);
} else {
$("#scanningStatus").hide();
setTimeout("startGetScanningStatusTimer()", 15000);
}
}
</script>
</head>
<body class="bgcolor1 rightframe" style="padding-top:2em" onload="init()">
<c:if test="${not model.licenseInfo.licenseValid}">
<div class="detail" style="text-align: center;padding-bottom: 1em">
<a href="premiumSettings.view" target="main"><img src="<spring:theme code="donateSmallImage"/>" alt="">
<fmt:message key="top.getpremium"/></a>
<c:if test="${model.licenseInfo.trialDaysLeft gt 0}">
<br>
<a href="premiumSettings.view" target="main"><fmt:message key="top.trialdaysleft"><fmt:param value="${model.licenseInfo.trialDaysLeft}"/></fmt:message></a>
</c:if>
</div>
</c:if>
<c:if test="${model.newVersionAvailable}">
<div class="warning" style="padding-bottom: 1em">
<fmt:message key="top.upgrade"><fmt:param value="${model.brand}"/><fmt:param value="${model.latestVersion}"/></fmt:message>
</div>
</c:if>
<div id="scanningStatus" style="display: none;" class="warning">
<img src="<spring:theme code="scanningImage"/>" title="" alt=""> <fmt:message key="main.scanning"/> <span id="scanCount"></span>
</div>
<div id="nowPlaying"></div>
<c:if test="${model.showChat}">
<h2><fmt:message key="main.chat"/></h2>
<div style="padding-top:0.3em;padding-bottom:0.3em">
<input type="text" id="message" placeholder="<fmt:message key="main.message"/>" style="width:100%" onkeypress="dwr.util.onReturn(event, addMessage)"/>
</div>
<table>
<tbody id="chatlog">
<tr id="pattern" style="display:none;margin:0;padding:0 0 0.15em 0;border:0"><td>
<span id="user" class="detail" style="font-weight:bold"></span>&nbsp;<span id="date" class="detail"></span> <span id="content"></span></td>
</tr>
</tbody>
</table>
<c:if test="${model.user.adminRole}">
<div id="clearDiv" style="display:none;" class="forward"><a href="javascript:clearMessages()"> <fmt:message key="main.clearchat"/></a></div>
</c:if>
</c:if>
</body>
</html>
@@ -0,0 +1,124 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<%--@elvariable id="command" type="org.libresonic.player.command.SearchCommand"--%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript" language="javascript">
function showMoreArtists() {
$('.artistRow').show(); $('#moreArtists').hide();
}
function showMoreAlbums() {
$('.albumRow').show(); $('#moreAlbums').hide();
}
function showMoreSongs() {
$('.songRow').show();$('#moreSongs').hide();
}
</script>
</head>
<body class="mainframe bgcolor1">
<h1>
<img src="<spring:theme code="searchImage"/>" alt=""/>
<span style="vertical-align: middle"><fmt:message key="search.title"/></span>
</h1>
<c:if test="${command.indexBeingCreated}">
<p class="warning"><fmt:message key="search.index"/></p>
</c:if>
<c:if test="${not command.indexBeingCreated and empty command.artists and empty command.albums and empty command.songs}">
<p class="warning"><fmt:message key="search.hits.none"/></p>
</c:if>
<c:if test="${not empty command.artists}">
<h2><b><fmt:message key="search.hits.artists"/></b></h2>
<table class="music indent">
<c:forEach items="${command.artists}" var="match" varStatus="loopStatus">
<sub:url value="/main.view" var="mainUrl">
<sub:param name="path" value="${match.path}"/>
</sub:url>
<tr class="artistRow" ${loopStatus.count > 5 ? "style='display:none'" : ""}>
<c:import url="playButtons.jsp">
<c:param name="id" value="${match.id}"/>
<c:param name="playEnabled" value="${command.user.streamRole and not command.partyModeEnabled}"/>
<c:param name="addEnabled" value="${command.user.streamRole and (not command.partyModeEnabled or not match.directory)}"/>
<c:param name="asTable" value="true"/>
</c:import>
<td class="truncate"><a href="${mainUrl}">${fn:escapeXml(match.name)}</a></td>
</tr>
</c:forEach>
</table>
<c:if test="${fn:length(command.artists) gt 5}">
<div id="moreArtists" class="forward"><a href="javascript:showMoreArtists()"><fmt:message key="search.hits.more"/></a></div>
</c:if>
</c:if>
<c:if test="${not empty command.albums}">
<h2><b><fmt:message key="search.hits.albums"/></b></h2>
<table class="music indent">
<c:forEach items="${command.albums}" var="match" varStatus="loopStatus">
<sub:url value="/main.view" var="mainUrl">
<sub:param name="path" value="${match.path}"/>
</sub:url>
<tr class="albumRow" ${loopStatus.count > 5 ? "style='display:none'" : ""}>
<c:import url="playButtons.jsp">
<c:param name="id" value="${match.id}"/>
<c:param name="playEnabled" value="${command.user.streamRole and not command.partyModeEnabled}"/>
<c:param name="addEnabled" value="${command.user.streamRole and (not command.partyModeEnabled or not match.directory)}"/>
<c:param name="asTable" value="true"/>
</c:import>
<td class="truncate"><a href="${mainUrl}">${fn:escapeXml(match.albumName)}</a></td>
<td class="truncate"><span class="detail">${fn:escapeXml(match.artist)}</span></td>
</tr>
</c:forEach>
</table>
<c:if test="${fn:length(command.albums) gt 5}">
<div id="moreAlbums" class="forward"><a href="javascript:showMoreAlbums()"><fmt:message key="search.hits.more"/></a></div>
</c:if>
</c:if>
<c:if test="${not empty command.songs}">
<h2><b><fmt:message key="search.hits.songs"/></b></h2>
<table class="music indent">
<c:forEach items="${command.songs}" var="match" varStatus="loopStatus">
<sub:url value="/main.view" var="mainUrl">
<sub:param name="path" value="${match.parentPath}"/>
</sub:url>
<tr class="songRow" ${loopStatus.count > 15 ? "style='display:none'" : ""}>
<c:import url="playButtons.jsp">
<c:param name="id" value="${match.id}"/>
<c:param name="playEnabled" value="${command.user.streamRole and not command.partyModeEnabled}"/>
<c:param name="addEnabled" value="${command.user.streamRole and (not command.partyModeEnabled or not match.directory)}"/>
<c:param name="video" value="${match.video and command.player.web}"/>
<c:param name="asTable" value="true"/>
</c:import>
<td class="truncate"><span class="songTitle">${fn:escapeXml(match.title)}</span></td>
<td class="truncate"><a href="${mainUrl}"><span class="detail">${fn:escapeXml(match.albumName)}</span></a></td>
<td class="truncate"><span class="detail">${fn:escapeXml(match.artist)}</span></td>
</tr>
</c:forEach>
</table>
<c:if test="${fn:length(command.songs) gt 15}">
<div id="moreSongs" class="forward"><a href="javascript:showMoreSongs()"><fmt:message key="search.hits.more"/></a></div>
</c:if>
</c:if>
</body></html>
@@ -0,0 +1,39 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<%@ include file="include.jsp" %>
<c:if test="${param.toast}">
<script type="text/javascript">
$(document).ready(function () {
$().toastmessage("showSuccessToast", "<fmt:message key="common.settingssaved"/>");
});
</script>
</c:if>
<c:set var="categories" value="${param.restricted ? 'personal password player share' : 'musicFolder general advanced personal user player network share dlna sonos transcoding internetRadio podcast'}"/>
<h1>
<img src="<spring:theme code="settingsImage"/>" alt=""/>
<span style="vertical-align: middle"><fmt:message key="settingsheader.title"/></span>
</h1>
<h2>
<c:forTokens items="${categories}" delims=" " var="cat" varStatus="loopStatus">
<c:choose>
<c:when test="${loopStatus.count > 1 and (loopStatus.count - 1) % 8 != 0}">&nbsp;|&nbsp;</c:when>
<c:otherwise></h2><h2></c:otherwise>
</c:choose>
<c:url var="url" value="${cat}Settings.view?"/>
<c:choose>
<c:when test="${param.cat eq cat}">
<span class="headerSelected"><fmt:message key="settingsheader.${cat}"/></span>
</c:when>
<c:otherwise>
<span class="header"><a href="${url}"><fmt:message key="settingsheader.${cat}"/></a></span>
</c:otherwise>
</c:choose>
</c:forTokens>
</h2>
<p></p>
@@ -0,0 +1,70 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<%--@elvariable id="model" type="Map"--%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
</head>
<body class="mainframe bgcolor1">
<c:import url="settingsHeader.jsp">
<c:param name="cat" value="share"/>
<c:param name="toast" value="${model.toast}"/>
<c:param name="restricted" value="${not model.user.adminRole}"/>
</c:import>
<form method="post" action="shareSettings.view">
<table class="music indent">
<tr>
<th style="padding-left:1em"><fmt:message key="sharesettings.name"/></th>
<th style="padding-left:1em"><fmt:message key="sharesettings.owner"/></th>
<th style="padding-left:1em"><fmt:message key="sharesettings.description"/></th>
<th style="padding-left:1em"><fmt:message key="sharesettings.lastvisited"/></th>
<th style="padding-left:1em"><fmt:message key="sharesettings.visits"/></th>
<th style="padding-left:1em"><fmt:message key="sharesettings.files"/></th>
<th style="padding-left:1em"><fmt:message key="sharesettings.expires"/></th>
<th style="padding-left:1em"><fmt:message key="sharesettings.expirein"/></th>
<th style="padding-left:1em"><fmt:message key="common.delete"/></th>
</tr>
<c:forEach items="${model.shareInfos}" var="shareInfo" varStatus="loopStatus">
<c:set var="share" value="${shareInfo.share}"/>
<c:url value="main.view" var="albumUrl">
<c:param name="id" value="${shareInfo.dir.id}"/>
</c:url>
<tr>
<td style="padding-left:1em"><a href="${model.shareBaseUrl}${share.name}" target="_blank">${share.name}</a></td>
<td style="padding-left:1em">${fn:escapeXml(share.username)}</td>
<td style="padding-left:1em"><input type="text" name="description[${share.id}]" size="40" value="${share.description}"/></td>
<td style="padding-left:1em"><fmt:formatDate value="${share.lastVisited}" type="date" dateStyle="medium"/></td>
<td style="padding-left:1em; text-align:right">${share.visitCount}</td>
<td style="padding-left:1em"><a href="${albumUrl}" title="${shareInfo.dir.name}"><str:truncateNicely upper="30">${fn:escapeXml(shareInfo.dir.name)}</str:truncateNicely></a></td>
<td style="padding-left:1em"><fmt:formatDate value="${share.expires}" type="date" dateStyle="medium"/></td>
<td style="padding-left:1em">
<label><input type="radio" name="expireIn[${share.id}]" value="7"><fmt:message key="sharesettings.expirein.week"/></label>
<label><input type="radio" name="expireIn[${share.id}]" value="30"><fmt:message key="sharesettings.expirein.month"/></label>
<label><input type="radio" name="expireIn[${share.id}]" value="365"><fmt:message key="sharesettings.expirein.year"/></label>
<label><input type="radio" name="expireIn[${share.id}]" value="0"><fmt:message key="sharesettings.expirein.never"/></label>
</td>
<td style="padding-left:1em" align="center" style="padding-left:1em"><input type="checkbox" name="delete[${share.id}]" class="checkbox"/></td>
</tr>
</c:forEach>
</table>
<c:set var="licenseInfo" value="${model.licenseInfo}"/>
<%@ include file="licenseNotice.jsp" %>
<p style="padding-top:1em">
<input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
<input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'" style="margin-right:2.0em">
<input type="checkbox" id="deleteExpired" name="deleteExpired" class="checkbox"/>
<label for="deleteExpired"><fmt:message key="sharesettings.deleteexpired"/></label>
</p>
</form>
</body></html>
@@ -0,0 +1,66 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<%--
~ This file is part of Libresonic.
~
~ Libresonic is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ Libresonic is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
~
~ Copyright 2015 (C) Sindre Mehus
--%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript" src="<c:url value="/script/scripts.js"/>"></script>
</head>
<body class="mainframe bgcolor1">
<script type="text/javascript" src="<c:url value="/script/wz_tooltip.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/tip_balloon.js"/>"></script>
<c:import url="settingsHeader.jsp">
<c:param name="cat" value="sonos"/>
<c:param name="toast" value="${model.toast}"/>
</c:import>
<form method="post" action="sonosSettings.view">
<div>
<input type="checkbox" name="sonosEnabled" id="sonosEnabled" class="checkbox"
<c:if test="${model.sonosEnabled}">checked="checked"</c:if>/>
<label for="sonosEnabled"><fmt:message key="sonossettings.enabled"/></label>
</div>
<p class="detail" style="width:60%;white-space:normal">
<fmt:message key="sonossettings.description"/>
</p>
<div>
<fmt:message key="sonossettings.servicename"/>
<input name="sonosServiceName" id="sonosServiceName" size="40"
value="<c:out value="${model.sonosServiceName}" escapeXml="true"/>"/>
</div>
<p class="detail" style="width:60%;white-space:normal;padding-top:0">
<fmt:message key="sonossettings.servicename.description"/>
</p>
<c:set var="licenseInfo" value="${model.licenseInfo}"/>
<%@ include file="licenseNotice.jsp" %>
<p>
<input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
<input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
</p>
</form>
</body></html>
@@ -0,0 +1,181 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript" src="<c:url value="/script/scripts-2.0.js"/>"></script>
<script type="text/javascript" src="<c:url value='/dwr/util.js'/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/interface/starService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/interface/playlistService.js"/>"></script>
<script type="text/javascript" language="javascript">
function toggleStar(mediaFileId, imageId) {
if ($(imageId).attr("src").indexOf("<spring:theme code="ratingOnImage"/>") != -1) {
$(imageId).attr("src", "<spring:theme code="ratingOffImage"/>");
starService.unstar(mediaFileId);
}
else if ($(imageId).attr("src").indexOf("<spring:theme code="ratingOffImage"/>") != -1) {
$(imageId).attr("src", "<spring:theme code="ratingOnImage"/>");
starService.star(mediaFileId);
}
}
function onSavePlaylist() {
playlistService.createPlaylistForStarredSongs(function (playlistId) {
top.left.updatePlaylists();
top.left.showAllPlaylists();
top.main.location.href = "playlist.view?id=" + playlistId;
$().toastmessage("showSuccessToast", "<fmt:message key="playlist.toast.saveasplaylist"/>");
});
}
function onPlayAll() {
top.playQueue.onPlayStarred();
}
</script>
</head>
<body class="mainframe bgcolor1">
<h1>
<img src="<spring:theme code="starredImage"/>" alt="">
<span style="vertical-align: middle"><fmt:message key="starred.title"/></span>
</h1>
<c:if test="${empty model.artists and empty model.albums and empty model.songs}">
<p style="padding-top: 1em"><em><fmt:message key="starred.empty"/></em></p>
</c:if>
<c:if test="${not empty model.albums}">
<h2><fmt:message key="search.hits.albums"/></h2>
<div style="padding-top:0.5em">
<c:forEach items="${model.albums}" var="album" varStatus="loopStatus">
<c:set var="albumTitle">
<c:choose>
<c:when test="${empty album.name}">
<fmt:message key="common.unknown"/>
</c:when>
<c:otherwise>
${fn:escapeXml(album.name)}
</c:otherwise>
</c:choose>
</c:set>
<div class="albumThumb">
<c:import url="coverArt.jsp">
<c:param name="albumId" value="${album.id}"/>
<c:param name="caption1" value="${albumTitle}"/>
<c:param name="caption2" value="${fn:escapeXml(album.artist)}"/>
<c:param name="captionCount" value="2"/>
<c:param name="coverArtSize" value="${model.coverArtSize}"/>
<c:param name="showLink" value="true"/>
<c:param name="appearAfter" value="${loopStatus.count * 30}"/>
</c:import>
</div>
</c:forEach>
</c:if>
</div>
<c:if test="${not empty model.artists}">
<h2><fmt:message key="search.hits.artists"/></h2>
<table class="music indent">
<c:forEach items="${model.artists}" var="artist">
<c:url value="/main.view" var="mainUrl">
<c:param name="id" value="${artist.id}"/>
</c:url>
<tr>
<c:import url="playButtons.jsp">
<c:param name="id" value="${artist.id}"/>
<c:param name="playEnabled" value="${model.user.streamRole and not model.partyModeEnabled}"/>
<c:param name="addEnabled" value="${model.user.streamRole and (not model.partyModeEnabled or not artist.directory)}"/>
<c:param name="starEnabled" value="true"/>
<c:param name="starred" value="${not empty artist.starredDate}"/>
<c:param name="asTable" value="true"/>
</c:import>
<td class="truncate">
<a href="${mainUrl}">${fn:escapeXml(artist.name)}</a>
</td>
</tr>
</c:forEach>
</table>
</c:if>
<c:if test="${not empty model.songs}">
<h2><fmt:message key="search.hits.songs"/></h2>
<table class="music indent">
<c:forEach items="${model.songs}" var="song">
<sub:url value="/main.view" var="mainUrl">
<sub:param name="path" value="${song.parentPath}"/>
</sub:url>
<tr>
<c:import url="playButtons.jsp">
<c:param name="id" value="${song.id}"/>
<c:param name="playEnabled" value="${model.user.streamRole and not model.partyModeEnabled}"/>
<c:param name="addEnabled" value="${model.user.streamRole and not model.partyModeEnabled}"/>
<c:param name="starEnabled" value="true"/>
<c:param name="starred" value="${not empty song.starredDate}"/>
<c:param name="video" value="false"/>
<c:param name="asTable" value="true"/>
</c:import>
<td class="truncate">
${fn:escapeXml(song.title)}
</td>
<td class="truncate">
<a href="${mainUrl}"><span class="detail">${fn:escapeXml(song.albumName)}</span></a>
</td>
<td class="truncate">
<span class="detail">${fn:escapeXml(song.artist)}</span>
</td>
</tr>
</c:forEach>
</table>
<div class="forward" style="float:left;padding-right:1.5em">
<a href="javascript:noop()" onclick="onSavePlaylist()"><fmt:message key="playlist.save"/></a>
</div>
<div class="forward" style="float: left">
<a href="javascript:noop()" onclick="onPlayAll()"><fmt:message key="main.playall"/></a>
</div>
<div style="clear: both"></div>
</c:if>
<c:if test="${not empty model.videos}">
<h2><fmt:message key="search.hits.videos"/></h2>
<table class="music indent">
<c:forEach items="${model.videos}" var="video">
<c:url value="/videoPlayer.view" var="videoUrl">
<c:param name="id" value="${video.id}"/>
</c:url>
<tr>
<c:import url="playButtons.jsp">
<c:param name="id" value="${video.id}"/>
<c:param name="playEnabled" value="${model.user.streamRole and not model.partyModeEnabled}"/>
<c:param name="addEnabled" value="${model.user.streamRole and not model.partyModeEnabled}"/>
<c:param name="starEnabled" value="true"/>
<c:param name="starred" value="${not empty video.starredDate}"/>
<c:param name="video" value="${model.player.web}"/>
<c:param name="asTable" value="true"/>
</c:import>
<td class="truncate">
<a href="${videoUrl}">${fn:escapeXml(video.name)}</a>
</td>
</tr>
</c:forEach>
</table>
</c:if>
</body></html>
@@ -0,0 +1,112 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<%@ include file="head.jsp" %>
<meta http-equiv="CACHE-CONTROL" content="NO-CACHE">
<meta http-equiv="REFRESH" content="20;URL=status.view">
</head>
<body class="mainframe bgcolor1">
<h1>
<img src="<spring:theme code="statusImage"/>" alt="">
<span style="vertical-align: middle"><fmt:message key="status.title"/></span>
</h1>
<table width="100%" class="ruleTable indent">
<tr>
<th class="ruleTableHeader"><fmt:message key="status.type"/></th>
<th class="ruleTableHeader"><fmt:message key="status.player"/></th>
<th class="ruleTableHeader"><fmt:message key="status.user"/></th>
<th class="ruleTableHeader"><fmt:message key="status.current"/></th>
<th class="ruleTableHeader"><fmt:message key="status.transmitted"/></th>
<th class="ruleTableHeader"><fmt:message key="status.bitrate"/></th>
</tr>
<c:forEach items="${model.transferStatuses}" var="status">
<c:choose>
<c:when test="${empty status.playerType}">
<fmt:message key="common.unknown" var="type"/>
</c:when>
<c:otherwise>
<c:set var="type" value="(${status.playerType})"/>
</c:otherwise>
</c:choose>
<c:choose>
<c:when test="${status.stream}">
<fmt:message key="status.stream" var="transferType"/>
</c:when>
<c:when test="${status.download}">
<fmt:message key="status.download" var="transferType"/>
</c:when>
<c:when test="${status.upload}">
<fmt:message key="status.upload" var="transferType"/>
</c:when>
</c:choose>
<c:choose>
<c:when test="${empty status.username}">
<fmt:message key="common.unknown" var="user"/>
</c:when>
<c:otherwise>
<c:set var="user" value="${status.username}"/>
</c:otherwise>
</c:choose>
<c:choose>
<c:when test="${empty status.path}">
<fmt:message key="common.unknown" var="current"/>
</c:when>
<c:otherwise>
<c:set var="current" value="${status.path}"/>
</c:otherwise>
</c:choose>
<sub:url value="/statusChart.view" var="chartUrl">
<c:if test="${status.stream}">
<sub:param name="type" value="stream"/>
</c:if>
<c:if test="${status.download}">
<sub:param name="type" value="download"/>
</c:if>
<c:if test="${status.upload}">
<sub:param name="type" value="upload"/>
</c:if>
<sub:param name="index" value="${status.index}"/>
</sub:url>
<tr>
<td class="ruleTableCell">${transferType}</td>
<td class="ruleTableCell">${status.player}<br>${type}</td>
<td class="ruleTableCell">${user}</td>
<td class="ruleTableCell">${current}</td>
<td class="ruleTableCell">${status.bytes}</td>
<td class="ruleTableCell" width="${model.chartWidth}"><img width="${model.chartWidth}" height="${model.chartHeight}" src="${chartUrl}" alt=""></td>
</tr>
</c:forEach>
</table>
<table style="width:100%;padding-top:3em">
<tr>
<th><fmt:message key="home.chart.total"/></th>
<th><fmt:message key="home.chart.stream"/></th>
</tr>
<tr>
<td><img src="<c:url value="/userChart.view"><c:param name="type" value="total"/></c:url>" alt=""></td>
<td><img src="<c:url value="/userChart.view"><c:param name="type" value="stream"/></c:url>" alt=""></td>
</tr>
<tr>
<th><fmt:message key="home.chart.download"/></th>
<th><fmt:message key="home.chart.upload"/></th>
</tr>
<tr>
<td><img src="<c:url value="/userChart.view"><c:param name="type" value="download"/></c:url>" alt=""></td>
<td><img src="<c:url value="/userChart.view"><c:param name="type" value="upload"/></c:url>" alt=""></td>
</tr>
</table>
<div class="forward"><a href="status.view?"><fmt:message key="common.refresh"/></a></div>
</body></html>
@@ -0,0 +1,26 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<html>
<head>
<%@ include file="head.jsp" %>
</head>
<body style="padding-top: 10em">
<audio id="demo" autoplay preload="none" controls src="http://localhost:4040/stream?id=1009&maxBitRate=64&foo=3bar"></audio>
<%--<audio id="demo" autoplay preload="none" controls src="http://localhost:4040/stream?id=98"></audio>--%>
<div>
<button onclick="document.getElementById('demo').play()">Play the Audio</button>
<button onclick="document.getElementById('demo').pause()">Pause the Audio</button>
<button onclick="document.getElementById('demo').volume+=0.1">Increase Volume</button>
<button onclick="document.getElementById('demo').volume-=0.1">Decrease Volume</button>
</div>
<script>
var a = document.getElementById("demo");
a.addEventListener("seeked", function() { console.log("seeked") }, true);
// a.addEventListener("timeupdate", function() { console.log("timeupdate " + a.currentTime) }, true);
a.addEventListener("progress", function() { console.log("progress " + a.buffered.end(0)) }, true);
</script>
</body>
</html>
@@ -0,0 +1,147 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/interface/multiService.js"/>"></script>
<script type="text/javascript">
var previousQuery = "";
var instantSearchTimeout;
function triggerInstantSearch() {
if (instantSearchTimeout) {
window.clearTimeout(instantSearchTimeout);
}
instantSearchTimeout = window.setTimeout(executeInstantSearch, 300);
}
function executeInstantSearch() {
var query = $("#query").val().trim();
if (query.length > 1 && query != previousQuery) {
previousQuery = query;
document.searchForm.submit();
}
}
function showLeftFrame() {
$("#show-left-frame").hide();
$("#hide-left-frame").show();
toggleLeftFrame(230);
multiService.setShowSideBar(true);
}
function hideLeftFrame() {
$("#hide-left-frame").hide();
$("#show-left-frame").show();
toggleLeftFrame(0);
multiService.setShowSideBar(false);
}
function toggleLeftFrame(width) {
<%-- Disable animation in Chrome. It stopped working in Chrome 44. --%>
var duration = navigator.userAgent.indexOf("Chrome") != -1 ? 0 : 400;
$("#dummy-animation-target").stop();
$("#dummy-animation-target").animate({"max-width": width}, {
step: function (now, fx) {
top.document.getElementById("mainFrameset").cols = now + ",*";
},
duration: duration
});
}
</script>
</head>
<body class="bgcolor2 topframe" style="margin:0.4em 1em 0 1em;">
<span id="dummy-animation-target" style="max-width:0;display: none"></span>
<fmt:message key="top.home" var="home"/>
<fmt:message key="top.now_playing" var="nowPlaying"/>
<fmt:message key="top.starred" var="starred"/>
<fmt:message key="left.playlists" var="playlists"/>
<fmt:message key="top.settings" var="settings"/>
<fmt:message key="top.status" var="status" />
<fmt:message key="top.podcast" var="podcast"/>
<fmt:message key="top.more" var="more"/>
<fmt:message key="top.help" var="help"/>
<fmt:message key="top.search" var="search"/>
<table style="margin:0;padding-top:5px">
<tr>
<td style="padding-right:4.5em;">
<img id="show-left-frame" src="<spring:theme code="viewAsListImage"/>" onclick="showLeftFrame()" alt="" style="display:${model.showSideBar ? 'none' : 'inline'};cursor:pointer">
<img id="hide-left-frame" src="<spring:theme code="viewAsListImage"/>" onclick="hideLeftFrame()" alt="" style="display:${model.showSideBar ? 'inline' : 'none'};cursor:pointer">
</td>
<td style="min-width:3em;padding-right:1em;text-align: center">
<a href="home.view?" target="main"><img src="<spring:theme code="homeImage"/>" title="${home}" alt="${home}"></a>
<div class="topHeader"><a href="home.view?" target="main">${home}</a></div>
</td>
<td style="min-width:3em;padding-right:1em;text-align: center">
<a href="nowPlaying.view?" target="main"><img src="<spring:theme code="nowPlayingImage"/>" title="${nowPlaying}" alt="${nowPlaying}"></a>
<div class="topHeader"><a href="nowPlaying.view?" target="main">${nowPlaying}</a></div>
</td>
<td style="min-width:3em;padding-right:1em;text-align: center">
<a href="starred.view?" target="main"><img src="<spring:theme code="starredImage"/>" title="${starred}" alt="${starred}"></a>
<div class="topHeader"><a href="starred.view?" target="main">${starred}</a></div>
</td>
<td style="min-width:3em;padding-right:1em;text-align: center">
<a href="playlists.view?" target="main"><img src="<spring:theme code="playlistImage"/>" title="${playlists}" alt="${playlists}"></a>
<div class="topHeader"><a href="playlists.view?" target="main">${playlists}</a></div>
</td>
<td style="min-width:4em;padding-right:1em;text-align: center">
<a href="podcastChannels.view?" target="main"><img src="<spring:theme code="podcastLargeImage"/>" title="${podcast}" alt="${podcast}"></a>
<div class="topHeader"><a href="podcastChannels.view?" target="main">${podcast}</a></div>
</td>
<c:if test="${model.user.settingsRole}">
<td style="min-width:3em;padding-right:1em;text-align: center">
<a href="settings.view?" target="main"><img src="<spring:theme code="settingsImage"/>" title="${settings}" alt="${settings}"></a>
<div class="topHeader"><a href="settings.view?" target="main">${settings}</a></div>
</td>
</c:if>
<td style="min-width:3em;padding-right:1em;text-align: center">
<a href="status.view?" target="main"><img src="<spring:theme code="statusImage"/>" title="${status}" alt="${status}"></a>
<div class="topHeader"><a href="status.view?" target="main">${status}</a></div>
</td>
<td style="min-width:3em;padding-right:1em;text-align: center">
<a href="more.view?" target="main"><img src="<spring:theme code="moreImage"/>" title="${more}" alt="${more}"></a>
<div class="topHeader"><a href="more.view?" target="main">${more}</a></div>
</td>
<td style="min-width:3em;padding-right:1em;text-align: center">
<a href="help.view?" target="main"><img src="<spring:theme code="helpImage"/>" title="${help}" alt="${help}"></a>
<div class="topHeader"><a href="help.view?" target="main">${help}</a></div>
</td>
<td style="padding-left:1em">
<form method="post" action="search.view" target="main" name="searchForm">
<td><input type="text" name="query" id="query" size="28" placeholder="${search}" onclick="select();"
onkeyup="triggerInstantSearch();"></td>
<td><a href="javascript:document.searchForm.submit()"><img src="<spring:theme code="searchImage"/>" alt="${search}" title="${search}"></a></td>
</form>
</td>
<td style="padding-left:15pt;padding-right:5pt;vertical-align: middle;width: 100%;text-align: center">
<c:if test="${model.showAvatar}">
<sub:url value="avatar.view" var="avatarUrl">
<sub:param name="username" value="${model.user.username}"/>
</sub:url>
<div style="padding-bottom: 4px">
<c:if test="${model.user.settingsRole}"><a href="personalSettings.view" target="main"></c:if>
<img src="${avatarUrl}" alt="" width="30" height="30">
<c:if test="${model.user.settingsRole}"></a></c:if>
</div>
</c:if>
<div class="detail">
<fmt:message key="top.logout" var="logout"><fmt:param value="${model.user.username}"/></fmt:message>
<a href="j_acegi_logout" target="_top">${fn:escapeXml(logout)}</a>
</div>
</td>
</tr></table>
</body></html>
@@ -0,0 +1,97 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
</head>
<body class="mainframe bgcolor1">
<script type="text/javascript" src="<c:url value="/script/wz_tooltip.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/tip_balloon.js"/>"></script>
<c:import url="settingsHeader.jsp">
<c:param name="cat" value="transcoding"/>
<c:param name="toast" value="${model.toast}"/>
</c:import>
<form method="post" action="transcodingSettings.view">
<table class="indent">
<tr>
<th><fmt:message key="transcodingsettings.name"/></th>
<th><fmt:message key="transcodingsettings.sourceformat"/></th>
<th><fmt:message key="transcodingsettings.targetformat"/></th>
<th><fmt:message key="transcodingsettings.step1"/></th>
<th><fmt:message key="transcodingsettings.step2"/></th>
<th style="padding-left:1em"><fmt:message key="common.delete"/></th>
</tr>
<c:forEach items="${model.transcodings}" var="transcoding">
<tr>
<td><input style="font-family:monospace" type="text" name="name[${transcoding.id}]" size="10" value="${transcoding.name}"/></td>
<td><input style="font-family:monospace" type="text" name="sourceFormats[${transcoding.id}]" size="36" value="${transcoding.sourceFormats}"/></td>
<td><input style="font-family:monospace" type="text" name="targetFormat[${transcoding.id}]" size="10" value="${transcoding.targetFormat}"/></td>
<td><input style="font-family:monospace" type="text" name="step1[${transcoding.id}]" size="60" value="${transcoding.step1}"/></td>
<td><input style="font-family:monospace" type="text" name="step2[${transcoding.id}]" size="22" value="${transcoding.step2}"/></td>
<td align="center" style="padding-left:1em"><input type="checkbox" name="delete[${transcoding.id}]" class="checkbox"/></td>
</tr>
</c:forEach>
<tr>
<th colspan="6" align="left" style="padding-top:1em"><fmt:message key="transcodingsettings.add"/></th>
</tr>
<tr>
<td><input style="font-family:monospace" type="text" name="name" size="10" placeholder="<fmt:message key="transcodingsettings.name"/>" value="${model.newTranscoding.name}"/></td>
<td><input style="font-family:monospace" type="text" name="sourceFormats" size="36" placeholder="<fmt:message key="transcodingsettings.sourceformat"/>" value="${model.newTranscoding.sourceFormats}"/></td>
<td><input style="font-family:monospace" type="text" name="targetFormat" size="10" placeholder="<fmt:message key="transcodingsettings.targetformat"/>" value="${model.newTranscoding.targetFormat}"/></td>
<td><input style="font-family:monospace" type="text" name="step1" size="60" placeholder="<fmt:message key="transcodingsettings.step1"/>" value="${model.newTranscoding.step1}"/></td>
<td><input style="font-family:monospace" type="text" name="step2" size="22" placeholder="<fmt:message key="transcodingsettings.step2"/>" value="${model.newTranscoding.step2}"/></td>
<td/>
</tr>
<tr>
<td colspan="6" style="padding-top:0.1em">
<input type="checkbox" id="defaultActive" name="defaultActive" class="checkbox" checked/>
<label for="defaultActive"><fmt:message key="transcodingsettings.defaultactive"/></label>
</td>
</tr>
</table>
<table style="white-space:nowrap" class="indent">
<tr>
<td style="font-weight: bold;">
<fmt:message key="advancedsettings.downsamplecommand"/>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="downsamplecommand"/></c:import>
</td>
<td>
<input style="font-family:monospace" type="text" name="downsampleCommand" size="100" value="${model.downsampleCommand}"/>
</td>
</tr>
<tr>
<td style="font-weight: bold;">
<fmt:message key="advancedsettings.hlscommand"/>
<c:import url="helpToolTip.jsp"><c:param name="topic" value="hlscommand"/></c:import>
</td>
<td>
<input style="font-family:monospace" type="text" name="hlsCommand" size="100" value="${model.hlsCommand}"/>
</td>
</tr>
</table>
<p style="padding-top:0.75em">
<input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
<input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'" style="margin-right:1.3em">
<a href="http://www.libresonic.org/pages/transcoding.jsp" target="_blank"><fmt:message key="transcodingsettings.recommended"/></a>
</p>
</form>
<c:if test="${not empty model.error}">
<p class="warning"><fmt:message key="${model.error}"/></p>
</c:if>
<div style="width:60%">
<fmt:message key="transcodingsettings.info"><fmt:param value="${model.transcodeDirectory}"/><fmt:param value="${model.brand}"/></fmt:message>
</div>
</body></html>
@@ -0,0 +1,29 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<html><head>
<%@ include file="head.jsp" %>
</head><body>
<h1><fmt:message key="upload.title"/></h1>
<c:forEach items="${model.uploadedFiles}" var="file">
<p><fmt:message key="upload.success"><fmt:param value="${file.path}"/></fmt:message></p>
</c:forEach>
<c:forEach items="${model.unzippedFiles}" var="file">
<fmt:message key="upload.unzipped"><fmt:param value="${file.path}"/></fmt:message><br/>
</c:forEach>
<c:choose>
<c:when test="${not empty model.exception}">
<p><fmt:message key="upload.failed"><fmt:param value="${model.exception.message}"/></fmt:message></p>
</c:when>
<c:when test="${empty model.uploadedFiles}">
<p><fmt:message key="upload.empty"/></p>
</c:when>
</c:choose>
<div class="back"><a href="more.view?"><fmt:message key="common.back"/></a></div>
</body></html>
@@ -0,0 +1,221 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<%--@elvariable id="command" type="org.libresonic.player.command.UserSettingsCommand"--%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript" src="<c:url value="/script/scripts-2.0.js"/>"></script>
<script type="text/javascript">
function init() {
enablePasswordChangeFields();
<c:if test="${command.reload}">
parent.frames.left.location.href="left.view?";
</c:if>
}
function enablePasswordChangeFields() {
var changePasswordCheckbox = $("#passwordChange");
var ldapCheckbox = $("#ldapAuthenticated");
var passwordChangeTable = $("#passwordChangeTable");
var passwordChangeCheckboxTable = $("#passwordChangeCheckboxTable");
if (changePasswordCheckbox && changePasswordCheckbox.is(":checked") && (ldapCheckbox == null || !ldapCheckbox.is(":checked"))) {
passwordChangeTable.show();
} else {
passwordChangeTable.hide();
}
if (changePasswordCheckbox) {
if (ldapCheckbox && ldapCheckbox.is(":checked")) {
passwordChangeCheckboxTable.hide();
} else {
passwordChangeCheckboxTable.show();
}
}
}
</script>
</head>
<body class="mainframe bgcolor1" onload="init()">
<script type="text/javascript" src="<c:url value="/script/wz_tooltip.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/tip_balloon.js"/>"></script>
<c:import url="settingsHeader.jsp">
<c:param name="cat" value="user"/>
<c:param name="toast" value="${command.toast}"/>
</c:import>
<table class="indent">
<tr>
<td><b><fmt:message key="usersettings.title"/></b></td>
<td>
<select name="username" onchange="location='userSettings.view?userIndex=' + (selectedIndex - 1);">
<option value="">-- <fmt:message key="usersettings.newuser"/> --</option>
<c:forEach items="${command.users}" var="user">
<option ${user.username eq command.username ? "selected" : ""}
value="${fn:escapeXml(user.username)}">${fn:escapeXml(user.username)}</option>
</c:forEach>
</select>
</td>
</tr>
</table>
<p/>
<form:form method="post" action="userSettings.view" commandName="command">
<c:if test="${not command.admin}">
<table>
<tr>
<td><form:checkbox path="adminRole" id="admin" cssClass="checkbox"/></td>
<td><label for="admin"><fmt:message key="usersettings.admin"/></label></td>
</tr>
<tr>
<td><form:checkbox path="settingsRole" id="settings" cssClass="checkbox"/></td>
<td><label for="settings"><fmt:message key="usersettings.settings"/></label></td>
</tr>
<tr>
<td style="padding-top:1em"><form:checkbox path="streamRole" id="stream" cssClass="checkbox"/></td>
<td style="padding-top:1em"><label for="stream"><fmt:message key="usersettings.stream"/></label></td>
</tr>
<tr>
<td><form:checkbox path="jukeboxRole" id="jukebox" cssClass="checkbox"/></td>
<td><label for="jukebox"><fmt:message key="usersettings.jukebox"/></label></td>
</tr>
<tr>
<td><form:checkbox path="downloadRole" id="download" cssClass="checkbox"/></td>
<td><label for="download"><fmt:message key="usersettings.download"/></label></td>
</tr>
<tr>
<td><form:checkbox path="uploadRole" id="upload" cssClass="checkbox"/></td>
<td><label for="upload"><fmt:message key="usersettings.upload"/></label></td>
</tr>
<tr>
<td><form:checkbox path="shareRole" id="share" cssClass="checkbox"/></td>
<td><label for="share"><fmt:message key="usersettings.share"/></label></td>
</tr>
<tr>
<td><form:checkbox path="coverArtRole" id="coverArt" cssClass="checkbox"/></td>
<td><label for="coverArt"><fmt:message key="usersettings.coverart"/></label></td>
</tr>
<tr>
<td><form:checkbox path="commentRole" id="comment" cssClass="checkbox"/></td>
<td><label for="comment"><fmt:message key="usersettings.comment"/></label></td>
</tr>
<tr>
<td><form:checkbox path="podcastRole" id="podcast" cssClass="checkbox"/></td>
<td><label for="podcast"><fmt:message key="usersettings.podcast"/></label></td>
</tr>
</table>
</c:if>
<c:if test="${not empty command.allMusicFolders}">
<h2><fmt:message key="usersettings.folderaccess"/></h2>
<div style="width:75%">
<c:forEach items="${command.allMusicFolders}" var="musicFolder">
<span style="white-space:nowrap">
<form:checkbox path="allowedMusicFolderIds" id="musicFolder${musicFolder.id}" value="${musicFolder.id}" cssClass="checkbox"/>
<label for="musicFolder${musicFolder.id}" style="padding-right:1.5em">${musicFolder.name}</label>
</span>
</c:forEach>
</div>
</c:if>
<table class="indent">
<tr>
<td><fmt:message key="playersettings.maxbitrate"/></td>
<td>
<form:select path="transcodeSchemeName" cssStyle="width:8em">
<c:forEach items="${command.transcodeSchemeHolders}" var="transcodeSchemeHolder">
<form:option value="${transcodeSchemeHolder.name}" label="${transcodeSchemeHolder.description}"/>
</c:forEach>
</form:select>
</td>
<td><c:import url="helpToolTip.jsp"><c:param name="topic" value="transcode"/></c:import></td>
<c:if test="${not command.transcodingSupported}">
<td class="warning"><fmt:message key="playersettings.notranscoder"/></td>
</c:if>
</tr>
</table>
<c:if test="${not command.newUser and not command.admin}">
<table class="indent">
<tr>
<td><form:checkbox path="deleteUser" id="delete" cssClass="checkbox"/></td>
<td><label for="delete"><fmt:message key="usersettings.delete"/></label></td>
</tr>
</table>
</c:if>
<c:if test="${command.ldapEnabled and not command.admin}">
<table>
<tr>
<td><form:checkbox path="ldapAuthenticated" id="ldapAuthenticated" cssClass="checkbox" onclick="enablePasswordChangeFields()"/></td>
<td><label for="ldapAuthenticated"><fmt:message key="usersettings.ldap"/></label></td>
<td><c:import url="helpToolTip.jsp"><c:param name="topic" value="ldap"/></c:import></td>
</tr>
</table>
</c:if>
<c:choose>
<c:when test="${command.newUser}">
<table class="indent">
<tr>
<td><fmt:message key="usersettings.username"/></td>
<td><form:input path="username" cssStyle="width:15em"/></td>
<td class="warning"><form:errors path="username" cssStyle="width:15em"/></td>
</tr>
<tr>
<td><fmt:message key="usersettings.email"/></td>
<td><form:input path="email" cssStyle="width:15em"/></td>
<td class="warning"><form:errors path="email"/></td>
</tr>
<tr>
<td><fmt:message key="usersettings.password"/></td>
<td><form:password path="password" cssStyle="width:15em"/></td>
<td class="warning"><form:errors path="password"/></td>
</tr>
<tr>
<td><fmt:message key="usersettings.confirmpassword"/></td>
<td><form:password path="confirmPassword" cssStyle="width:15em"/></td>
<td/>
</tr>
</table>
</c:when>
<c:otherwise>
<table id="passwordChangeCheckboxTable">
<tr>
<td><form:checkbox path="passwordChange" id="passwordChange" onclick="enablePasswordChangeFields();" cssClass="checkbox"/></td>
<td><label for="passwordChange"><fmt:message key="usersettings.changepassword"/></label></td>
</tr>
</table>
<table id="passwordChangeTable" style="display:none">
<tr>
<td><fmt:message key="usersettings.newpassword"/></td>
<td><form:password path="password" id="path"/></td>
<td class="warning"><form:errors path="password"/></td>
</tr>
<tr>
<td><fmt:message key="usersettings.confirmpassword"/></td>
<td><form:password path="confirmPassword" id="confirmPassword"/></td>
<td/>
</tr>
</table>
<table>
<tr>
<td><fmt:message key="usersettings.email"/></td>
<td><form:input path="email" cssStyle="width:20em"/></td>
<td class="warning"><form:errors path="email"/></td>
</tr>
</table>
</c:otherwise>
</c:choose>
<input type="submit" value="<fmt:message key="common.save"/>" style="margin-top:1.5em;margin-right:0.3em">
<input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'" style="margin-top:1.5em">
</form:form>
</body></html>
@@ -0,0 +1,154 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
<%--
~ This file is part of Libresonic.
~
~ Libresonic is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ Libresonic is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
~
~ Copyright 2014 (C) Sindre Mehus
--%>
<%--@elvariable id="model" type="java.util.Map"--%>
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<script type="text/javascript">
var image;
var id;
var duration;
var timer;
var offset;
var step;
var size = 120;
function startPreview(img, id, duration) {
stopPreview();
image = $(img);
step = Math.max(5, Math.round(duration / 50));
offset = step;
this.id = id;
this.duration = duration;
updatePreview();
timer = window.setInterval(updatePreview, 1000);
}
function updatePreview() {
image.attr("src", "coverArt.view?id=" + id + "&size=" + size + "&offset=" + offset);
offset += step;
if (offset > duration) {
stopPreview();
}
}
function stopPreview() {
if (timer != null) {
window.clearInterval(timer);
timer = null;
}
if (image != null) {
image.attr("src", "coverArt.view?id=" + id + "&size=" + size);
}
}
</script>
<style type="text/css">
.duration {
position: absolute;
bottom: 3px;
right: 3px;
color: #d3d3d3;
background-color: black;
opacity: 0.8;
padding-right:3px;
padding-left:3px;
}
</style>
</head><body class="mainframe bgcolor1">
<h1 style="float:left">
<span style="vertical-align: middle;">
<c:forEach items="${model.ancestors}" var="ancestor">
<sub:url value="main.view" var="ancestorUrl">
<sub:param name="id" value="${ancestor.id}"/>
</sub:url>
<a href="${ancestorUrl}">${fn:escapeXml(ancestor.name)}</a> &raquo;
</c:forEach>
${fn:escapeXml(model.dir.name)}
</span>
</h1>
<%@ include file="viewSelector.jsp" %>
<div style="clear:both;padding-bottom:2em"></div>
<table class="music">
<c:forEach items="${model.subDirs}" var="subDir" varStatus="loopStatus">
<tr><td class="truncate" colspan="9"><a href="main.view?id=${subDir.id}" title="${fn:escapeXml(subDir.name)}">${fn:escapeXml(subDir.name)}</a></td></tr>
</c:forEach>
<c:if test="${model.viewAsList}">
<c:forEach items="${model.files}" var="child">
<c:url value="/videoPlayer.view" var="videoUrl">
<c:param name="id" value="${child.id}"/>
</c:url>
<tr>
<c:import url="playButtons.jsp">
<c:param name="id" value="${child.id}"/>
<c:param name="playEnabled" value="${model.user.streamRole and not model.partyMode}"/>
<c:param name="downloadEnabled" value="${model.user.downloadRole and not model.partyMode}"/>
<c:param name="video" value="${child.video and model.player.web}"/>
<c:param name="asTable" value="true"/>
</c:import>
<td class="truncate">
<a href="${videoUrl}"><span class="songTitle" title="${fn:escapeXml(child.name)}">${fn:escapeXml(child.name)}</span></a>
</td>
<td class="fit rightalign detail">${child.year}</td>
<td class="fit rightalign detail">${fn:toLowerCase(child.format)}</td>
<td class="fit rightalign detail"><sub:formatBytes bytes="${child.fileSize}"/></td>
<td class="fit rightalign detail">${child.durationString}</td>
</tr>
</c:forEach>
</c:if>
</table>
<div style="clear:both;height:1.5em"></div>
<c:if test="${not model.viewAsList}">
<c:forEach items="${model.files}" var="child">
<c:url value="/videoPlayer.view" var="videoUrl">
<c:param name="id" value="${child.id}"/>
</c:url>
<c:url value="/coverArt.view" var="coverArtUrl">
<c:param name="id" value="${child.id}"/>
<c:param name="size" value="120"/>
</c:url>
<div class="albumThumb">
<div class="coverart dropshadow" style="width:213px">
<div style="position:relative">
<div>
<a href="${videoUrl}"><img src="${coverArtUrl}" height="120" width="213" alt=""
onmouseover="startPreview(this, ${child.id}, ${child.durationSeconds})"
onmouseout="stopPreview()"></a>
</div>
<div class="detail duration">${child.durationString}</div>
</div>
<div class="caption1" title="${fn:escapeXml(child.name)}"><a href="${videoUrl}" title="${fn:escapeXml(child.name)}">${fn:escapeXml(child.name)}</a></div>
</div>
</div>
</c:forEach>
</c:if>
</body>
</html>
@@ -0,0 +1,85 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
<html>
<head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<link rel="stylesheet" type="text/css" href="<c:url value="/style/videoPlayer.css"/>">
<script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/interface/starService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/jwplayer-5.10.min.js"/>"></script>
<script type="text/javascript" src="<c:url value="/script/cast_sender-v1.js"/>"></script>
<%@ include file="videoPlayerCast.jsp" %>
<script type="text/javascript" language="javascript">
function toggleStar(mediaFileId, imageId) {
if ($(imageId).attr("src").indexOf("<spring:theme code="ratingOnImage"/>") != -1) {
$(imageId).attr("src", "<spring:theme code="ratingOffImage"/>");
starService.unstar(mediaFileId);
}
else if ($(imageId).attr("src").indexOf("<spring:theme code="ratingOffImage"/>") != -1) {
$(imageId).attr("src", "<spring:theme code="ratingOnImage"/>");
starService.star(mediaFileId);
}
}
</script>
</head>
<body class="mainframe bgcolor1" style="padding-bottom:0.5em">
<c:set var="licenseInfo" value="${model.licenseInfo}"/>
<%@ include file="licenseNotice.jsp" %>
<c:if test="${licenseInfo.licenseOrTrialValid}">
<div>
<div id="overlay">
<div id="overlay_text">Playing on Chromecast</div>
</div>
<div id="jwplayer"><a href="http://www.adobe.com/go/getflashplayer" target="_blank">Get Flash</a></div>
<div id="media_control">
<div id="progress_slider"></div>
<div id="play"></div>
<div id="pause"></div>
<div id="progress">0:00</div>
<div id="duration">0:00</div>
<div id="audio_on"></div>
<div id="audio_off"></div>
<div id="volume_slider"></div>
<select name="bitrate_menu" id="bitrate_menu">
<c:forEach items="${model.bitRates}" var="bitRate">
<c:choose>
<c:when test="${bitRate eq model.defaultBitRate}">
<option selected="selected" value="${bitRate}">${bitRate} Kbps</option>
</c:when>
<c:otherwise>
<option value="${bitRate}">${bitRate} Kbps</option>
</c:otherwise>
</c:choose>
</c:forEach>
</select>
<div id="share"></div>
<div id="download"></div>
<div id="casticonactive"></div>
<div id="casticonidle"></div>
</div>
</div>
<div id="debug"></div>
<script type="text/javascript">
var CastPlayer = new CastPlayer();
</script>
</c:if>
<h1 style="padding-top:1em;padding-bottom:0.5em;">
<img id="starImage" src="<spring:theme code="${not empty model.video.starredDate ? 'ratingOnImage' : 'ratingOffImage'}"/>"
onclick="toggleStar(${model.video.id}, '#starImage'); return false;" style="cursor:pointer" alt="">
<span style="vertical-align:middle">${fn:escapeXml(model.video.title)}</span>
</h1>
<sub:url value="main.view" var="backUrl"><sub:param name="id" value="${model.video.id}"/></sub:url>
<div class="back" style="float:left;padding-right:2em"><a href="${backUrl}"><fmt:message key="common.back"/></a></div>
<div style="clear: both"></div>
</body>
</html>
@@ -0,0 +1,707 @@
<script type="text/javascript">
(function () {
'use strict';
/**
* Constants of states for Chromecast device
**/
var DEVICE_STATE = {
'NOT_PRESENT': 0,
'IDLE': 1,
'ACTIVE': 2,
'WARNING': 3,
'ERROR': 4
};
var PLAYER_STATE = {
'IDLE': 'IDLE',
'LOADING': 'LOADING',
'LOADED': 'LOADED',
'PLAYING': 'PLAYING',
'PAUSED': 'PAUSED',
'SEEKING': 'SEEKING'
};
/**
* Cast player object
* main variables:
* - deviceState for Cast mode:
* IDLE: Default state indicating that Cast extension is installed, but showing no current activity
* ACTIVE: Shown when Chrome has one or more local activities running on a receiver
* WARNING: Shown when the device is actively being used, but when one or more issues have occurred
* ERROR: Should not normally occur, but shown when there is a failure
* - Cast player variables for controlling Cast mode media playback
* - Local player variables for controlling local mode media playbacks
* - Current media variables for transition between Cast and local modes
*/
var CastPlayer = function () {
/* device variables */
// @type {DEVICE_STATE} A state for device
this.deviceState = DEVICE_STATE.NOT_PRESENT;
/* Cast player variables */
// @type {Object} a chrome.cast.media.Media object
this.currentMediaSession = null;
// @type {Number} volume between 0 and 100
this.currentVolume = 50;
// @type {Boolean} A flag for autoplay after load
this.autoplay = true;
// @type {string} a chrome.cast.Session object
this.session = null;
// @type {PLAYER_STATE} A state for Cast media player
this.castPlayerState = PLAYER_STATE.IDLE;
/* Local player variables */
// @type {PLAYER_STATE} A state for local media player
this.localPlayerState = PLAYER_STATE.IDLE;
// @type {jwplayer} local player
this.localPlayer = null;
/* Current media variables */
// @type {Boolean} Muted audio
this.muted = false;
// @type {Number} A number for current media offset
this.currentMediaOffset = 0;
// @type {Number} A number for current media time, relative to offset
this.currentMediaTime = 0;
// @type {Number} A number for current media duration
this.currentMediaDuration = ${empty model.duration ? 0: model.duration};
// @type {Boolean} A boolean to stop timer update of progress when triggered by media status event
this.seekInProgress = false;
this.updateDurationLabel();
this.initializeUI();
this.initializeLocalPlayer();
this.initializeCastPlayer();
this.playMediaLocally(0);
};
/**
* Initialize local media player
*/
CastPlayer.prototype.initializeLocalPlayer = function () {
jwplayer("jwplayer").setup({
flashplayer: "<c:url value="/flash/jw-player-5.10.swf"/>",
height: 360,
width: 640,
skin: "<c:url value="/flash/jw-player-libresonic-skin.zip"/>",
screencolor: "000000",
controlbar: "over",
autostart: "false",
bufferlength: 3,
provider: "video",
events: {
onTime: this.updateLocalProgress.bind(this),
onPlay: this.updateLocalState.bind(this),
onPause: this.updateLocalState.bind(this),
onIdle: this.updateLocalState.bind(this)
}
});
this.localPlayer = jwplayer();
this.localPlayer.setMute(false);
this.localPlayer.setVolume(this.currentVolume);
};
CastPlayer.prototype.updateLocalProgress = function (event) {
var newTime = Math.round(event.position);
if (newTime != this.currentMediaTime && !this.seekInProgress) {
this.currentMediaTime = newTime;
this.updateProgressBar();
}
};
CastPlayer.prototype.updateLocalState = function () {
if (this.localPlayer.getState() == "PLAYING" || this.localPlayer.getState() == "BUFFERING") {
this.localPlayerState = PLAYER_STATE.PLAYING;
} else if (this.localPlayer.getState() == "PAUSED") {
this.localPlayerState = PLAYER_STATE.PAUSED;
} else if (this.localPlayer.getState() == "IDLE") {
this.localPlayerState = PLAYER_STATE.IDLE;
}
this.updateMediaControlUI();
};
/**
* Initialize Cast media player
* Initializes the API. Note that either successCallback and errorCallback will be
* invoked once the API has finished initialization. The sessionListener and
* receiverListener may be invoked at any time afterwards, and possibly more than once.
*/
CastPlayer.prototype.initializeCastPlayer = function () {
if (!window.chrome) {
return;
}
if (!chrome.cast || !chrome.cast.isAvailable) {
setTimeout(this.initializeCastPlayer.bind(this), 1000);
return;
}
// request session
var applicationID = "9EAA0B71";
var sessionRequest = new chrome.cast.SessionRequest(applicationID);
var apiConfig = new chrome.cast.ApiConfig(sessionRequest,
this.sessionListener.bind(this),
this.receiverListener.bind(this));
chrome.cast.initialize(apiConfig, this.onInitSuccess.bind(this), this.onError.bind(this));
this.timer = setInterval(this.incrementMediaTime.bind(this), 1000);
};
/**
* Callback function for init success
*/
CastPlayer.prototype.onInitSuccess = function () {
console.log("init success");
this.updateMediaControlUI();
};
/**
* Generic error callback function
*/
CastPlayer.prototype.onError = function () {
console.log("error");
};
/**
* @param {!Object} e A new session
* This handles auto-join when a page is reloaded
* When active session is detected, playback will automatically
* join existing session and occur in Cast mode and media
* status gets synced up with current media of the session
*/
CastPlayer.prototype.sessionListener = function (e) {
this.session = e;
if (this.session) {
this.deviceState = DEVICE_STATE.ACTIVE;
if (this.session.media[0]) {
this.onMediaDiscovered('activeSession', this.session.media[0]);
}
else {
this.loadMedia();
}
this.session.addUpdateListener(this.sessionUpdateListener.bind(this));
}
};
/**
* @param {string} e Receiver availability
* This indicates availability of receivers but
* does not provide a list of device IDs
*/
CastPlayer.prototype.receiverListener = function (e) {
if (e === 'available') {
console.log("receiver found");
this.deviceState = DEVICE_STATE.IDLE;
}
else {
console.log("receiver list empty");
this.deviceState = DEVICE_STATE.NOT_PRESENT;
}
this.updateMediaControlUI();
};
/**
* session update listener
*/
CastPlayer.prototype.sessionUpdateListener = function (isAlive) {
if (!isAlive) {
this.session = null;
this.deviceState = DEVICE_STATE.IDLE;
this.castPlayerState = PLAYER_STATE.IDLE;
console.log(this.castPlayerState + " (sessionUpdateListener)");
this.currentMediaSession = null;
// continue to play media locally
this.playMediaLocally(this.currentMediaOffset + this.currentMediaTime);
this.updateMediaControlUI();
}
};
/**
* Requests that a receiver application session be created or joined. By default, the SessionRequest
* passed to the API at initialization time is used; this may be overridden by passing a different
* session request in opt_sessionRequest.
*/
CastPlayer.prototype.launchApp = function () {
console.log("launching app...");
chrome.cast.requestSession(this.onRequestSessionSuccess.bind(this), this.onLaunchError.bind(this));
};
/**
* Callback function for request session success
* @param {Object} e A chrome.cast.Session object
*/
CastPlayer.prototype.onRequestSessionSuccess = function (e) {
console.log("session success: " + e.sessionId);
this.session = e;
this.deviceState = DEVICE_STATE.ACTIVE;
this.stopMediaLocally();
this.loadMedia();
this.session.addUpdateListener(this.sessionUpdateListener.bind(this));
};
/**
* Callback function for launch error
*/
CastPlayer.prototype.onLaunchError = function () {
console.log("launch error");
this.deviceState = DEVICE_STATE.ERROR;
};
/**
* Stops the running receiver application associated with the session.
*/
CastPlayer.prototype.stopApp = function () {
this.session.stop(this.onStopAppSuccess.bind(this, 'Session stopped'),
this.onError.bind(this));
};
/**
* Callback function for stop app success
*/
CastPlayer.prototype.onStopAppSuccess = function (message) {
console.log(message);
this.deviceState = DEVICE_STATE.IDLE;
this.castPlayerState = PLAYER_STATE.IDLE;
console.log(this.castPlayerState + " (onStopAppSuccess)");
this.currentMediaSession = null;
// continue to play media locally
this.playMediaLocally(this.currentMediaOffset + this.currentMediaTime);
this.updateMediaControlUI();
};
/**
* Loads media into a running receiver application
*/
CastPlayer.prototype.loadMedia = function () {
if (!this.session) {
console.log("no session");
return;
}
var offset = this.currentMediaOffset + this.currentMediaTime;
this.currentMediaOffset = offset;
this.currentMediaTime = 0;
var url = "${model.remoteStreamUrl}" + "&maxBitRate=" + this.getBitRate() + "&format=mkv&timeOffset=" + offset;
console.log("casting " + url);
var mediaInfo = new chrome.cast.media.MediaInfo(url);
mediaInfo.contentType = 'video/x-matroska';
mediaInfo.streamType = chrome.cast.media.StreamType.BUFFERED;
mediaInfo.duration = this.currentMediaDuration;
mediaInfo.metadata = new chrome.cast.media.MovieMediaMetadata();
mediaInfo.metadata.metadataType = chrome.cast.media.MetadataType.MOVIE;
mediaInfo.metadata.title = "${model.video.title}";
mediaInfo.metadata.images = [new chrome.cast.Image("${model.remoteCoverArtUrl}&size=384")];
var request = new chrome.cast.media.LoadRequest(mediaInfo);
request.autoplay = this.autoplay;
request.currentTime = 0;
this.castPlayerState = PLAYER_STATE.LOADING;
console.log(this.castPlayerState + " (loadMedia)");
this.session.loadMedia(request,
this.onMediaDiscovered.bind(this, 'loadMedia'),
this.onLoadMediaError.bind(this));
};
/**
* Callback function for loadMedia success
* @param {Object} mediaSession A new media object.
* @param {String} how How the session was discovered.
*/
CastPlayer.prototype.onMediaDiscovered = function (how, mediaSession) {
console.log("new media session ID:" + mediaSession.mediaSessionId + ' (' + how + ')');
this.currentMediaSession = mediaSession;
if (how == 'loadMedia') {
this.castPlayerState = this.castPlayerState = PLAYER_STATE.LOADED;
console.log(this.castPlayerState + " (onMediaDiscovered-loadMedia)");
}
if (how == 'activeSession') {
// TODO: Use currentMediaSession?
this.castPlayerState = this.session.media[0].playerState;
console.log(this.castPlayerState + " (onMediaDiscovered-activeSession)");
this.currentMediaTime = Math.round(this.session.media[0].currentTime);
}
this.currentMediaSession.addUpdateListener(this.onMediaStatusUpdate.bind(this));
// update UI
this.updateMediaControlUI();
};
/**
* Callback function when media load returns error
*/
CastPlayer.prototype.onLoadMediaError = function (e) {
console.log("media error");
this.castPlayerState = PLAYER_STATE.IDLE;
console.log(this.castPlayerState + " (onLoadMediaError)");
this.updateMediaControlUI();
};
/**
* Callback function for media status update from receiver
* @param {!Boolean} alive whether the media object is still alive.
*/
CastPlayer.prototype.onMediaStatusUpdate = function (alive) {
if (!alive) {
this.castPlayerState = PLAYER_STATE.IDLE;
console.log(this.castPlayerState + " (onMediaStatusUpdate-dead)");
} else {
this.castPlayerState = this.currentMediaSession.playerState;
console.log(this.castPlayerState + " (onMediaStatusUpdate)");
}
this.updateProgressBar();
this.updateMediaControlUI();
};
/**
* Helper function
* Increment media current position by 1 second
*/
CastPlayer.prototype.incrementMediaTime = function () {
if (this.castPlayerState == PLAYER_STATE.PLAYING) {
if (this.currentMediaOffset + this.currentMediaTime < this.currentMediaDuration) {
this.currentMediaTime += 1;
this.updateProgressBar();
}
}
};
/**
* Updates the duration label.
*/
CastPlayer.prototype.updateDurationLabel = function () {
$("#duration").html(this.formatDuration(this.currentMediaDuration));
};
CastPlayer.prototype.formatDuration = function (duration) {
var hours = Math.floor(duration / 3600);
duration = duration % 3600;
var minutes = Math.floor(duration / 60);
var seconds = Math.floor(duration % 60);
var result = "";
if (hours > 0) {
result += hours + ":";
if (minutes < 10) {
result += "0";
}
}
result += minutes + ":";
if (seconds < 10) {
result += "0";
}
result += seconds;
return result;
};
/**
* Play media in Cast mode
*/
CastPlayer.prototype.playMedia = function () {
if (!this.currentMediaSession) {
this.playMediaLocally(0);
return;
}
switch (this.castPlayerState) {
case PLAYER_STATE.LOADED:
case PLAYER_STATE.PAUSED:
this.currentMediaSession.play(null,
this.mediaCommandSuccessCallback.bind(this, "playing started for " + this.currentMediaSession.sessionId),
this.onError.bind(this));
this.currentMediaSession.addUpdateListener(this.onMediaStatusUpdate.bind(this));
this.castPlayerState = PLAYER_STATE.PLAYING;
console.log(this.castPlayerState + " (playMedia)");
break;
case PLAYER_STATE.IDLE:
case PLAYER_STATE.LOADING:
this.loadMedia();
this.currentMediaSession.addUpdateListener(this.onMediaStatusUpdate.bind(this));
this.castPlayerState = PLAYER_STATE.PLAYING;
console.log(this.castPlayerState + " (playMedia)");
break;
default:
break;
}
this.updateMediaControlUI();
};
/**
* Play media in local player
* @param {Number} offset A number for media current position
*/
CastPlayer.prototype.playMediaLocally = function (offset) {
if (this.localPlayerState == PLAYER_STATE.PLAYING || this.localPlayerState == PLAYER_STATE.PAUSED) {
this.localPlayer.play();
} else {
this.currentMediaOffset = offset;
this.currentMediaTime = 0;
var url = "${model.streamUrl}" + "&maxBitRate=" + this.getBitRate() + "&timeOffset=" + offset;
console.log("playing local: " + url);
this.localPlayer.load({
file: url,
duration: this.currentMediaDuration,
provider: "video"
});
this.localPlayer.play();
this.seekInProgress = false;
}
this.updateMediaControlUI();
};
CastPlayer.prototype.getBitRate = function () {
return $("#bitrate_menu").val();
};
/**
* Pause media playback in Cast mode
*/
CastPlayer.prototype.pauseMedia = function () {
if (!this.currentMediaSession) {
this.pauseMediaLocally();
return;
}
if (this.castPlayerState == PLAYER_STATE.PLAYING) {
this.castPlayerState = PLAYER_STATE.PAUSED;
console.log(this.castPlayerState + " (pauseMedia)");
this.currentMediaSession.pause(null,
this.mediaCommandSuccessCallback.bind(this, "paused " + this.currentMediaSession.sessionId),
this.onError.bind(this));
this.updateMediaControlUI();
}
};
/**
* Changes the bit rate.
*/
CastPlayer.prototype.changeBitRate = function () {
// This effectively restarts streaming with the new bit rate.
this.seekMedia();
};
/**
* Share the video.
*/
CastPlayer.prototype.share = function () {
location.href = "createShare.view?id=${model.video.id}";
};
/**
* Download the video.
*/
CastPlayer.prototype.download = function () {
location.href = "download.view?id=${model.video.id}";
};
/**
* Pause media playback in local player
*/
CastPlayer.prototype.pauseMediaLocally = function () {
this.localPlayer.pause();
this.updateMediaControlUI();
};
/**
* Stop media playback in local player
*/
CastPlayer.prototype.stopMediaLocally = function () {
this.localPlayer.stop();
this.updateMediaControlUI();
};
/**
* Set media volume in local or Cast mode
* @param {Boolean} mute A boolean
*/
CastPlayer.prototype.setVolume = function (mute) {
this.currentVolume = parseInt($("#volume_slider").slider("option", "value"));
if (!this.currentMediaSession) {
this.localPlayer.setMute(mute);
if (!mute) {
this.localPlayer.setVolume(this.currentVolume);
}
return;
}
if (mute) {
this.session.setReceiverMuted(true,
this.mediaCommandSuccessCallback.bind(this),
this.onError.bind(this));
} else {
this.session.setReceiverVolumeLevel(this.currentVolume / 100.0,
this.mediaCommandSuccessCallback.bind(this),
this.onError.bind(this));
}
this.updateMediaControlUI();
};
/**
* Toggle mute in either Cast or local mode
*/
CastPlayer.prototype.muteMedia = function () {
this.muted = !this.muted;
this.setVolume(this.muted);
$('#audio_on').toggle(!this.muted);
$('#audio_off').toggle(this.muted);
this.updateMediaControlUI();
};
/**
* media seek function in either Cast or local mode
*/
CastPlayer.prototype.seekMedia = function () {
var offset = parseInt($("#progress_slider").slider("option", "value"));
this.seekInProgress = true;
this.currentMediaOffset = offset;
this.currentMediaTime = 0;
if (this.localPlayerState == PLAYER_STATE.PLAYING || this.localPlayerState == PLAYER_STATE.PAUSED) {
this.localPlayerState = PLAYER_STATE.SEEKING;
this.playMediaLocally(offset);
return;
}
if (this.castPlayerState != PLAYER_STATE.PLAYING && this.castPlayerState != PLAYER_STATE.PAUSED) {
return;
}
this.castPlayerState = PLAYER_STATE.SEEKING;
this.loadMedia();
this.updateMediaControlUI();
};
/**
* Callback function for media command success
*/
CastPlayer.prototype.mediaCommandSuccessCallback = function (info, e) {
this.currentMediaTime = Math.round(this.session.media[0].currentTime);
if (info) {
console.log(info);
}
};
/**
* Update progress bar with the current media time.
*/
CastPlayer.prototype.updateProgressBar = function () {
$("#progress_slider").slider("option", "value", this.currentMediaOffset + this.currentMediaTime);
$("#progress").html(this.formatDuration(this.currentMediaOffset + this.currentMediaTime));
};
CastPlayer.prototype.updateDebug = function () {
var debug = "<br>currentMediaOffset: " + this.currentMediaOffset + "<br>"
+ "currentMediaTime: " + this.currentMediaTime + "<br>"
+ "localPlayerState: " + this.localPlayerState + "<br>"
+ "castPlayerState: " + this.castPlayerState;
$("#debug").html(debug);
};
/**
* Update media control UI components based on localPlayerState or castPlayerState
*/
CastPlayer.prototype.updateMediaControlUI = function () {
var playerState = this.localPlayerState;
if (this.deviceState == DEVICE_STATE.NOT_PRESENT) {
$("#casticonactive").hide();
$("#casticonidle").hide();
$("#overlay_text").hide();
var loaded = this.localPlayer.getPlaylist().length > 0;
$("#overlay").toggle(!loaded);
} else if (this.deviceState == DEVICE_STATE.ACTIVE) {
$("#casticonactive").show();
$("#casticonidle").hide();
$("#overlay_text").show();
$("#overlay").show();
playerState = this.castPlayerState;
} else {
$("#casticonactive").hide();
$("#casticonidle").show();
$("#overlay_text").hide();
var loaded = this.localPlayer.getPlaylist().length > 0;
$("#overlay").toggle(!loaded);
}
switch (playerState) {
case PLAYER_STATE.LOADED:
case PLAYER_STATE.PLAYING:
$("#play").hide();
$("#pause").show();
break;
case PLAYER_STATE.PAUSED:
case PLAYER_STATE.IDLE:
case PLAYER_STATE.LOADING:
$("#play").show();
$("#pause").hide();
break;
default:
break;
}
};
/**
* Initialize UI components and add event listeners
*/
CastPlayer.prototype.initializeUI = function () {
$("#progress_slider").slider({max: this.currentMediaDuration, animate: "fast", range: "min"});
$("#volume_slider").slider({max: 100, value: 50, animate: "fast", range: "min"});
// add event handlers to UI components
$("#casticonidle").on('click', this.launchApp.bind(this));
$("#casticonactive").on('click', this.stopApp.bind(this));
$("#progress_slider").on('slidestop', this.seekMedia.bind(this));
$("#volume_slider").on('slidestop', this.setVolume.bind(this, false));
$("#audio_on").on('click', this.muteMedia.bind(this));
$("#audio_off").on('click', this.muteMedia.bind(this));
$("#play").on('click', this.playMedia.bind(this));
$("#pause").on('click', this.pauseMedia.bind(this));
$("#bitrate_menu").on('change', this.changeBitRate.bind(this));
$("#share").on('click', this.share.bind(this));
$("#download").on('click', this.download.bind(this));
<c:if test="${not model.user.shareRole}">
$("#share").hide();
</c:if>
<c:if test="${not model.user.downloadRole}">
$("#download").hide();
</c:if>
// setInterval(this.updateDebug.bind(this), 100);
};
window.CastPlayer = CastPlayer;
})();
</script>
@@ -0,0 +1,38 @@
<%--
~ This file is part of Libresonic.
~
~ Libresonic is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ Libresonic is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
~
~ Copyright 2015 (C) Sindre Mehus
--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<div style="float:right;padding-right:1em">
<c:url value="main.view" var="changeViewUrl">
<c:param name="id" value="${model.dir.id}"/>
<c:param name="viewAsList" value="${not model.viewAsList}"/>
</c:url>
<c:choose>
<c:when test="${model.viewAsList}">
<img src="<spring:theme code="viewAsListImage"/>" alt="" class="headerSelected" style="margin-right:8px"/>
<a href="${changeViewUrl}"><img src="<spring:theme code="viewAsGridImage"/>" alt=""/></a>
</c:when>
<c:otherwise>
<a href="${changeViewUrl}"><img src="<spring:theme code="viewAsListImage"/>" alt="" style="margin-right:8px"/></a>
<img src="<spring:theme code="viewAsGridImage"/>" alt="" class="headerSelected"/>
</c:otherwise>
</c:choose>
</div>
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
<%@ page language="java" contentType="text/vnd.wap.wml; charset=utf-8" pageEncoding="iso-8859-1"%>
<wml>
<%@ include file="head.jsp" %>
<card id="main" title="Libresonic" newcontext="false">
<p><small><b>
<sub:url value="/wap/playlist.view" var="playUrl">
<sub:param name="play" value="${model.parent.path}"/>
</sub:url>
<sub:url value="/wap/playlist.view" var="addUrl">
<sub:param name="add" value="${model.parent.path}"/>
</sub:url>
<sub:url value="/wap/download.view" var="downloadUrl">
<sub:param name="path" value="${model.parent.path}"/>
</sub:url>
<c:choose>
<c:when test="${fn:length(model.children) eq 1 and model.children[0].file}">
<a href="${playUrl}">[<fmt:message key="wap.browse.playone"/>]</a><br/>
<a href="${addUrl}">[<fmt:message key="wap.browse.addone"/>]</a><br/>
<c:if test="${model.user.downloadRole}">
<a href="${downloadUrl}">[<fmt:message key="wap.browse.downloadone"/>]</a><br/>
</c:if>
</c:when>
<c:otherwise>
<a href="${playUrl}">[<fmt:message key="wap.browse.playall"/>]</a><br/>
<a href="${addUrl}">[<fmt:message key="wap.browse.addall"/>]</a><br/>
<c:if test="${model.user.downloadRole}">
<a href="${downloadUrl}">[<fmt:message key="wap.browse.downloadall"/>]</a><br/>
</c:if>
</c:otherwise>
</c:choose>
<a href="<c:url value="/wap/index.view"/>">[<fmt:message key="common.home"/>]</a><br/>
</b></small></p>
<p><small>
<c:forEach items="${model.children}" var="child">
<sub:url value="/wap/browse.view" var="browseUrl">
<sub:param name="path" value="${child.path}"/>
</sub:url>
<a href="${browseUrl}">${fn:escapeXml(child.title)}</a><br/>
</c:forEach>
</small></p>
</card>
</wml>
@@ -0,0 +1,10 @@
<%@ include file="../include.jsp" %>
<head>
<meta http-equiv="Cache-Control" content="max-age=0" forua="true"/>
<meta http-equiv="Cache-Control" content="must-revalidate" forua="true"/>
</head>
<template>
<do type="prev" name="back" label="<fmt:message key="common.back"/>"><prev/></do>
</template>
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
<%@ page language="java" contentType="text/vnd.wap.wml; charset=utf-8" pageEncoding="iso-8859-1" %>
<wml>
<%@ include file="head.jsp" %>
<card id="main" title="Libresonic" newcontext="false">
<p>
<small>
<c:choose>
<c:when test="${empty model.artists}">
<b>
<a href="<c:url value="/wap/playlist.view"/>">[<fmt:message key="wap.index.playlist"/>]</a>
</b>
<br/>
<b>
<a href="<c:url value="/wap/search.view"/>">[<fmt:message key="wap.index.search"/>]</a>
</b>
<br/>
<b>
<a href="<c:url value="/wap/settings.view"/>">[<fmt:message key="wap.index.settings"/>]</a>
</b>
<br/>
</small>
</p>
<p>
<small>
<c:forEach items="${model.indexes}" var="index">
<sub:url var="url" value="/wap/index.view">
<sub:param name="index" value="${index.index}"/>
</sub:url>
<a href="${url}">${index.index}</a>
</c:forEach>
</c:when>
<c:otherwise>
<c:forEach items="${model.artists}" var="artist">
<c:forEach items="${artist.musicFiles}" var="mediaFile">
<sub:url var="url" value="/wap/browse.view">
<sub:param name="path" value="${mediaFile.path}"/>
</sub:url>
<a href="${url}">${fn:escapeXml(mediaFile.title)}</a>
<br/>
</c:forEach>
</c:forEach>
</c:otherwise>
</c:choose>
<c:if test="${model.noMusic}">
<fmt:message key="wap.index.missing"/>
</c:if>
</small>
</p>
</card>
</wml>
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
<%@ page language="java" contentType="text/vnd.wap.wml; charset=utf-8" pageEncoding="iso-8859-1"%>
<wml>
<%@ include file="head.jsp" %>
<card id="main" title="Libresonic" newcontext="false">
<p><small>
<c:forEach items="${model.playlists}" var="playlist">
<sub:url var="url" value="/wap/playlist.view">
<sub:param name="load" value="${playlist.id}"/>
</sub:url>
<b><a href="${url}">${fn:escapeXml(playlist.name)}</a></b><br/>
</c:forEach>
</small></p>
</card>
</wml>
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
<%@ page language="java" contentType="text/vnd.wap.wml; charset=utf-8" pageEncoding="iso-8859-1"%>
<wml>
<%@ include file="head.jsp" %>
<c:if test="${fn:length(model.players) eq 1}">
<c:choose>
<c:when test="${empty model.players[0].name}">
<c:set var="playerName" value=" - Player ${model.players[0].id}"/>
</c:when>
<c:otherwise>
<c:set var="playerName" value=" - ${model.players[0].name}"/>
</c:otherwise>
</c:choose>
</c:if>
<card id="main" title="Libresonic" newcontext="false">
<p><small><b><fmt:message key="wap.playlist.title"/>${playerName}</b></small></p>
<p><small>
<c:choose>
<c:when test="${empty model.players}">
<fmt:message key="wap.playlist.noplayer"/>
</c:when>
<c:otherwise>
<b><a href="<c:url value="/wap/index.view"/>">[<fmt:message key="common.home"/>]</a></b><br/>
<b><a href="<c:url value="/wap/loadPlaylist.view"/>">[<fmt:message key="wap.playlist.load"/>]</a></b><br/>
<b><a href="<c:url value="/wap/playlist.view?random"/>">[<fmt:message key="wap.playlist.random"/>]</a></b><br/>
<c:set var="playlist" value="${model.players[0].playlist}"/>
<c:if test="${not empty playlist.files}">
<b><a href="<c:url value="/play.m3u"/>">[<fmt:message key="wap.playlist.play"/>]</a></b><br/>
<b><a href="<c:url value="/wap/playlist.view?clear"/>">[<fmt:message key="wap.playlist.clear"/>]</a></b><br/>
</small></p>
<p><small>
<c:forEach items="${playlist.files}" var="file" varStatus="loopStatus">
<c:set var="isCurrent" value="${(file eq playlist.currentFile) and (loopStatus.count - 1 eq playlist.index)}"/>
${isCurrent ? "<b>" : ""}
<a href="<c:url value="/wap/playlist.view?skip=${loopStatus.count - 1}"/>">${fn:escapeXml(file.title)}</a>
${isCurrent ? "</b>" : ""}
<br/>
</c:forEach>
</c:if>
</c:otherwise>
</c:choose>
</small></p>
</card>
</wml>
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
<%@ page language="java" contentType="text/vnd.wap.wml; charset=utf-8" pageEncoding="iso-8859-1"%>
<wml>
<%@ include file="head.jsp" %>
<card id="main" title="Libresonic" newcontext="false">
<p>
<input name="query" value="" size="10"/>
<anchor><fmt:message key="wap.search.title"/>
<go href="<c:url value="/wap/searchResult.view"/>" method="get">
<postfield name="query" value="$query"/>
</go>
</anchor>
</p>
</card>
</wml>
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
<%@ page language="java" contentType="text/vnd.wap.wml; charset=utf-8" pageEncoding="iso-8859-1"%>
<wml>
<%@ include file="head.jsp" %>
<card id="main" title="Libresonic" newcontext="false">
<p><small>
<c:choose>
<c:when test="${model.creatingIndex}">
<fmt:message key="wap.searchresult.index"/>
</c:when>
<c:otherwise>
<c:forEach items="${model.hits}" var="hit">
<sub:url var="url" value="/wap/browse.view">
<sub:param name="path" value="${hit.path}"/>
</sub:url>
<a href="${url}">${fn:escapeXml(hit.title)}</a><br/>
</c:forEach>
</c:otherwise>
</c:choose>
</small></p>
</card>
</wml>
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
<%@ page language="java" contentType="text/vnd.wap.wml; charset=utf-8" pageEncoding="iso-8859-1"%>
<wml>
<%@ include file="head.jsp" %>
<card id="main" title="Libresonic" newcontext="false">
<p><small>
<b><a href="<c:url value="/wap/index.view"/>">[<fmt:message key="common.home"/>]</a><br/></b>
<b><a href="#player">[<fmt:message key="wap.settings.selectplayer"/>]</a></b>
</small></p>
</card>
<card id="player" title="Libresonic" newcontext="false">
<p><small>
<b><a href="<c:url value="/wap/index.view"/>">[<fmt:message key="common.home"/>]</a><br/></b>
</small></p><p><small>
<c:choose>
<c:when test="${empty model.playerId}">
<fmt:message key="wap.settings.allplayers"/>
</c:when>
<c:otherwise>
<a href="<c:url value="/wap/selectPlayer.view"/>"><fmt:message key="wap.settings.allplayers"/></a>
</c:otherwise>
</c:choose>
<br/>
<c:forEach items="${model.players}" var="player">
<c:choose>
<c:when test="${player.id eq model.playerId}">
${player}
</c:when>
<c:otherwise>
<a href="<c:url value="/wap/selectPlayer.view?playerId=${player.id}"/>">${player}</a>
</c:otherwise>
</c:choose>
<br/>
</c:forEach>
</small></p>
</card>
</wml>
@@ -0,0 +1,512 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="leftController" class="org.libresonic.player.controller.LeftController">
<property name="viewName" value="left"/>
<property name="mediaScannerService" ref="mediaScannerService"/>
<property name="securityService" ref="securityService"/>
<property name="settingsService" ref="settingsService"/>
<property name="musicIndexService" ref="musicIndexService"/>
<property name="playerService" ref="playerService"/>
</bean>
<bean id="rightController" class="org.libresonic.player.controller.RightController">
<property name="viewName" value="right"/>
<property name="securityService" ref="securityService"/>
<property name="settingsService" ref="settingsService"/>
<property name="versionService" ref="versionService"/>
</bean>
<bean id="statusController" class="org.libresonic.player.controller.StatusController">
<property name="viewName" value="status"/>
<property name="statusService" ref="statusService"/>
</bean>
<bean id="mainController" class="org.libresonic.player.controller.MainController">
<property name="securityService" ref="securityService"/>
<property name="playerService" ref="playerService"/>
<property name="settingsService" ref="settingsService"/>
<property name="ratingService" ref="ratingService"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="adService" ref="adService"/>
</bean>
<bean id="playlistController" class="org.libresonic.player.controller.PlaylistController">
<property name="viewName" value="playlist"/>
<property name="securityService" ref="securityService"/>
<property name="playlistService" ref="playlistService"/>
<property name="settingsService" ref="settingsService"/>
<property name="playerService" ref="playerService"/>
</bean>
<bean id="playlistsController" class="org.libresonic.player.controller.PlaylistsController">
<property name="viewName" value="playlists"/>
<property name="securityService" ref="securityService"/>
<property name="playlistService" ref="playlistService"/>
</bean>
<bean id="importPlaylistController" class="org.libresonic.player.controller.ImportPlaylistController">
<property name="viewName" value="importPlaylist"/>
<property name="securityService" ref="securityService"/>
<property name="playlistService" ref="playlistService"/>
</bean>
<bean id="topController" class="org.libresonic.player.controller.TopController">
<property name="viewName" value="top"/>
<property name="settingsService" ref="settingsService"/>
<property name="securityService" ref="securityService"/>
</bean>
<bean id="helpController" class="org.libresonic.player.controller.HelpController">
<property name="viewName" value="help"/>
<property name="versionService" ref="versionService"/>
<property name="settingsService" ref="settingsService"/>
<property name="securityService" ref="securityService"/>
</bean>
<bean id="moreController" class="org.libresonic.player.controller.MoreController">
<property name="viewName" value="more"/>
<property name="settingsService" ref="settingsService"/>
<property name="securityService" ref="securityService"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="playerService" ref="playerService"/>
</bean>
<bean id="uploadController" class="org.libresonic.player.controller.UploadController">
<property name="viewName" value="upload"/>
<property name="securityService" ref="securityService"/>
<property name="statusService" ref="statusService"/>
<property name="playerService" ref="playerService"/>
<property name="settingsService" ref="settingsService"/>
</bean>
<bean id="lyricsController" class="org.libresonic.player.controller.LyricsController">
<property name="viewName" value="lyrics"/>
</bean>
<bean id="allmusicController" class="org.libresonic.player.controller.AllmusicController">
<property name="viewName" value="allmusic"/>
</bean>
<bean id="podcastController" class="org.libresonic.player.controller.PodcastController">
<property name="viewName" value="podcast"/>
<property name="playlistService" ref="playlistService"/>
<property name="settingsService" ref="settingsService"/>
<property name="securityService" ref="securityService"/>
</bean>
<bean id="podcastChannelsController" class="org.libresonic.player.controller.PodcastChannelsController">
<property name="viewName" value="podcastChannels"/>
<property name="podcastService" ref="podcastService"/>
<property name="securityService" ref="securityService"/>
<property name="settingsService" ref="settingsService"/>
</bean>
<bean id="podcastChannelController" class="org.libresonic.player.controller.PodcastChannelController">
<property name="viewName" value="podcastChannel"/>
<property name="podcastService" ref="podcastService"/>
<property name="securityService" ref="securityService"/>
</bean>
<bean id="podcastReceiverAdminController" class="org.libresonic.player.controller.PodcastReceiverAdminController">
<property name="podcastService" ref="podcastService"/>
</bean>
<bean id="setMusicFileInfoController" class="org.libresonic.player.controller.SetMusicFileInfoController">
<property name="mediaFileService" ref="mediaFileService"/>
</bean>
<bean id="shareManagementController" class="org.libresonic.player.controller.ShareManagementController">
<property name="settingsService" ref="settingsService"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="shareService" ref="shareService"/>
<property name="playerService" ref="playerService"/>
<property name="playlistService" ref="playlistService"/>
<property name="securityService" ref="securityService"/>
</bean>
<bean id="setRatingController" class="org.libresonic.player.controller.SetRatingController">
<property name="ratingService" ref="ratingService"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="securityService" ref="securityService"/>
</bean>
<bean id="randomPlayQueueController" class="org.libresonic.player.controller.RandomPlayQueueController">
<property name="viewName" value="reload"/>
<property name="playerService" ref="playerService"/>
<property name="searchService" ref="searchService"/>
<property name="securityService" ref="securityService"/>
<property name="settingsService" ref="settingsService"/>
<property name="reloadFrames">
<list>
<bean class="org.libresonic.player.controller.ReloadFrame">
<property name="frame" value="playQueue"/>
<property name="view" value="playQueue.view?"/>
</bean>
<bean class="org.libresonic.player.controller.ReloadFrame">
<property name="frame" value="main"/>
<property name="view" value="more.view"/>
</bean>
</list>
</property>
</bean>
<bean id="changeCoverArtController" class="org.libresonic.player.controller.ChangeCoverArtController">
<property name="viewName" value="changeCoverArt"/>
<property name="mediaFileService" ref="mediaFileService"/>
</bean>
<bean id="nowPlayingController" class="org.libresonic.player.controller.NowPlayingController">
<property name="playerService" ref="playerService"/>
<property name="statusService" ref="statusService"/>
<property name="mediaFileService" ref="mediaFileService"/>
</bean>
<bean id="starredController" class="org.libresonic.player.controller.StarredController">
<property name="viewName" value="starred"/>
<property name="playerService" ref="playerService"/>
<property name="securityService" ref="securityService"/>
<property name="settingsService" ref="settingsService"/>
<property name="mediaFileDao" ref="mediaFileDao"/>
<property name="mediaFileService" ref="mediaFileService"/>
</bean>
<bean id="searchController" class="org.libresonic.player.controller.SearchController">
<property name="commandClass" value="org.libresonic.player.command.SearchCommand"/>
<property name="successView" value="search"/>
<property name="formView" value="search"/>
<property name="searchService" ref="searchService"/>
<property name="securityService" ref="securityService"/>
<property name="settingsService" ref="settingsService"/>
<property name="playerService" ref="playerService"/>
</bean>
<bean id="settingsController" class="org.libresonic.player.controller.SettingsController">
<property name="securityService" ref="securityService"/>
</bean>
<bean id="playerSettingsController" class="org.libresonic.player.controller.PlayerSettingsController">
<property name="commandClass" value="org.libresonic.player.command.PlayerSettingsCommand"/>
<property name="successView" value="playerSettings"/>
<property name="formView" value="playerSettings"/>
<property name="playerService" ref="playerService"/>
<property name="securityService" ref="securityService"/>
<property name="transcodingService" ref="transcodingService"/>
</bean>
<bean id="dlnaSettingsController" class="org.libresonic.player.controller.DLNASettingsController">
<property name="viewName" value="dlnaSettings"/>
<property name="upnpService" ref="upnpService"/>
<property name="settingsService" ref="settingsService"/>
</bean>
<bean id="sonosSettingsController" class="org.libresonic.player.controller.SonosSettingsController">
<property name="viewName" value="sonosSettings"/>
<property name="sonosService" ref="sonosService"/>
<property name="settingsService" ref="settingsService"/>
</bean>
<bean id="shareSettingsController" class="org.libresonic.player.controller.ShareSettingsController">
<property name="viewName" value="shareSettings"/>
<property name="shareService" ref="shareService"/>
<property name="securityService" ref="securityService"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="settingsService" ref="settingsService"/>
</bean>
<bean id="musicFolderSettingsController" class="org.libresonic.player.controller.MusicFolderSettingsController">
<property name="commandClass" value="org.libresonic.player.command.MusicFolderSettingsCommand"/>
<property name="successView" value="musicFolderSettings"/>
<property name="formView" value="musicFolderSettings"/>
<property name="settingsService" ref="settingsService"/>
<property name="mediaScannerService" ref="mediaScannerService"/>
<property name="artistDao" ref="artistDao"/>
<property name="albumDao" ref="albumDao"/>
<property name="mediaFileDao" ref="mediaFileDao"/>
</bean>
<bean id="networkSettingsController" class="org.libresonic.player.controller.NetworkSettingsController">
<property name="commandClass" value="org.libresonic.player.command.NetworkSettingsCommand"/>
<property name="successView" value="networkSettings"/>
<property name="formView" value="networkSettings"/>
<property name="settingsService" ref="settingsService"/>
<property name="networkService" ref="networkService"/>
</bean>
<bean id="transcodingSettingsController" class="org.libresonic.player.controller.TranscodingSettingsController">
<property name="viewName" value="transcodingSettings"/>
<property name="transcodingService" ref="transcodingService"/>
<property name="settingsService" ref="settingsService"/>
</bean>
<bean id="internetRadioSettingsController"
class="org.libresonic.player.controller.InternetRadioSettingsController">
<property name="viewName" value="internetRadioSettings"/>
<property name="settingsService" ref="settingsService"/>
</bean>
<bean id="podcastSettingsController" class="org.libresonic.player.controller.PodcastSettingsController">
<property name="commandClass" value="org.libresonic.player.command.PodcastSettingsCommand"/>
<property name="successView" value="podcastSettings"/>
<property name="formView" value="podcastSettings"/>
<property name="settingsService" ref="settingsService"/>
<property name="podcastService" ref="podcastService"/>
</bean>
<bean id="generalSettingsController" class="org.libresonic.player.controller.GeneralSettingsController">
<property name="commandClass" value="org.libresonic.player.command.GeneralSettingsCommand"/>
<property name="successView" value="generalSettings"/>
<property name="formView" value="generalSettings"/>
<property name="settingsService" ref="settingsService"/>
</bean>
<bean id="advancedSettingsController" class="org.libresonic.player.controller.AdvancedSettingsController">
<property name="commandClass" value="org.libresonic.player.command.AdvancedSettingsCommand"/>
<property name="successView" value="advancedSettings"/>
<property name="formView" value="advancedSettings"/>
<property name="settingsService" ref="settingsService"/>
</bean>
<bean id="personalSettingsController" class="org.libresonic.player.controller.PersonalSettingsController">
<property name="commandClass" value="org.libresonic.player.command.PersonalSettingsCommand"/>
<property name="successView" value="personalSettings"/>
<property name="formView" value="personalSettings"/>
<property name="settingsService" ref="settingsService"/>
<property name="securityService" ref="securityService"/>
</bean>
<bean id="avatarUploadController" class="org.libresonic.player.controller.AvatarUploadController">
<property name="viewName" value="avatarUploadResult"/>
<property name="settingsService" ref="settingsService"/>
<property name="securityService" ref="securityService"/>
</bean>
<bean id="userSettingsController" class="org.libresonic.player.controller.UserSettingsController">
<property name="sessionForm" value="true"/>
<property name="commandClass" value="org.libresonic.player.command.UserSettingsCommand"/>
<property name="validator" ref="userSettingsValidator"/>
<property name="successView" value="userSettings"/>
<property name="formView" value="userSettings"/>
<property name="securityService" ref="securityService"/>
<property name="settingsService" ref="settingsService"/>
<property name="transcodingService" ref="transcodingService"/>
</bean>
<bean id="passwordSettingsController" class="org.libresonic.player.controller.PasswordSettingsController">
<property name="sessionForm" value="true"/>
<property name="commandClass" value="org.libresonic.player.command.PasswordSettingsCommand"/>
<property name="validator" ref="passwordSettingsValidator"/>
<property name="successView" value="passwordSettings"/>
<property name="formView" value="passwordSettings"/>
<property name="securityService" ref="securityService"/>
</bean>
<bean id="homeController" class="org.libresonic.player.controller.HomeController">
<property name="viewName" value="home"/>
<property name="ratingService" ref="ratingService"/>
<property name="mediaScannerService" ref="mediaScannerService"/>
<property name="settingsService" ref="settingsService"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="searchService" ref="searchService"/>
<property name="securityService" ref="securityService"/>
</bean>
<bean id="editTagsController" class="org.libresonic.player.controller.EditTagsController">
<property name="viewName" value="editTags"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="metaDataParserFactory" ref="metaDataParserFactory"/>
</bean>
<bean id="playQueueController" class="org.libresonic.player.controller.PlayQueueController">
<property name="viewName" value="playQueue"/>
<property name="playerService" ref="playerService"/>
<property name="securityService" ref="securityService"/>
<property name="settingsService" ref="settingsService"/>
</bean>
<bean id="coverArtController" class="org.libresonic.player.controller.CoverArtController" init-method="init">
<property name="mediaFileService" ref="mediaFileService"/>
<property name="transcodingService" ref="transcodingService"/>
<property name="settingsService" ref="settingsService"/>
<property name="playlistService" ref="playlistService"/>
<property name="podcastService" ref="podcastService"/>
<property name="artistDao" ref="artistDao"/>
<property name="albumDao" ref="albumDao"/>
</bean>
<bean id="avatarController" class="org.libresonic.player.controller.AvatarController">
<property name="settingsService" ref="settingsService"/>
</bean>
<bean id="proxyController" class="org.libresonic.player.controller.ProxyController"/>
<bean id="statusChartController" class="org.libresonic.player.controller.StatusChartController">
<property name="statusService" ref="statusService"/>
</bean>
<bean id="userChartController" class="org.libresonic.player.controller.UserChartController">
<property name="securityService" ref="securityService"/>
</bean>
<bean id="m3uController" class="org.libresonic.player.controller.M3UController">
<property name="playerService" ref="playerService"/>
<property name="settingsService" ref="settingsService"/>
<property name="transcodingService" ref="transcodingService"/>
</bean>
<bean id="streamController" class="org.libresonic.player.controller.StreamController">
<property name="playerService" ref="playerService"/>
<property name="playlistService" ref="playlistService"/>
<property name="statusService" ref="statusService"/>
<property name="securityService" ref="securityService"/>
<property name="settingsService" ref="settingsService"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="searchService" ref="searchService"/>
<property name="transcodingService" ref="transcodingService"/>
<property name="audioScrobblerService" ref="audioScrobblerService"/>
</bean>
<bean id="hlsController" class="org.libresonic.player.controller.HLSController">
<property name="playerService" ref="playerService"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="securityService" ref="securityService"/>
</bean>
<bean id="videoPlayerController" class="org.libresonic.player.controller.VideoPlayerController">
<property name="viewName" value="videoPlayer"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="settingsService" ref="settingsService"/>
<property name="playerService" ref="playerService"/>
<property name="securityService" ref="securityService"/>
</bean>
<bean id="externalPlayerController" class="org.libresonic.player.controller.ExternalPlayerController">
<property name="viewName" value="externalPlayer"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="settingsService" ref="settingsService"/>
<property name="playerService" ref="playerService"/>
<property name="shareService" ref="shareService"/>
</bean>
<bean id="downloadController" class="org.libresonic.player.controller.DownloadController">
<property name="playerService" ref="playerService"/>
<property name="statusService" ref="statusService"/>
<property name="securityService" ref="securityService"/>
<property name="playlistService" ref="playlistService"/>
<property name="settingsService" ref="settingsService"/>
<property name="mediaFileService" ref="mediaFileService"/>
</bean>
<bean id="premiumSettingsController" class="org.libresonic.player.controller.PremiumSettingsController">
<property name="commandClass" value="org.libresonic.player.command.PremiumSettingsCommand"/>
<property name="successView" value="premiumSettings"/>
<property name="formView" value="premiumSettings"/>
<property name="validator" ref="premiumSettingsValidator"/>
<property name="settingsService" ref="settingsService"/>
<property name="securityService" ref="securityService"/>
</bean>
<bean id="multiController" class="org.libresonic.player.controller.MultiController">
<property name="securityService" ref="securityService"/>
<property name="settingsService" ref="settingsService"/>
<property name="playlistService" ref="playlistService"/>
</bean>
<bean id="wapController" class="org.libresonic.player.controller.WapController">
<property name="settingsService" ref="settingsService"/>
<property name="playerService" ref="playerService"/>
<property name="playlistService" ref="playlistService"/>
<property name="searchService" ref="searchService"/>
<property name="securityService" ref="securityService"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="musicIndexService" ref="musicIndexService"/>
</bean>
<bean id="restController" class="org.libresonic.player.controller.RESTController" init-method="init">
<property name="settingsService" ref="settingsService"/>
<property name="securityService" ref="securityService"/>
<property name="playerService" ref="playerService"/>
<property name="mediaFileService" ref="mediaFileService"/>
<property name="lastFmService" ref="lastFmService"/>
<property name="musicIndexService" ref="musicIndexService"/>
<property name="transcodingService" ref="transcodingService"/>
<property name="statusService" ref="statusService"/>
<property name="searchService" ref="searchService"/>
<property name="jukeboxService" ref="jukeboxService"/>
<property name="audioScrobblerService" ref="audioScrobblerService"/>
<property name="playlistService" ref="playlistService"/>
<property name="playQueueService" ref="ajaxPlayQueueService"/>
<property name="ratingService" ref="ratingService"/>
<property name="chatService" ref="ajaxChatService"/>
<property name="lyricsService" ref="ajaxLyricsService"/>
<property name="podcastService" ref="podcastService"/>
<property name="shareService" ref="shareService"/>
<property name="mediaFileDao" ref="mediaFileDao"/>
<property name="artistDao" ref="artistDao"/>
<property name="albumDao" ref="albumDao"/>
<property name="bookmarkDao" ref="bookmarkDao"/>
<property name="playQueueDao" ref="playQueueDao"/>
<property name="downloadController" ref="downloadController"/>
<property name="streamController" ref="streamController"/>
<property name="hlsController" ref="hlsController"/>
<property name="coverArtController" ref="coverArtController"/>
<property name="avatarController" ref="avatarController"/>
<property name="userSettingsController" ref="userSettingsController"/>
<property name="leftController" ref="leftController"/>
</bean>
<bean id="dbController" class="org.libresonic.player.controller.DBController">
<property name="viewName" value="db"/>
<property name="daoHelper" ref="daoHelper"/>
</bean>
<bean id="premiumSettingsValidator" class="org.libresonic.player.validator.PremiumSettingsValidator">
<property name="settingsService" ref="settingsService"/>
</bean>
<bean id="userSettingsValidator" class="org.libresonic.player.validator.UserSettingsValidator">
<property name="securityService" ref="securityService"/>
<property name="settingsService" ref="settingsService"/>
</bean>
<bean id="passwordSettingsValidator" class="org.libresonic.player.validator.PasswordSettingsValidator"/>
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="alwaysUseFullPath" value="true"/>
<property name="mappings">
<props>
<prop key="/main.view">mainController</prop>
<prop key="/playlist.view">playlistController</prop>
<prop key="/playlists.view">playlistsController</prop>
<prop key="/help.view">helpController</prop>
<prop key="/lyrics.view">lyricsController</prop>
<prop key="/left.view">leftController</prop>
<prop key="/right.view">rightController</prop>
<prop key="/status.view">statusController</prop>
<prop key="/more.view">moreController</prop>
<prop key="/upload.view">uploadController</prop>
<prop key="/importPlaylist.view">importPlaylistController</prop>
<prop key="/exportPlaylist.view">multiController</prop>
<prop key="/setMusicFileInfo.view">setMusicFileInfoController</prop>
<prop key="/createShare.view">shareManagementController</prop>
<prop key="/setRating.view">setRatingController</prop>
<prop key="/top.view">topController</prop>
<prop key="/randomPlayQueue.view">randomPlayQueueController</prop>
<prop key="/changeCoverArt.view">changeCoverArtController</prop>
<prop key="/login.view">multiController</prop>
<prop key="/recover.view">multiController</prop>
<prop key="/accessDenied.view">multiController</prop>
<prop key="/notFound.view">multiController</prop>
<prop key="/gettingStarted.view">multiController</prop>
<prop key="/index.view">multiController</prop>
<prop key="/videoPlayer.view">videoPlayerController</prop>
<prop key="/nowPlaying.view">nowPlayingController</prop>
<prop key="/starred.view">starredController</prop>
<prop key="/search.view">searchController</prop>
<prop key="/settings.view">settingsController</prop>
<prop key="/playerSettings.view">playerSettingsController</prop>
<prop key="/dlnaSettings.view">dlnaSettingsController</prop>
<prop key="/sonosSettings.view">sonosSettingsController</prop>
<prop key="/shareSettings.view">shareSettingsController</prop>
<prop key="/musicFolderSettings.view">musicFolderSettingsController</prop>
<prop key="/networkSettings.view">networkSettingsController</prop>
<prop key="/transcodingSettings.view">transcodingSettingsController</prop>
<prop key="/internetRadioSettings.view">internetRadioSettingsController</prop>
<prop key="/podcastSettings.view">podcastSettingsController</prop>
<prop key="/generalSettings.view">generalSettingsController</prop>
<prop key="/advancedSettings.view">advancedSettingsController</prop>
<prop key="/personalSettings.view">personalSettingsController</prop>
<prop key="/avatarUpload.view">avatarUploadController</prop>
<prop key="/userSettings.view">userSettingsController</prop>
<prop key="/passwordSettings.view">passwordSettingsController</prop>
<prop key="/premiumSettings.view">premiumSettingsController</prop>
<prop key="/allmusic.view">allmusicController</prop>
<prop key="/home.view">homeController</prop>
<prop key="/editTags.view">editTagsController</prop>
<prop key="/playQueue.view">playQueueController</prop>
<prop key="/coverArt.view">coverArtController</prop>
<prop key="/avatar.view">avatarController</prop>
<prop key="/proxy.view">proxyController</prop>
<prop key="/statusChart.view">statusChartController</prop>
<prop key="/userChart.view">userChartController</prop>
<prop key="/download.view">downloadController</prop>
<prop key="/db.view">dbController</prop>
<prop key="/test.view">multiController</prop>
<prop key="/podcastChannels.view">podcastChannelsController</prop>
<prop key="/podcastChannel.view">podcastChannelController</prop>
<prop key="/podcastReceiverAdmin.view">podcastReceiverAdminController</prop>
<prop key="/podcast.view">podcastController</prop>
<prop key="/podcast/**">podcastController</prop>
<prop key="/wap/download.view">downloadController</prop>
<prop key="/wap/**">wapController</prop>
<prop key="/rest/**">restController</prop>
<prop key="/play.m3u">m3uController</prop>
<prop key="/stream/**">streamController</prop>
<prop key="/hls/**">hlsController</prop>
<prop key="/share/**">externalPlayerController</prop>
</props>
</property>
</bean>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="org.libresonic.player.i18n.ResourceBundle"/>
</bean>
<bean id="themeSource" class="org.libresonic.player.theme.LibresonicThemeSource">
<property name="basenamePrefix" value="org.libresonic.player.theme."/>
<property name="settingsService" ref="settingsService"/>
</bean>
<bean id="themeResolver" class="org.libresonic.player.theme.LibresonicThemeResolver">
<property name="securityService" ref="securityService"/>
<property name="settingsService" ref="settingsService"/>
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
@@ -0,0 +1,114 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<description>Libresonic tag library</description>
<display-name>Libresonic tag library</display-name>
<tlib-version>1.1</tlib-version>
<short-name>sub</short-name>
<uri>http://libresonic.org/taglib/sub</uri>
<tag>
<description>
Creates a URL with optional query parameters. Similar to 'c:url', but
you may specify which character encoding to use for the URL query
parameters. If no encoding is specified, the following steps are performed:
a) Parameter values are encoded as the hexadecimal representation of the UTF-8 bytes of the original string.
b) Parameter names are prepended with the suffix "Utf8Hex"
</description>
<name>url</name>
<tag-class>org.libresonic.player.taglib.UrlTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<description>
Name of the exported scoped variable for the
processed url. The type of the scoped variable is
String.
</description>
<name>var</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<description>URL to be processed.</description>
<name>value</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<description>The encoding to use. Default is ISO-8859-1.</description>
<name>encoding</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<description>Adds a parameter to a containing 'url' tag.</description>
<name>param</name>
<tag-class>org.libresonic.player.taglib.ParamTag</tag-class>
<body-content>empty</body-content>
<attribute>
<description>Name of the query string parameter.</description>
<name>name</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<description>Value of the parameter.</description>
<name>value</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<description>
Converts a byte-count to a formatted string suitable for display to the user, with respect
to the current locale.
</description>
<name>formatBytes</name>
<tag-class>org.libresonic.player.taglib.FormatBytesTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<description>The byte count.</description>
<name>bytes</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<description>
Renders a Wiki text with markup to HTML, using the Radeox render engine.
</description>
<name>wiki</name>
<tag-class>org.libresonic.player.taglib.WikiTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<description>The Wiki markup text.</description>
<name>text</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<description>
Escapes the characters in a string using JavaScript rules.
</description>
<name>escapeJavaScript</name>
<tag-class>org.libresonic.player.taglib.EscapeJavaScriptTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<description>The string to escape.</description>
<name>string</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
@@ -0,0 +1,224 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app id="libresonic" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Libresonic Music Streamer</display-name>
<!-- Location of application context. Used by ContextLoaderListener. -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext-service.xml
/WEB-INF/applicationContext-security.xml
/WEB-INF/applicationContext-cache.xml
/WEB-INF/applicationContext-sonos.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>net.sf.ehcache.constructs.web.ShutdownListener</listener-class>
</listener>
<servlet>
<servlet-name>libresonic</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<display-name>CFX Servlet</display-name>
<servlet-name>cxfservlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<display-name>DWR Servlet</display-name>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
<init-param>
<param-name>crossDomainSessionSecurity</param-name>
<param-value>false</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>libresonic</servlet-name>
<url-pattern>*.view</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>libresonic</servlet-name>
<url-pattern>/podcast</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>libresonic</servlet-name>
<url-pattern>/wap</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>libresonic</servlet-name>
<url-pattern>/play.m3u</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>libresonic</servlet-name>
<url-pattern>/stream/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>libresonic</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>libresonic</servlet-name>
<url-pattern>/hls/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>libresonic</servlet-name>
<url-pattern>/share/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>cxfservlet</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/error.jsp</location>
</error-page>
<filter>
<filter-name>BootstrapVerificationFilter</filter-name>
<filter-class>org.libresonic.player.filter.BootstrapVerificationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>BootstrapVerificationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>ParameterDecodingFilter</filter-name>
<filter-class>org.libresonic.player.filter.ParameterDecodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ParameterDecodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>RESTFilter</filter-name>
<filter-class>org.libresonic.player.filter.RESTFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>RESTFilter</filter-name>
<url-pattern>/rest/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>RequestEncodingFilter</filter-name>
<filter-class>org.libresonic.player.filter.RequestEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>RequestEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<description>Sets HTTP headers to enable browser caching.</description>
<filter-name>CacheFilter</filter-name>
<filter-class>org.libresonic.player.filter.ResponseHeaderFilter</filter-class>
<init-param>
<param-name>Cache-Control</param-name>
<param-value>max-age=36000</param-value>
</init-param>
</filter>
<filter>
<description>Sets HTTP headers to disable browser caching.</description>
<filter-name>NoCacheFilter</filter-name>
<filter-class>org.libresonic.player.filter.ResponseHeaderFilter</filter-class>
<init-param>
<param-name>Cache-Control</param-name>
<param-value>no-cache, post-check=0, pre-check=0</param-value>
</init-param>
<init-param>
<param-name>Pragma</param-name>
<param-value>no-cache</param-value>
</init-param>
<init-param>
<param-name>Expires</param-name>
<param-value>Thu, 01 Dec 1994 16:00:00 GMT</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CacheFilter</filter-name>
<url-pattern>/icons/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CacheFilter</filter-name>
<url-pattern>/style/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>NoCacheFilter</filter-name>
<url-pattern>/statusChart.view</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>NoCacheFilter</filter-name>
<url-pattern>/userChart.view</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>NoCacheFilter</filter-name>
<url-pattern>/playQueue.view</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>NoCacheFilter</filter-name>
<url-pattern>/podcastChannels.view</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>NoCacheFilter</filter-name>
<url-pattern>/podcastChannel.view</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>NoCacheFilter</filter-name>
<url-pattern>/help.view</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>NoCacheFilter</filter-name>
<url-pattern>/top.view</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>NoCacheFilter</filter-name>
<url-pattern>/home.view</url-pattern>
</filter-mapping>
<filter>
<filter-name>AcegiFilter</filter-name>
<filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>org.acegisecurity.util.FilterChainProxy</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>AcegiFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
@@ -0,0 +1,5 @@
<html>
<body topmargin=0 leftmargin=0 marginheight=0 marginwidth=0>
<script type='text/javascript'>amazon_ad_tag = 'libresonic-20'; amazon_ad_width = '120'; amazon_ad_height = '240'; amazon_ad_link_target = 'new';</script><script type='text/javascript' src='http://www.assoc-amazon.com/s/ads.js'></script>
</body>
</html>
@@ -0,0 +1,6 @@
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-access-from domain="*" />
</cross-domain-policy>
+51
View File
@@ -0,0 +1,51 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" isErrorPage="true" %>
<%@ page import="java.io.PrintWriter, java.io.StringWriter"%>
<html><head>
<!--[if lt IE 7.]>
<script defer type="text/javascript" src="script/pngfix.js"></script>
<![endif]-->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="style/default.css" type="text/css"/>
</head>
<body>
<h1>
<img src="icons/default_light/error.png" alt=""/>
<span style="vertical-align: middle">Error</span>
</h1>
<p>
Libresonic encountered an internal error. You can report this error in the
<a href="http://forum.libresonic.org" target="_blank">Libresonic Forum</a>.
Please include the information below.
</p>
<%
StringWriter sw = new StringWriter();
exception.printStackTrace(new PrintWriter(sw));
long totalMemory = Runtime.getRuntime().totalMemory();
long freeMemory = Runtime.getRuntime().freeMemory();
long usedMemory = totalMemory - freeMemory;
%>
<table class="ruleTable indent">
<tr><td class="ruleTableHeader">Exception</td>
<td class="ruleTableCell"><%=exception.getClass().getName()%></td></tr>
<tr><td class="ruleTableHeader">Message</td>
<td class="ruleTableCell"><%=exception.getMessage()%></td></tr>
<tr><td class="ruleTableHeader">Java version</td>
<td class="ruleTableCell"><%=System.getProperty("java.vendor") + ' ' + System.getProperty("java.version")%></td></tr>
<tr><td class="ruleTableHeader">Operating system</td>
<td class="ruleTableCell"><%=System.getProperty("os.name") + ' ' + System.getProperty("os.version")%></td></tr>
<tr><td class="ruleTableHeader">Server</td>
<td class="ruleTableCell"><%=application.getServerInfo()%></td></tr>
<tr><td class="ruleTableHeader">Memory</td>
<td class="ruleTableCell">Used <%=usedMemory/1024L/1024L%> of <%=totalMemory/1024L/1024L%> MB</td></tr>
<tr><td class="ruleTableHeader" style="vertical-align:top;">Stack trace</td>
<td class="ruleTableCell" style="white-space:pre"><%=sw.getBuffer()%></td></tr>
</table>
</body>
</html>
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 976 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1009 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1020 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 B

Some files were not shown because too many files have changed in this diff Show More