diff --git a/.circleci/config.yml b/.circleci/config.yml index 2c414fc1..e2b2238e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -27,7 +27,6 @@ jobs: command: | ./gradlew ciTest testDebugUnitTest ./gradlew jacocoFullReport - bash <(curl -s https://codecov.io/bash) - run: name: lint command: ./gradlew :ultrasonic:lintRelease diff --git a/README.md b/README.md index 30e14864..4596e859 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ See [CONTRIBUTING](CONTRIBUTING.md). - [Subsonic](http://www.subsonic.org/pages/index.jsp) - [Airsonic](https://github.com/airsonic/airsonic) - [Supysonic](https://github.com/spl0k/supysonic) +- [Ampache](https://ampache.org/) Other *Subsonic API* implementations should work as well as long as they follow API [documentation](http://www.subsonic.org/pages/api.jsp). diff --git a/dependencies.gradle b/dependencies.gradle index b9742fd6..ea05210e 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -25,7 +25,7 @@ ext.versions = [ kotlinxCoroutines : "1.3.9", viewModelKtx : "2.2.0", - retrofit : "2.4.0", + retrofit : "2.6.4", jackson : "2.9.5", okhttp : "3.12.13", semver : "1.0.0", diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/EditServerFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/EditServerFragment.kt index 9b6c031e..12519813 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/EditServerFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/EditServerFragment.kt @@ -320,7 +320,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler { // Execute a ping to retrieve the API version. // This is accepted to fail if the authentication is incorrect yet. var pingResponse = subsonicApiClient.api.ping().execute() - if (pingResponse?.body() != null) { + if (pingResponse.body() != null) { val restApiVersion = pingResponse.body()!!.version.restApiVersion currentServerSetting!!.minimumApiVersion = restApiVersion Timber.i("Server minimum API version set to %s", restApiVersion) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SelectAlbumFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SelectAlbumFragment.kt index bc059dfa..63694ff9 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SelectAlbumFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SelectAlbumFragment.kt @@ -93,7 +93,6 @@ class SelectAlbumFragment : Fragment() { private var cancellationToken: CancellationToken? = null private val activeServerProvider: ActiveServerProvider by inject() - private val serverSettingsModel: ServerSettingsModel by viewModel() private val model: SelectAlbumModel by viewModels() private val random: Random = SecureRandom() @@ -133,6 +132,7 @@ class SelectAlbumFragment : Fragment() { requireContext(), view as ViewGroup ) { selectedFolderId -> if (!isOffline(context)) { + val serverSettingsModel: ServerSettingsModel by viewModel() val currentSetting = activeServerProvider.getActiveServer() currentSetting.musicFolderId = selectedFolderId serverSettingsModel.updateItem(currentSetting) @@ -230,31 +230,32 @@ class SelectAlbumFragment : Fragment() { } private fun updateDisplay(refresh: Boolean) { - val id = requireArguments().getString(Constants.INTENT_EXTRA_NAME_ID) - val isAlbum = requireArguments().getBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, false) - val name = requireArguments().getString(Constants.INTENT_EXTRA_NAME_NAME) - val parentId = requireArguments().getString(Constants.INTENT_EXTRA_NAME_PARENT_ID) - val playlistId = requireArguments().getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID) - val podcastChannelId = requireArguments().getString( + val args = requireArguments() + val id = args.getString(Constants.INTENT_EXTRA_NAME_ID) + val isAlbum = args.getBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, false) + val name = args.getString(Constants.INTENT_EXTRA_NAME_NAME) + val parentId = args.getString(Constants.INTENT_EXTRA_NAME_PARENT_ID) + val playlistId = args.getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID) + val podcastChannelId = args.getString( Constants.INTENT_EXTRA_NAME_PODCAST_CHANNEL_ID ) - val playlistName = requireArguments().getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME) - val shareId = requireArguments().getString(Constants.INTENT_EXTRA_NAME_SHARE_ID) - val shareName = requireArguments().getString(Constants.INTENT_EXTRA_NAME_SHARE_NAME) - val albumListType = requireArguments().getString( + val playlistName = args.getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME) + val shareId = args.getString(Constants.INTENT_EXTRA_NAME_SHARE_ID) + val shareName = args.getString(Constants.INTENT_EXTRA_NAME_SHARE_NAME) + val albumListType = args.getString( Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE ) - val genreName = requireArguments().getString(Constants.INTENT_EXTRA_NAME_GENRE_NAME) - val albumListTitle = requireArguments().getInt( + val genreName = args.getString(Constants.INTENT_EXTRA_NAME_GENRE_NAME) + val albumListTitle = args.getInt( Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE, 0 ) - val getStarredTracks = requireArguments().getInt(Constants.INTENT_EXTRA_NAME_STARRED, 0) - val getVideos = requireArguments().getInt(Constants.INTENT_EXTRA_NAME_VIDEOS, 0) - val getRandomTracks = requireArguments().getInt(Constants.INTENT_EXTRA_NAME_RANDOM, 0) - val albumListSize = requireArguments().getInt( + val getStarredTracks = args.getInt(Constants.INTENT_EXTRA_NAME_STARRED, 0) + val getVideos = args.getInt(Constants.INTENT_EXTRA_NAME_VIDEOS, 0) + val getRandomTracks = args.getInt(Constants.INTENT_EXTRA_NAME_RANDOM, 0) + val albumListSize = args.getInt( Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0 ) - val albumListOffset = requireArguments().getInt( + val albumListOffset = args.getInt( Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0 ) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SelectAlbumModel.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SelectAlbumModel.kt index 1cbdc1c6..7c402bf8 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SelectAlbumModel.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SelectAlbumModel.kt @@ -54,50 +54,49 @@ class SelectAlbumModel(application: Application) : AndroidViewModel(application) parentId: String? ) { withContext(Dispatchers.IO) { - if (!ActiveServerProvider.isOffline(context)) { - val service = MusicServiceFactory.getMusicService(context) - var root = MusicDirectory() + val service = MusicServiceFactory.getMusicService(context) - if (allSongsId == id) { - val musicDirectory = service.getMusicDirectory( - parentId, name, refresh, context - ) + var root = MusicDirectory() - val songs: MutableList = LinkedList() - getSongsRecursively(musicDirectory, songs) + if (allSongsId == id) { + val musicDirectory = service.getMusicDirectory( + parentId, name, refresh, context + ) - for (song in songs) { - if (!song.isDirectory) { - root.addChild(song) - } - } - } else { - val musicDirectory = service.getMusicDirectory(id, name, refresh, context) + val songs: MutableList = LinkedList() + getSongsRecursively(musicDirectory, songs) - if (Util.getShouldShowAllSongsByArtist(context) && - musicDirectory.findChild(allSongsId) == null && - hasOnlyFolders(musicDirectory) - ) { - val allSongs = MusicDirectory.Entry() - - allSongs.isDirectory = true - allSongs.artist = name - allSongs.parent = id - allSongs.id = allSongsId - allSongs.title = String.format( - context.resources.getString(R.string.select_album_all_songs), name - ) - - root.addChild(allSongs) - root.addAll(musicDirectory.getChildren()) - } else { - root = musicDirectory + for (song in songs) { + if (!song.isDirectory) { + root.addChild(song) } } + } else { + val musicDirectory = service.getMusicDirectory(id, name, refresh, context) - currentDirectory.postValue(root) + if (Util.getShouldShowAllSongsByArtist(context) && + musicDirectory.findChild(allSongsId) == null && + hasOnlyFolders(musicDirectory) + ) { + val allSongs = MusicDirectory.Entry() + + allSongs.isDirectory = true + allSongs.artist = name + allSongs.parent = id + allSongs.id = allSongsId + allSongs.title = String.format( + context.resources.getString(R.string.select_album_all_songs), name + ) + + root.addChild(allSongs) + root.addAll(musicDirectory.getChildren()) + } else { + root = musicDirectory + } } + + currentDirectory.postValue(root) } } @@ -128,107 +127,99 @@ class SelectAlbumModel(application: Application) : AndroidViewModel(application) suspend fun getArtist(refresh: Boolean, id: String?, name: String?) { withContext(Dispatchers.IO) { - if (!ActiveServerProvider.isOffline(context)) { - val service = MusicServiceFactory.getMusicService(context) + val service = MusicServiceFactory.getMusicService(context) - var root = MusicDirectory() + var root = MusicDirectory() - val musicDirectory = service.getArtist(id, name, refresh, context) + val musicDirectory = service.getArtist(id, name, refresh, context) - if (Util.getShouldShowAllSongsByArtist(context) && - musicDirectory.findChild(allSongsId) == null && - hasOnlyFolders(musicDirectory) - ) { - val allSongs = MusicDirectory.Entry() + if (Util.getShouldShowAllSongsByArtist(context) && + musicDirectory.findChild(allSongsId) == null && + hasOnlyFolders(musicDirectory) + ) { + val allSongs = MusicDirectory.Entry() - allSongs.isDirectory = true - allSongs.artist = name - allSongs.parent = id - allSongs.id = allSongsId - allSongs.title = String.format( - context.resources.getString(R.string.select_album_all_songs), name - ) + allSongs.isDirectory = true + allSongs.artist = name + allSongs.parent = id + allSongs.id = allSongsId + allSongs.title = String.format( + context.resources.getString(R.string.select_album_all_songs), name + ) - root.addFirst(allSongs) - root.addAll(musicDirectory.getChildren()) - } else { - root = musicDirectory - } - currentDirectory.postValue(root) + root.addFirst(allSongs) + root.addAll(musicDirectory.getChildren()) + } else { + root = musicDirectory } + currentDirectory.postValue(root) } } suspend fun getAlbum(refresh: Boolean, id: String?, name: String?, parentId: String?) { withContext(Dispatchers.IO) { - if (!ActiveServerProvider.isOffline(context)) { - val service = MusicServiceFactory.getMusicService(context) + val service = MusicServiceFactory.getMusicService(context) - val musicDirectory: MusicDirectory + val musicDirectory: MusicDirectory - musicDirectory = if (allSongsId == id) { - val root = MusicDirectory() + musicDirectory = if (allSongsId == id) { + val root = MusicDirectory() - val songs: MutableCollection = LinkedList() - val artist = service.getArtist(parentId, "", false, context) + val songs: MutableCollection = LinkedList() + val artist = service.getArtist(parentId, "", false, context) - for ((id1) in artist.getChildren()) { - if (allSongsId != id1) { - val albumDirectory = service.getAlbum( - id1, "", false, context - ) + for ((id1) in artist.getChildren()) { + if (allSongsId != id1) { + val albumDirectory = service.getAlbum( + id1, "", false, context + ) - for (song in albumDirectory.getChildren()) { - if (!song.isVideo) { - songs.add(song) - } + for (song in albumDirectory.getChildren()) { + if (!song.isVideo) { + songs.add(song) } } } - - for (song in songs) { - if (!song.isDirectory) { - root.addChild(song) - } - } - root - } else { - service.getAlbum(id, name, refresh, context) } - currentDirectory.postValue(musicDirectory) + + for (song in songs) { + if (!song.isDirectory) { + root.addChild(song) + } + } + root + } else { + service.getAlbum(id, name, refresh, context) } + currentDirectory.postValue(musicDirectory) } } suspend fun getSongsForGenre(genre: String, count: Int, offset: Int) { withContext(Dispatchers.IO) { - if (!ActiveServerProvider.isOffline(context)) { - val service = MusicServiceFactory.getMusicService(context) - val musicDirectory = service.getSongsByGenre(genre, count, offset, context) - songsForGenre.postValue(musicDirectory) - } + val service = MusicServiceFactory.getMusicService(context) + val musicDirectory = service.getSongsByGenre(genre, count, offset, context) + songsForGenre.postValue(musicDirectory) } } suspend fun getStarred() { withContext(Dispatchers.IO) { - if (!ActiveServerProvider.isOffline(context)) { - val service = MusicServiceFactory.getMusicService(context) - val musicDirectory: MusicDirectory - val context = context + val service = MusicServiceFactory.getMusicService(context) + val musicDirectory: MusicDirectory + val context = context - if (Util.getShouldUseId3Tags(context)) { - musicDirectory = Util.getSongsFromSearchResult(service.getStarred2(context)) - } else { - musicDirectory = Util.getSongsFromSearchResult(service.getStarred(context)) - } - - currentDirectory.postValue(musicDirectory) + if (Util.getShouldUseId3Tags(context)) { + musicDirectory = Util.getSongsFromSearchResult(service.getStarred2(context)) + } else { + musicDirectory = Util.getSongsFromSearchResult(service.getStarred(context)) } + + currentDirectory.postValue(musicDirectory) } } @@ -236,68 +227,58 @@ class SelectAlbumModel(application: Application) : AndroidViewModel(application) showHeader = false withContext(Dispatchers.IO) { - if (!ActiveServerProvider.isOffline(context)) { - val service = MusicServiceFactory.getMusicService(context) - currentDirectory.postValue(service.getVideos(refresh, context)) - } + val service = MusicServiceFactory.getMusicService(context) + currentDirectory.postValue(service.getVideos(refresh, context)) } } suspend fun getRandom(size: Int) { withContext(Dispatchers.IO) { - if (!ActiveServerProvider.isOffline(context)) { - val service = MusicServiceFactory.getMusicService(context) - val musicDirectory = service.getRandomSongs(size, context) + val service = MusicServiceFactory.getMusicService(context) + val musicDirectory = service.getRandomSongs(size, context) - currentDirectoryIsSortable = false - currentDirectory.postValue(musicDirectory) - } + currentDirectoryIsSortable = false + currentDirectory.postValue(musicDirectory) } } suspend fun getPlaylist(playlistId: String, playlistName: String?) { withContext(Dispatchers.IO) { - if (!ActiveServerProvider.isOffline(context)) { - val service = MusicServiceFactory.getMusicService(context) - val musicDirectory = service.getPlaylist(playlistId, playlistName, context) + val service = MusicServiceFactory.getMusicService(context) + val musicDirectory = service.getPlaylist(playlistId, playlistName, context) - currentDirectory.postValue(musicDirectory) - } + currentDirectory.postValue(musicDirectory) } } suspend fun getPodcastEpisodes(podcastChannelId: String) { withContext(Dispatchers.IO) { - if (!ActiveServerProvider.isOffline(context)) { - val service = MusicServiceFactory.getMusicService(context) - val musicDirectory = service.getPodcastEpisodes(podcastChannelId, context) - currentDirectory.postValue(musicDirectory) - } + val service = MusicServiceFactory.getMusicService(context) + val musicDirectory = service.getPodcastEpisodes(podcastChannelId, context) + currentDirectory.postValue(musicDirectory) } } suspend fun getShare(shareId: String) { withContext(Dispatchers.IO) { - if (!ActiveServerProvider.isOffline(context)) { - val service = MusicServiceFactory.getMusicService(context) - val musicDirectory = MusicDirectory() + val service = MusicServiceFactory.getMusicService(context) + val musicDirectory = MusicDirectory() - val shares = service.getShares(true, context) + val shares = service.getShares(true, context) - for (share in shares) { - if (share.id == shareId) { - for (entry in share.getEntries()) { - musicDirectory.addChild(entry) - } - break + for (share in shares) { + if (share.id == shareId) { + for (entry in share.getEntries()) { + musicDirectory.addChild(entry) } + break } - currentDirectory.postValue(musicDirectory) } + currentDirectory.postValue(musicDirectory) } } @@ -311,30 +292,28 @@ class SelectAlbumModel(application: Application) : AndroidViewModel(application) ) withContext(Dispatchers.IO) { - if (!ActiveServerProvider.isOffline(context)) { - val service = MusicServiceFactory.getMusicService(context) - val musicDirectory: MusicDirectory - val musicFolderId = if (showSelectFolderHeader) { - activeServerProvider.getActiveServer().musicFolderId - } else { - null - } - - if (Util.getShouldUseId3Tags(context)) { - musicDirectory = service.getAlbumList2( - albumListType, size, - offset, musicFolderId, context - ) - } else { - musicDirectory = service.getAlbumList( - albumListType, size, - offset, musicFolderId, context - ) - } - - currentDirectoryIsSortable = sortableCollection(albumListType) - albumList.postValue(musicDirectory) + val service = MusicServiceFactory.getMusicService(context) + val musicDirectory: MusicDirectory + val musicFolderId = if (showSelectFolderHeader) { + activeServerProvider.getActiveServer().musicFolderId + } else { + null } + + if (Util.getShouldUseId3Tags(context)) { + musicDirectory = service.getAlbumList2( + albumListType, size, + offset, musicFolderId, context + ) + } else { + musicDirectory = service.getAlbumList( + albumListType, size, + offset, musicFolderId, context + ) + } + + currentDirectoryIsSortable = sortableCollection(albumListType) + albumList.postValue(musicDirectory) } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/ApiCallResponseChecker.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/ApiCallResponseChecker.kt index 74baeec5..51bd4b0a 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/ApiCallResponseChecker.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/ApiCallResponseChecker.kt @@ -26,7 +26,7 @@ class ApiCallResponseChecker( if (activeServerProvider.getActiveServer().minimumApiVersion == null) { try { val response = subsonicAPIClient.api.ping().execute() - if (response?.body() != null) { + if (response.body() != null) { val restApiVersion = response.body()!!.version.restApiVersion Timber.i("Server minimum API version set to %s", restApiVersion) activeServerProvider.setMinimumApiVersion(restApiVersion)