Skip to content

Commit

Permalink
v1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
youlalala committed Dec 14, 2023
2 parents c91962b + 71e4c19 commit f3a4576
Show file tree
Hide file tree
Showing 49 changed files with 389 additions and 324 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/server-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,5 @@ jobs:
docker pull ${{ secrets.NCP_REGISTRY }}/catchy-tape:latest
docker stop catchy-tape-latest
docker rm catchy-tape-latest
docker run -d -p 3000:3000 --name catchy-tape-latest ${{ secrets.NCP_REGISTRY }}/catchy-tape:latest
docker run -v /home/hyung/logs:/catchy-tape/logs -d -p 3000:3000 --name catchy-tape-latest ${{ secrets.NCP_REGISTRY }}/catchy-tape:latest
curl -X POST -H 'Content-type: application/json' --data '{"text":"서버 배포 성공!"}' ${{ secrets.SLACK_WEBHOOK_URL }}
50 changes: 31 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
</p>
<div align="center">

누구든지 쉽고 빠르게 노래를 공유할 수 있는,
숨겨진 아티스트를 발굴하고 신선한 노래를 들을 수 있는
누구든지 노래를 공유할 수 있는
숨겨진 아티스트의 노래를 들을 수 있는
**Catchy Tape** 로 오세요 ~ 📼 📼


Expand All @@ -18,44 +18,56 @@
</div>

## OverView
*기능 개발 후 추가할 예정*
| Player | Playlist | Upload | Search |
| ------------- | ------------- | ------------- | ------------- |
| <img width="200" src="https://github.com/boostcampwm2023/and04-catchy-tape/assets/62279741/a769d2f0-f3e1-4af4-8c97-58da38230ee7" /> | <img width="200" src="https://github.com/boostcampwm2023/and04-catchy-tape/assets/62279741/0789dab5-644d-4709-95c8-5dd4d16a5094"/> | <img width="200" src="https://github.com/boostcampwm2023/and04-catchy-tape/assets/62279741/c6443860-136d-444d-908e-94162714d81d" /> | <img width="200" src="https://github.com/boostcampwm2023/and04-catchy-tape/assets/62279741/956609bb-cfbb-4cf2-b676-89afa01e1837" />


