Skip to content

Commit

Permalink
Report plays (#238)
Browse files Browse the repository at this point in the history
  • Loading branch information
chvp authored Jul 23, 2021
1 parent 962c40b commit c0abde7
Show file tree
Hide file tree
Showing 10 changed files with 709 additions and 11 deletions.
545 changes: 545 additions & 0 deletions app/schemas/me.vanpetegem.accentor.data.AccentorDatabase/8.json

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions app/src/main/java/me/vanpetegem/accentor/api/plays/Play.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package me.vanpetegem.accentor.api.plays

import com.github.kittinunf.fuel.httpPost
import java.time.Instant
import me.vanpetegem.accentor.data.authentication.AuthenticationData
import me.vanpetegem.accentor.data.plays.Play
import me.vanpetegem.accentor.util.Result
import me.vanpetegem.accentor.util.jsonBody
import me.vanpetegem.accentor.util.responseObject

data class Arguments(val play: PlayArguments)
data class PlayArguments(val trackId: Int, val playedAt: Instant)

fun create(server: String, authenticationData: AuthenticationData, trackId: Int, playedAt: Instant): Result<Play> {
return "$server/api/plays".httpPost()
.set("Accept", "application/json")
.set("X-Secret", authenticationData.secret)
.set("X-Device-Id", authenticationData.deviceId)
.jsonBody(Arguments(PlayArguments(trackId, playedAt)))
.responseObject<Play>().third
.fold(
{ play: Play -> Result.Success(play) },
{ e: Throwable -> Result.Error(Exception("Error creating play", e)) },
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import me.vanpetegem.accentor.data.artists.ArtistDao
import me.vanpetegem.accentor.data.artists.DbArtist
import me.vanpetegem.accentor.data.codecconversions.CodecConversionDao
import me.vanpetegem.accentor.data.codecconversions.DbCodecConversion
import me.vanpetegem.accentor.data.plays.UnreportedPlay
import me.vanpetegem.accentor.data.plays.UnreportedPlayDao
import me.vanpetegem.accentor.data.tracks.DbTrack
import me.vanpetegem.accentor.data.tracks.DbTrackArtist
import me.vanpetegem.accentor.data.tracks.DbTrackGenre
Expand All @@ -40,15 +42,17 @@ import me.vanpetegem.accentor.util.RoomTypeConverters
DbTrack::class,
DbTrackArtist::class,
DbTrackGenre::class,
UnreportedPlay::class,
],
version = 7
version = 8
)
abstract class AccentorDatabase : RoomDatabase() {
abstract fun albumDao(): AlbumDao
abstract fun artistDao(): ArtistDao
abstract fun codecConversionDao(): CodecConversionDao
abstract fun trackDao(): TrackDao
abstract fun userDao(): UserDao
abstract fun unreportedPlayDao(): UnreportedPlayDao
}

@Module
Expand Down Expand Up @@ -180,6 +184,25 @@ internal object DatabaseModule {
}
}
})
.addMigrations(object : Migration(7, 8) {
override fun migrate(database: SupportSQLiteDatabase) {
database.beginTransaction()
try {
database.execSQL(
"""
CREATE TABLE IF NOT EXISTS `unreported_plays` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`track_id` INTEGER NOT NULL,
`played_at` TEXT NOT NULL
)
"""
)
database.setTransactionSuccessful()
} finally {
database.endTransaction()
}
}
})
.build()
}

