mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2025-05-08 11:31:07 +03:00
Store loaded indexes in persistent storage.
Signed-off-by: Yahor Berdnikau <egorr.berd@gmail.com>
This commit is contained in:
parent
63715aab18
commit
423461c3ba
60
cache/src/main/kotlin/org/moire/ultrasonic/cache/serializers/ArtistSerializer.kt
vendored
Normal file
60
cache/src/main/kotlin/org/moire/ultrasonic/cache/serializers/ArtistSerializer.kt
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
@file:JvmMultifileClass
|
||||||
|
@file:JvmName("DomainSerializers")
|
||||||
|
package org.moire.ultrasonic.cache.serializers
|
||||||
|
|
||||||
|
import com.twitter.serial.serializer.CollectionSerializers
|
||||||
|
import com.twitter.serial.serializer.ObjectSerializer
|
||||||
|
import com.twitter.serial.serializer.SerializationContext
|
||||||
|
import com.twitter.serial.stream.SerializerDefs
|
||||||
|
import com.twitter.serial.stream.SerializerInput
|
||||||
|
import com.twitter.serial.stream.SerializerOutput
|
||||||
|
import org.moire.ultrasonic.domain.Artist
|
||||||
|
|
||||||
|
private const val SERIALIZER_VERSION = 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializer/deserializer for [Artist] domain entity.
|
||||||
|
*/
|
||||||
|
val artistSerializer get() = object : ObjectSerializer<Artist>(SERIALIZER_VERSION) {
|
||||||
|
override fun serializeObject(
|
||||||
|
context: SerializationContext,
|
||||||
|
output: SerializerOutput<out SerializerOutput<*>>,
|
||||||
|
item: Artist
|
||||||
|
) {
|
||||||
|
output.writeString(item.id)
|
||||||
|
.writeString(item.name)
|
||||||
|
.writeString(item.index)
|
||||||
|
.writeString(item.coverArt)
|
||||||
|
.apply {
|
||||||
|
val albumCount = item.albumCount
|
||||||
|
if (albumCount != null) writeLong(albumCount) else writeNull()
|
||||||
|
}
|
||||||
|
.writeInt(item.closeness)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserializeObject(
|
||||||
|
context: SerializationContext,
|
||||||
|
input: SerializerInput,
|
||||||
|
versionNumber: Int
|
||||||
|
): Artist? {
|
||||||
|
if (versionNumber != SERIALIZER_VERSION) return null
|
||||||
|
|
||||||
|
val id = input.readString()
|
||||||
|
val name = input.readString()
|
||||||
|
val index = input.readString()
|
||||||
|
val coverArt = input.readString()
|
||||||
|
val albumCount = if (input.peekType() == SerializerDefs.TYPE_NULL) {
|
||||||
|
input.readNull()
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
input.readLong()
|
||||||
|
}
|
||||||
|
val closeness = input.readInt()
|
||||||
|
return Artist(id, name, index, coverArt, albumCount, closeness)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializer/deserializer for list of [Artist] domain entities.
|
||||||
|
*/
|
||||||
|
val artistListSerializer = CollectionSerializers.getListSerializer(artistSerializer)
|
44
cache/src/main/kotlin/org/moire/ultrasonic/cache/serializers/IndexesSerializer.kt
vendored
Normal file
44
cache/src/main/kotlin/org/moire/ultrasonic/cache/serializers/IndexesSerializer.kt
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
@file:JvmMultifileClass
|
||||||
|
@file:JvmName("DomainSerializers")
|
||||||
|
package org.moire.ultrasonic.cache.serializers
|
||||||
|
|
||||||
|
import com.twitter.serial.serializer.CollectionSerializers
|
||||||
|
import com.twitter.serial.serializer.ObjectSerializer
|
||||||
|
import com.twitter.serial.serializer.SerializationContext
|
||||||
|
import com.twitter.serial.stream.SerializerInput
|
||||||
|
import com.twitter.serial.stream.SerializerOutput
|
||||||
|
import org.moire.ultrasonic.domain.Artist
|
||||||
|
import org.moire.ultrasonic.domain.Indexes
|
||||||
|
|
||||||
|
private const val SERIALIZATION_VERSION = 1
|
||||||
|
|
||||||
|
val indexesSerializer get() = object : ObjectSerializer<Indexes>(SERIALIZATION_VERSION) {
|
||||||
|
override fun serializeObject(
|
||||||
|
context: SerializationContext,
|
||||||
|
output: SerializerOutput<out SerializerOutput<*>>,
|
||||||
|
item: Indexes
|
||||||
|
) {
|
||||||
|
output.writeLong(item.lastModified)
|
||||||
|
.writeString(item.ignoredArticles)
|
||||||
|
.writeObject<MutableList<Artist>>(context, item.shortcuts,
|
||||||
|
CollectionSerializers.getListSerializer(artistSerializer))
|
||||||
|
.writeObject<MutableList<Artist>>(context, item.artists,
|
||||||
|
CollectionSerializers.getListSerializer(artistSerializer))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserializeObject(
|
||||||
|
context: SerializationContext,
|
||||||
|
input: SerializerInput,
|
||||||
|
versionNumber: Int
|
||||||
|
): Indexes? {
|
||||||
|
if (versionNumber != SERIALIZATION_VERSION) return null
|
||||||
|
|
||||||
|
val lastModified = input.readLong()
|
||||||
|
val ignoredArticles = input.readString() ?: return null
|
||||||
|
val shortcutsList = input.readObject(context,
|
||||||
|
CollectionSerializers.getListSerializer(artistSerializer)) ?: return null
|
||||||
|
val artistsList = input.readObject(context,
|
||||||
|
CollectionSerializers.getListSerializer(artistSerializer)) ?: return null
|
||||||
|
return Indexes(lastModified, ignoredArticles, shortcutsList, artistsList)
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package org.moire.ultrasonic.cache
|
package org.moire.ultrasonic.cache
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
import com.nhaarman.mockito_kotlin.mock
|
||||||
|
import com.twitter.serial.util.SerializationUtils
|
||||||
import org.amshove.kluent.`it returns`
|
import org.amshove.kluent.`it returns`
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
@ -31,4 +32,9 @@ abstract class BaseStorageTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected val storageDir get() = File(mockDirectories.getInternalDataDir(), STORAGE_DIR_NAME)
|
protected val storageDir get() = File(mockDirectories.getInternalDataDir(), STORAGE_DIR_NAME)
|
||||||
|
|
||||||
|
protected fun validateSerializedData(index: Int = 0) {
|
||||||
|
val serializedFileBytes = storageDir.listFiles()[index].readBytes()
|
||||||
|
SerializationUtils.validateSerializedData(serializedFileBytes)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
57
cache/src/test/kotlin/org/moire/ultrasonic/cache/serializers/ArtistSerializerTest.kt
vendored
Normal file
57
cache/src/test/kotlin/org/moire/ultrasonic/cache/serializers/ArtistSerializerTest.kt
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package org.moire.ultrasonic.cache.serializers
|
||||||
|
|
||||||
|
import org.amshove.kluent.`should equal`
|
||||||
|
import org.junit.Test
|
||||||
|
import org.moire.ultrasonic.cache.BaseStorageTest
|
||||||
|
import org.moire.ultrasonic.domain.Artist
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Artist] serializers test.
|
||||||
|
*/
|
||||||
|
class ArtistSerializerTest : BaseStorageTest() {
|
||||||
|
@Test
|
||||||
|
fun `Should correctly serialize Artist object`() {
|
||||||
|
val item = Artist("id", "name", "index", "coverArt", 1, 0)
|
||||||
|
|
||||||
|
storage.store("some-name", item, artistSerializer)
|
||||||
|
|
||||||
|
validateSerializedData()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Should correctly deserialize Artist object`() {
|
||||||
|
val itemName = "some-name"
|
||||||
|
val item = Artist("id", "name", "index", "coverArt", null, 0)
|
||||||
|
storage.store(itemName, item, artistSerializer)
|
||||||
|
|
||||||
|
val loadedItem = storage.load(itemName, artistSerializer)
|
||||||
|
|
||||||
|
loadedItem `should equal` item
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Should correctly serialize list of Artists`() {
|
||||||
|
val itemsList = listOf(
|
||||||
|
Artist(id = "1"),
|
||||||
|
Artist(id = "2", name = "some")
|
||||||
|
)
|
||||||
|
|
||||||
|
storage.store("some-name", itemsList, artistListSerializer)
|
||||||
|
|
||||||
|
validateSerializedData()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Should correctly deserialize list of Artists`() {
|
||||||
|
val name = "some-name"
|
||||||
|
val itemsList = listOf(
|
||||||
|
Artist(id = "1"),
|
||||||
|
Artist(id = "2", name = "some")
|
||||||
|
)
|
||||||
|
storage.store(name, itemsList, artistListSerializer)
|
||||||
|
|
||||||
|
val loadedItems = storage.load(name, artistListSerializer)
|
||||||
|
|
||||||
|
loadedItems `should equal` itemsList
|
||||||
|
}
|
||||||
|
}
|
40
cache/src/test/kotlin/org/moire/ultrasonic/cache/serializers/IndexesSerializerTest.kt
vendored
Normal file
40
cache/src/test/kotlin/org/moire/ultrasonic/cache/serializers/IndexesSerializerTest.kt
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package org.moire.ultrasonic.cache.serializers
|
||||||
|
|
||||||
|
import org.amshove.kluent.`should equal`
|
||||||
|
import org.junit.Test
|
||||||
|
import org.moire.ultrasonic.cache.BaseStorageTest
|
||||||
|
import org.moire.ultrasonic.domain.Artist
|
||||||
|
import org.moire.ultrasonic.domain.Indexes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test [Indexes] domain entity serializer.
|
||||||
|
*/
|
||||||
|
class IndexesSerializerTest : BaseStorageTest() {
|
||||||
|
@Test
|
||||||
|
fun `Should correctly serialize Indexes object`() {
|
||||||
|
val item = Indexes(220L, "", mutableListOf(
|
||||||
|
Artist("12")
|
||||||
|
), mutableListOf(
|
||||||
|
Artist("233", "some")
|
||||||
|
))
|
||||||
|
|
||||||
|
storage.store("some-name", item, indexesSerializer)
|
||||||
|
|
||||||
|
validateSerializedData()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Should correctly deserialize Indexes object`() {
|
||||||
|
val name = "some-name"
|
||||||
|
val item = Indexes(220L, "", mutableListOf(
|
||||||
|
Artist("12")
|
||||||
|
), mutableListOf(
|
||||||
|
Artist("233", "some")
|
||||||
|
))
|
||||||
|
storage.store(name, item, indexesSerializer)
|
||||||
|
|
||||||
|
val loadedItem = storage.load(name, indexesSerializer)
|
||||||
|
|
||||||
|
loadedItem `should equal` item
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package org.moire.ultrasonic.cache.serializers
|
package org.moire.ultrasonic.cache.serializers
|
||||||
|
|
||||||
import com.twitter.serial.util.SerializationUtils
|
|
||||||
import org.amshove.kluent.`should equal`
|
import org.amshove.kluent.`should equal`
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.moire.ultrasonic.cache.BaseStorageTest
|
import org.moire.ultrasonic.cache.BaseStorageTest
|
||||||
@ -16,8 +15,7 @@ class MusicFolderSerializerTest : BaseStorageTest() {
|
|||||||
|
|
||||||
storage.store("some-name", item, musicFolderSerializer)
|
storage.store("some-name", item, musicFolderSerializer)
|
||||||
|
|
||||||
val serializedFileBytes = storageDir.listFiles()[0].readBytes()
|
validateSerializedData()
|
||||||
SerializationUtils.validateSerializedData(serializedFileBytes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -40,8 +38,7 @@ class MusicFolderSerializerTest : BaseStorageTest() {
|
|||||||
|
|
||||||
storage.store("some-name", itemsList, musicFolderListSerializer)
|
storage.store("some-name", itemsList, musicFolderListSerializer)
|
||||||
|
|
||||||
val serializedFileBytes = storageDir.listFiles()[0].readBytes()
|
validateSerializedData()
|
||||||
SerializationUtils.validateSerializedData(serializedFileBytes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -120,6 +120,7 @@ public class RESTMusicService implements MusicService {
|
|||||||
private static final String TAG = RESTMusicService.class.getSimpleName();
|
private static final String TAG = RESTMusicService.class.getSimpleName();
|
||||||
|
|
||||||
private static final String MUSIC_FOLDER_STORAGE_NAME = "music_folder";
|
private static final String MUSIC_FOLDER_STORAGE_NAME = "music_folder";
|
||||||
|
private static final String INDEXES_STORAGE_NAME = "indexes";
|
||||||
|
|
||||||
private final SubsonicAPIClient subsonicAPIClient;
|
private final SubsonicAPIClient subsonicAPIClient;
|
||||||
private final PermanentFileStorage fileStorage;
|
private final PermanentFileStorage fileStorage;
|
||||||
@ -155,8 +156,8 @@ public class RESTMusicService implements MusicService {
|
|||||||
public List<MusicFolder> getMusicFolders(boolean refresh,
|
public List<MusicFolder> getMusicFolders(boolean refresh,
|
||||||
Context context,
|
Context context,
|
||||||
ProgressListener progressListener) throws Exception {
|
ProgressListener progressListener) throws Exception {
|
||||||
List<MusicFolder> cachedMusicFolders = fileStorage
|
List<MusicFolder> cachedMusicFolders = fileStorage.load(MUSIC_FOLDER_STORAGE_NAME,
|
||||||
.load(MUSIC_FOLDER_STORAGE_NAME, DomainSerializers.getMusicFolderListSerializer());
|
DomainSerializers.getMusicFolderListSerializer());
|
||||||
if (cachedMusicFolders != null && !refresh) {
|
if (cachedMusicFolders != null && !refresh) {
|
||||||
return cachedMusicFolders;
|
return cachedMusicFolders;
|
||||||
}
|
}
|
||||||
@ -177,7 +178,8 @@ public class RESTMusicService implements MusicService {
|
|||||||
boolean refresh,
|
boolean refresh,
|
||||||
Context context,
|
Context context,
|
||||||
ProgressListener progressListener) throws Exception {
|
ProgressListener progressListener) throws Exception {
|
||||||
Indexes cachedIndexes = readCachedIndexes(context, musicFolderId);
|
Indexes cachedIndexes = fileStorage.load(INDEXES_STORAGE_NAME,
|
||||||
|
DomainSerializers.getIndexesSerializer());
|
||||||
if (cachedIndexes != null && !refresh) {
|
if (cachedIndexes != null && !refresh) {
|
||||||
return cachedIndexes;
|
return cachedIndexes;
|
||||||
}
|
}
|
||||||
@ -188,25 +190,10 @@ public class RESTMusicService implements MusicService {
|
|||||||
checkResponseSuccessful(response);
|
checkResponseSuccessful(response);
|
||||||
|
|
||||||
Indexes indexes = APIIndexesConverter.toDomainEntity(response.body().getIndexes());
|
Indexes indexes = APIIndexesConverter.toDomainEntity(response.body().getIndexes());
|
||||||
writeCachedIndexes(context, indexes, musicFolderId);
|
fileStorage.store(INDEXES_STORAGE_NAME, indexes, DomainSerializers.getIndexesSerializer());
|
||||||
return indexes;
|
return indexes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Indexes readCachedIndexes(Context context, String musicFolderId) {
|
|
||||||
String filename = getCachedIndexesFilename(context, musicFolderId);
|
|
||||||
return FileUtil.deserialize(context, filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void writeCachedIndexes(Context context, Indexes indexes, String musicFolderId) {
|
|
||||||
String filename = getCachedIndexesFilename(context, musicFolderId);
|
|
||||||
FileUtil.serialize(context, indexes, filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getCachedIndexesFilename(Context context, String musicFolderId) {
|
|
||||||
String s = Util.getRestUrl(context, null) + musicFolderId;
|
|
||||||
return String.format(Locale.US, "indexes-%d.ser", Math.abs(s.hashCode()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Indexes getArtists(boolean refresh,
|
public Indexes getArtists(boolean refresh,
|
||||||
Context context,
|
Context context,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user