## TechStack
*계속 업데이트 될 예정*
### 🤖 Android
| Category | TechStack | 기록 |
| ------------- | ------------- | ------------- |
| Architecture | Clean Architecture, Multi Module, MVVM | [프로젝트 구조](https://tral-lalala.tistory.com/126)[build-logic](https://algosketch.tistory.com/179)
| Architecture | Clean Architecture, Multi Module, MVVM | [프로젝트 구조](https://tral-lalala.tistory.com/126)[build-logic](https://algosketch.tistory.com/179)[네트워크 예외처리](https://github.com/boostcampwm2023/and04-catchy-tape/wiki/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC)
| DI | Hilt |
| Network | Retrofit, Kotlin Serialization | [역/직렬화 라이브러리 비교](https://github.com/boostcampwm2023/and04-catchy-tape/wiki/%EC%97%AD-%EC%A7%81%EB%A0%AC%ED%99%94-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EB%B9%84%EA%B5%90)
| Network | Retrofit, OkHttp, Kotlin Serialization | [역/직렬화 라이브러리 비교](https://github.com/boostcampwm2023/and04-catchy-tape/wiki/%EC%97%AD-%EC%A7%81%EB%A0%AC%ED%99%94-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EB%B9%84%EA%B5%90)
| Asynchronous | Coroutines, Flow
| Jetpack | DataBinding, Navigation |
| Jetpack | Media3, DataBinding, Navigation, DataStore |
| Image | Glide
| CI/CD | Github Actions |[PR 단위 테스트 자동화](https://algosketch.tistory.com/178)[Github Release 자동화](https://tral-lalala.tistory.com/127)[Firebase App 배포 자동화](https://tral-lalala.tistory.com/128)
| Test | Kotest
| Logging | Timber | [Timber 적용 이유](https://github.com/boostcampwm2023/and04-catchy-tape/wiki/Timber%EC%9D%84-%EC%A0%81%EC%9A%A9%ED%95%9C-%EC%9D%B4%EC%9C%A0)


<details>
<summary>그 외 기록</summary>
<summary>✏️ 그 외 기록</summary>

- [프로젝트 생성](https://github.com/boostcampwm2023/and04-catchy-tape/wiki/Android#%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%EC%8B%9C-%EA%B3%A0%EB%A0%A4%ED%95%9C-%EB%82%B4%EC%9A%A9)

</details>


### 📡 Server
| Category | TechStack |
| ------------- | ------------- |
| Framework, Language | NestJS, TypeScript |
| DB | MySQL |
| ORM | TypeORM |
| Category | TechStack | 기록 |
| ------------- | ------------- | -------------|
| Framework, Language | NestJS, TypeScript | [Nest 사용 이유](https://round-caution-9fd.notion.site/Nest-8da8116bb8014a95b268ea50c9080b8d) |
| DB & ORM | MySQL & TypeORM | [TypeORM 사용 이유](https://round-caution-9fd.notion.site/prisma-vs-TypeORM-0c8c89b3d5374405aca9e9c1db0a73b6)[관련 개념 학습](https://round-caution-9fd.notion.site/Server-624068a499114a14ba1388e198bb0dde?p=09210f9a985246639720c50e269f70a5&pm=s)|
| Test | Jest |
| API Docs | SwaggerHub |
| CI/CD | Github Actions |
| NCP | Server, Container Registry, VPC, Object Storage|
| Load Test | nGrinder -> k6 | [부하 테스트 일대기](https://round-caution-9fd.notion.site/174440e709e24d7c909e8c1684c1cc75)[부하 테스트 결과 기록지](https://round-caution-9fd.notion.site/4ead90a8131844c8b561a34908692e3c)
| API Docs | SwaggerHub | [Swagger Hub 링크](https://app.swaggerhub.com/apis/12201944/CatchyTapeImsi/1.0.0)
| CI/CD | Github Actions | [Github Actions 활용한 자동 배포](https://round-caution-9fd.notion.site/Github-Action-29d0d57f5a434954b4a7b4aa8c3b57e0) |
| NCP | Server, Container Registry, VPC, Object Storage|[vpc 환경 구성](https://round-caution-9fd.notion.site/VPC-a8eefbec2f0244629ee2a092c454ebd7) |
| 기술적 도전 |음악 인코딩, 인덱싱, docker 활용 배포, 부하 테스트| [인코딩](https://round-caution-9fd.notion.site/Cloud-Functions-d7f1528dd32146f6b8f0b255ef33ebd7)[인덱싱](https://round-caution-9fd.notion.site/ERD-DB-74377ed10a2347d1ac15f181134f52a1)[배포](https://round-caution-9fd.notion.site/docker-aa7522e8c5cf4b9c9135d6f6b2114fd4) |
| 한 눈에 보는 서버 기술 스택 | | [서버 기술 선정 이유](https://round-caution-9fd.notion.site/2a7b52b27e7d45cc980c1eea33a2ce09) |

<details>
<summary>그 외 기록</summary>
- 🔧 Architecture
<img src="https://github.com/boostcampwm2023/and04-catchy-tape/assets/83707411/33a0c12d-22da-4ae3-836b-3aeb46015183" width=600 height=400 />

-
<details>
<summary> ✏️ 그 외 기록</summary>
<a href="https://round-caution-9fd.notion.site/85dad9cc5a304161bfde523f62345e05">인코딩 성능 개선기</a>
<br>
<a href="https://round-caution-9fd.notion.site/SSH-Private-DB-9965141c545849a8bba9f2ad066bc959">ssh 터널링</a>
</details>

## Team. 🍗 오도독
Expand All @@ -64,7 +76,7 @@
|:---:|:---:|:---:|:---:|:---:|
|<img src="https://github.com/khw3754.png">|<img src="https://github.com/Cutiepazzipozzi.png">|<img src="https://github.com/youlalala.png">|<img src="https://github.com/HamBP.png">|<img src="https://github.com/2taezeat.png">|
|Backend|Backend|Android|Android|Android|
|강아지 귀여웡|엄마 뱃속으로 다시 들어가고 싶네요|hiphop은 계란이다 🥚|0과 1로 사람을 만들 수 있을까요?|Music is my life~|
|강아지 귀여웡|엄마 뱃속으로 다시 들어가고 싶어요|hiphop은 계란이다 🥚|0과 1로 사람을 만들 수 있을까요?|Music is my life~|

<div align="center">
<a href="https://github.com/boostcampwm2023/and04-catchy-tape/wiki/%08Ground-Rule" target="_blank">그라운드 룰</a>
Expand Down
4 changes: 2 additions & 2 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ android {
applicationId = "com.ohdodok.catchytape"
minSdk = 26
targetSdk = 33
versionCode = 5
versionName = "0.3.2"
versionCode = 7
versionName = "1.0.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down
6 changes: 4 additions & 2 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
android:usesCleartextTraffic="true">
<activity
android:name=".feature.login.LoginActivity"
android:exported="true">
android:exported="true"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand All @@ -25,7 +26,8 @@

<activity
android:name=".MainActivity"
android:exported="true" />
android:exported="true"
android:screenOrientation="portrait" />

<service
android:name=".mediasession.PlaybackService"
Expand Down
56 changes: 32 additions & 24 deletions android/app/src/main/java/com/ohdodok/catchytape/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.ohdodok.catchytape

import android.animation.ObjectAnimator
import android.content.ComponentName
import android.net.ConnectivityManager
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
Expand All @@ -9,7 +8,6 @@ import android.view.View
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.animation.doOnEnd
import androidx.core.view.WindowCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
Expand All @@ -21,6 +19,7 @@ import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController
import com.ohdodok.catchytape.core.ui.cterror.toMessageId
import com.ohdodok.catchytape.databinding.ActivityMainBinding
import com.ohdodok.catchytape.feature.player.PlayerEvent
import com.ohdodok.catchytape.feature.player.PlayerListener
Expand All @@ -34,7 +33,6 @@ import com.ohdodok.catchytape.mediasession.PlaybackService
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.launch
import javax.inject.Inject
import com.ohdodok.catchytape.core.ui.R.string as uiString
Expand Down Expand Up @@ -72,7 +70,7 @@ class MainActivity : AppCompatActivity() {
setupPlayButton()
setupPreviousButton()
setupNextButton()
observePlaylistChange()
observeEvents()
}

override fun onStart() {
Expand Down Expand Up @@ -102,7 +100,8 @@ class MainActivity : AppCompatActivity() {

navHostFragment.findNavController().addOnDestinationChangedListener { _, destination, _ ->
when (destination.id) {
com.ohdodok.catchytape.feature.player.R.id.player_fragment -> {
com.ohdodok.catchytape.feature.player.R.id.player_fragment,
com.ohdodok.catchytape.feature.player.R.id.playlist_bottom_sheet -> {
hideBottomNav()
hidePlayerController()
}
Expand All @@ -122,19 +121,22 @@ class MainActivity : AppCompatActivity() {
}

private fun hideBottomNav() {
val height = binding.bottomNav.height.toFloat()
ObjectAnimator.ofFloat(binding.bottomNav, "translationY", height).apply {
duration = BOTTOM_NAV_ANIMATION_DURATION
doOnEnd { binding.bottomNav.visibility = View.GONE }
start()
with(binding.bottomNav) {
animate()
.translationY(height.toFloat())
.setDuration(BOTTOM_NAV_ANIMATION_DURATION)
.withEndAction { visibility = View.GONE }
.start()
}
}

private fun showBottomNav() {
binding.bottomNav.visibility = View.VISIBLE
ObjectAnimator.ofFloat(binding.bottomNav, "translationY", 0f).apply {
duration = BOTTOM_NAV_ANIMATION_DURATION
start()
with(binding.bottomNav){
animate()
.translationY(0f)
.setDuration(BOTTOM_NAV_ANIMATION_DURATION)
.withStartAction { visibility = View.VISIBLE }
.start()
}
}

Expand Down Expand Up @@ -166,19 +168,25 @@ class MainActivity : AppCompatActivity() {
}
}

private fun observePlaylistChange() {
private fun observeEvents() {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
playViewModel.events
.filterIsInstance<PlayerEvent.PlaylistChanged>()
.collectLatest { event ->
val newItems = getMediasWithMetaData(event.currentPlaylist.musics)
player.clearMediaItems()
player.setMediaItems(newItems)

player.seekTo(event.currentPlaylist.startMusicIndex, 0)
player.play()
playViewModel.events.collectLatest { event ->
when (event) {
is PlayerEvent.ShowError -> {
Toast.makeText(this@MainActivity, getString(event.error.toMessageId()), Toast.LENGTH_LONG).show()
}

is PlayerEvent.PlaylistChanged -> {
val newItems = getMediasWithMetaData(event.currentPlaylist.musics)
player.clearMediaItems()
player.setMediaItems(newItems)

player.seekTo(event.currentPlaylist.startMusicIndex, 0)
player.play()
}
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ class PlaybackService : MediaSessionService() {

override fun onDestroy() {
mediaSession?.run {
player.release()
release()
mediaSession = null
}
Expand Down
8 changes: 8 additions & 0 deletions android/build-logic/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
`kotlin-dsl`
}

tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = "17"
}
}

dependencies {
implementation(libs.android.gradlePlugin)
implementation(libs.kotlin.gradlePlugin)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class AndroidFeaturePlugin : Plugin<Project> {
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")

dependencies {
add("implementation", project(":core:ui"))
add("api", project(":core:ui"))
add("implementation", project(":core:domain"))

add("testImplementation", libs.findLibrary("junit").get())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,23 @@ internal class AndroidLibraryPlugin : Plugin<Project> {

extensions.configure<LibraryExtension> {
compileSdk = 34
defaultConfig.minSdk = 26

defaultConfig {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
minSdk = 26
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
Expand Down
19 changes: 1 addition & 18 deletions android/core/data/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import java.util.Properties
import java.io.FileInputStream
import java.util.Properties

@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
plugins {
Expand All @@ -16,24 +16,7 @@ android {
namespace = "com.ohdodok.catchytape.core.data"

defaultConfig {

buildConfigField("String", "BASE_URL", localProperties["server.url"] as String)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}

kotlinOptions {
jvmTarget = "17"
}

buildFeatures {
Expand Down
2 changes: 0 additions & 2 deletions android/core/domain/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ dependencies {

implementation(libs.inject)

// fixme : kotest 사용이 확정되면 junit 지우기
testImplementation(libs.junit)
testImplementation(libs.kotest.runner)
testImplementation(libs.kotest.property)
testImplementation(libs.kotest.extentions.junitxml)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ enum class CtErrorType(val errorCode: Int) {
EXPIRED_TOKEN(4101),
SERVER(5000),
SERVICE(5001),
ENCODING_FAILURE(5002);
ENCODING_FAILURE(5002),
ENCODED_MUSIC_UPLOAD_ERROR(5003);

companion object {
val ctErrorEnums = CtErrorType.values().toList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,15 @@ class CurrentPlaylistUseCase @Inject constructor() {
musics = musics,
)

scope.launch {
_currentPlaylist.send(newPlaylist)
}
scope.launch { _currentPlaylist.send(newPlaylist) }
}

fun playMusic(music: Music) {
val newPlaylist = CurrentPlaylist(
startMusic = music,
musics = listOf(music),
)

scope.launch { _currentPlaylist.send(newPlaylist) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.ohdodok.catchytape.core.domain.utils

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow

fun <T> Flow<T>.throttleFirst(windowDuration: Long): Flow<T> = flow {
var lastEmissionTime = 0L
collect { upstream ->
val currentTime = System.currentTimeMillis()
if (currentTime - lastEmissionTime > windowDuration) {
lastEmissionTime = currentTime
emit(upstream)
}
}
}
Loading

0 comments on commit f3a4576

Please sign in to comment.