Expand All @@ -193,4 +216,6 @@ internal object DatabaseModule {
fun provideTrackDao(database: AccentorDatabase): TrackDao = database.trackDao()
@Provides
fun provideUserDao(database: AccentorDatabase): UserDao = database.userDao()
@Provides
fun provideUnreportedPlayDao(database: AccentorDatabase): UnreportedPlayDao = database.unreportedPlayDao()
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ class CodecConversionRepository @Inject constructor(
map
}

fun getFirst(): CodecConversion? = codecConversionDao.getFirstCodecConversion()
fun getById(id: Int): CodecConversion? = codecConversionDao.getCodecConversionById(id)

suspend fun refresh(handler: suspend (Result<Unit>) -> Unit) {
when (val result = index(authenticationRepository.server.value!!, authenticationRepository.authData.value!!)) {
is Result.Success -> {
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/java/me/vanpetegem/accentor/data/plays/Play.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package me.vanpetegem.accentor.data.plays

import java.time.Instant

data class Play(
val id: Int,
val playedAt: Instant,
val trackId: Int,
val userId: Int,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package me.vanpetegem.accentor.data.plays

import java.time.Instant
import javax.inject.Inject
import me.vanpetegem.accentor.api.plays.create
import me.vanpetegem.accentor.data.authentication.AuthenticationRepository
import me.vanpetegem.accentor.util.Result

class PlayRepository @Inject constructor(
private val authenticationRepository: AuthenticationRepository,
private val unreportedPlayDao: UnreportedPlayDao,
) {
suspend fun reportPlay(trackId: Int, playedAt: Instant) {
when (create(authenticationRepository.server.value!!, authenticationRepository.authData.value!!, trackId, playedAt)) {
is Result.Success -> {
for (play in unreportedPlayDao.getAllUnreportedPlays()) {
when (create(authenticationRepository.server.value!!, authenticationRepository.authData.value!!, play.trackId, play.playedAt)) {
is Result.Success -> unreportedPlayDao.delete(play)
}
}
}
is Result.Error -> {
unreportedPlayDao.insert(UnreportedPlay(trackId = trackId, playedAt = playedAt))
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package me.vanpetegem.accentor.data.plays

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.time.Instant

@Entity(tableName = "unreported_plays")
data class UnreportedPlay(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
@ColumnInfo(name = "track_id")
val trackId: Int,
@ColumnInfo(name = "played_at")
val playedAt: Instant,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package me.vanpetegem.accentor.data.plays

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query

@Dao
interface UnreportedPlayDao {
@Query("SELECT * FROM unreported_plays")
fun getAllUnreportedPlays(): List<UnreportedPlay>

@Insert
fun insert(play: UnreportedPlay)

@Delete
fun delete(play: UnreportedPlay)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class TrackRepository @Inject constructor(
private val authenticationRepository: AuthenticationRepository
) {
fun findById(id: Int): LiveData<Track?> = trackDao.findById(id)
fun getById(id: Int): Track? = trackDao.getTrackById(id)
fun findByIds(ids: List<Int>): LiveData<List<Track>> = trackDao.findByIds(ids)
fun findByArtist(artist: Artist): LiveData<List<Track>> = trackDao.findByArtist(artist)
fun findByAlbum(album: Album): LiveData<List<Track>> = trackDao.findByAlbum(album)
Expand Down
48 changes: 38 additions & 10 deletions app/src/main/java/me/vanpetegem/accentor/media/MusicService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.media2.session.SessionCommandGroup
import androidx.media2.session.SessionResult
import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.audio.AudioAttributes
import com.google.android.exoplayer2.database.ExoDatabaseProvider
Expand All @@ -29,6 +30,7 @@ import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvicto
import com.google.android.exoplayer2.upstream.cache.SimpleCache
import dagger.hilt.android.AndroidEntryPoint
import java.io.File
import java.time.Instant
import java.util.concurrent.Executors
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers.IO
Expand All @@ -37,12 +39,13 @@ import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import me.vanpetegem.accentor.R
import me.vanpetegem.accentor.data.albums.Album
import me.vanpetegem.accentor.data.albums.AlbumDao
import me.vanpetegem.accentor.data.albums.AlbumRepository
import me.vanpetegem.accentor.data.authentication.AuthenticationDataSource
import me.vanpetegem.accentor.data.codecconversions.CodecConversionDao
import me.vanpetegem.accentor.data.codecconversions.CodecConversionRepository
import me.vanpetegem.accentor.data.plays.PlayRepository
import me.vanpetegem.accentor.data.preferences.PreferencesDataSource
import me.vanpetegem.accentor.data.tracks.Track
import me.vanpetegem.accentor.data.tracks.TrackDao
import me.vanpetegem.accentor.data.tracks.TrackRepository
import me.vanpetegem.accentor.userAgent

@AndroidEntryPoint
Expand All @@ -51,9 +54,10 @@ class MusicService : MediaSessionService() {

@Inject lateinit var authenticationDataSource: AuthenticationDataSource
@Inject lateinit var preferencesDataSource: PreferencesDataSource
@Inject lateinit var codecConversionDao: CodecConversionDao
@Inject lateinit var trackDao: TrackDao
@Inject lateinit var albumDao: AlbumDao
@Inject lateinit var codecConversionRepository: CodecConversionRepository
@Inject lateinit var trackRepository: TrackRepository
@Inject lateinit var albumRepository: AlbumRepository
@Inject lateinit var playRepository: PlayRepository

private lateinit var notificationManager: NotificationManagerCompat
private lateinit var notificationBuilder: NotificationBuilder
Expand Down Expand Up @@ -95,6 +99,30 @@ class MusicService : MediaSessionService() {
setHandleAudioBecomingNoisy(true)
}.build().apply {
setAudioAttributes(accentorAudioAttributes, true)
addListener(object : Player.Listener {
private var trackId: Int? = null

override fun onMediaItemTransition(item: com.google.android.exoplayer2.MediaItem?, reason: Int) {
trackId = item?.mediaId?.toInt()
}

override fun onPositionDiscontinuity(old: Player.PositionInfo, new: Player.PositionInfo, reason: Int) {
if (trackId != null && reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
reportPlay()
}
}

override fun onPlaybackStateChanged(state: Int) {
if (trackId != null && state == Player.STATE_ENDED) {
reportPlay()
}
}

private fun reportPlay() {
val savedTrackId = trackId!!
mainScope.launch(IO) { playRepository.reportPlay(savedTrackId, Instant.now()) }
}
})
}
}

Expand All @@ -109,8 +137,8 @@ class MusicService : MediaSessionService() {
info: MediaSession.ControllerInfo,
mediaId: String
): MediaItem? {
val track = trackDao.getTrackById(mediaId.toInt())
val album = track?.let { albumDao.getAlbumById(it.albumId) }
val track = trackRepository.getById(mediaId.toInt())
val album = track?.let { albumRepository.getById(it.albumId) }
return track?.let { t -> album?.let { a -> convertTrack(t, a) } }
}
}
Expand Down Expand Up @@ -181,8 +209,8 @@ class MusicService : MediaSessionService() {

private fun convertTrack(track: Track, album: Album): MediaItem {
val conversionId = preferencesDataSource.conversionId.value
val firstConversion by lazy { codecConversionDao.getFirstCodecConversion() }
val conversionParam = if (conversionId != null && codecConversionDao.getCodecConversionById(conversionId) != null) {
val firstConversion by lazy { codecConversionRepository.getFirst() }
val conversionParam = if (conversionId != null && codecConversionRepository.getById(conversionId) != null) {
"&codec_conversion_id=$conversionId"
} else if (firstConversion != null) {
"&codec_conversion_id=${firstConversion!!.id}"
Expand Down

0 comments on commit c0abde7

Please sign in to comment.