Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/developeracademy-postech/2024-nc2-m28-music

NC2 Doran&Cinephile Music
https://github.com/developeracademy-postech/2024-nc2-m28-music

Last synced: about 10 hours ago
JSON representation

NC2 Doran&Cinephile Music

Awesome Lists containing this project

README

        

# 2024-NC2-M28-Music

![Frame 10](https://github.com/DeveloperAcademy-POSTECH/2024-NC2-M28-Music/assets/118119110/5bc39a7f-c1c1-4e28-88b8-e022f6cd214e)

## ๐ŸŽฅ Youtube Link

https://www.youtube.com/watch?v=XMYR4lOg4mM

## ๐Ÿ’ก About MusicKit

### MusicKit

> MusicKit์€ Swift๋ฅผ ์‚ฌ์šฉํ•ด ์•ฑ์—์„œ ์Œ์•… ํ•ญ๋ชฉ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ ์ž…๋‹ˆ๋‹ค.
MusicKit์„ ์‚ฌ์šฉํ•˜๋ฉด Apple Music์˜ ์ปจํ…์ธ (์žฅ๋ฅด, ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ, ๋…ธ๋ž˜)๋ฅผ ๊ฒ€์ƒ‰ํ•˜๊ณ  ํ•ด๋‹น ์ปจํ…์ธ ์— ๋Œ€ํ•œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ(์ œ๋ชฉ, ์•„ํ‹ฐ์ŠคํŠธ๋ช…, ๋ฐœ๋งค์ผ ๋“ฑ)๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

### MediaPlayer

> MusicKit์˜ ์ผ๋ถ€์ธ MediaPlayer ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋…ธ๋ž˜๋ฅผ ์žฌ์ƒํ•˜๊ฑฐ๋‚˜ ๋ฉˆ์ถœ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

## ๐ŸŽฏ What we focus on?

> MusicKit์„ ํ™œ์šฉํ•˜์—ฌ Apple Music์—์„œ ๋…ธ๋ž˜๋ฅผ ๊ฒ€์ƒ‰ํ•˜๊ณ , MediaPlayer๋ฅผ ํ†ตํ•ด ์žฌ์ƒํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์— ์ง‘์ค‘ํ–ˆ์Šต๋‹ˆ๋‹ค.

## ๐Ÿ’ผ Use Case

> "๋žœ๋ค ์Œ์•… 1์ดˆ๋“ฃ๊ณ  ๋งž์ถ”๊ธฐ"

## ๐Ÿ–ผ๏ธ Prototype

![(ํ”„๋กœํ† ํƒ€์ž…๊ณผ ์„ค๋ช… ์ถ”๊ฐ€)](https://github.com/DeveloperAcademy-POSTECH/2024-NC2-M28-Music/assets/52277540/ec32e1b5-cc27-4441-b414-ad4a87e05074)

### ์—ฐ๋„๋ณ„ K-Pop ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ ์„ ํƒ

- 2000~2023๋…„์˜ K-Pop ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ ์ค‘ 1๊ฐœ ์„ ํƒ

### ํ€ด์ฆˆ ์กฐ๊ฑด ์„ค์ •

- ๋ฌธ์ œ ๊ฐœ์ˆ˜
- ์ œํ•œ ์ฒญ์ทจ์‹œ๊ฐ„

### ์กฐ๊ฑด์— ๋”ฐ๋ฅธ ์Œ์•…ํ€ด์ฆˆ ์ƒ์„ฑ

### ์Œ์•… ํ€ด์ฆˆ

- ํ€ด์ฆˆ ์ง„ํ–‰์ƒํ™ฉ์„ ๋ณผ ์ˆ˜ ์žˆ๋Š” ProgressBar
- ์Œ์•… ์žฌ์ƒ
- ๋‹จ๊ณ„์— ๋”ฐ๋ฅธ ํžŒํŠธ ์ œ๊ณต ํ›„ ์ •๋‹ต ๋ณด๊ธฐ
- ์ •๋‹ต ๋ฐ”๋กœ ๋ณด๊ธฐ

### ํ€ด์ฆˆ ์ข…๋ฃŒ

- ํ™ˆํ™”๋ฉด ๋ฒ„ํŠผ ํด๋ฆญ์‹œ ์—ฐ๋„๋ณ„ K-Pop ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ ์„ ํƒ ํ™”๋ฉด์œผ๋กœ ๋„ค๋น„๊ฒŒ์ด์…˜

## ๐Ÿ› ๏ธ About Code

### ์‚ฌ์šฉ์ž ์ธ์ฆ ์š”์ฒญ

```swift
// MusicManager.swift

func requestAuthorization() async -> Bool {
let status = await MusicAuthorization.request()
return status == .authorized
}
```

- requestAuthorization ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž๊ฐ€ Apple Music์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ถŒํ•œ์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.

### ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ ๊ฒ€์ƒ‰

```swift
//MusicManager.swift

var searchRequest = MusicCatalogSearchRequest(term: "K-Pop Hits: \(year)", types: [Playlist.self])
searchRequest.limit = 1

let searchResponse = try await searchRequest.response()
```

- MusicCatalogSearchRequest๋ฅผ ํ†ตํ•ด ํŠน์ • ์ด๋ฆ„์˜ ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ ๊ฒ€์ƒ‰์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.

- ๊ฒ€์ƒ‰ํ•œ ๊ฒฐ๊ณผ ์ค‘์—์„œ ๊ฐ€์žฅ ์—ฐ๊ด€์„ฑ์ด ๋†’์€ ๊ฒ€์ƒ‰๊ฒฐ๊ณผ 1๊ฐœ๋ฅผ ๋ฐ›์•„์˜ต๋‹ˆ๋‹ค.

### ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ์—์„œ ํŠธ๋ž™ ๊ฐ€์ ธ์˜ค๊ธฐ

```swift
// MusicManager.swift

if let playlist = searchResponse.playlists.first {
print("ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ๋ฅผ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค: \(playlist)")

// ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ์˜ ํŠธ๋ž™๋“ค์„ ๊ฐ€์ ธ์˜ค๊ธฐ
var playlistRequest = MusicCatalogResourceRequest(matching: \.id, equalTo: playlist.id)
playlistRequest.properties = [.tracks]

let playlistResponse = try await playlistRequest.response()

if let detailedPlaylist = playlistResponse.items.first {
print("์ƒ์„ธ ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ: \(detailedPlaylist)")

let tracks = detailedPlaylist.tracks ?? []
let songs: [Song] = tracks.compactMap {
if case let .song(song) = $0 {
return song
}
return nil
}

DispatchQueue.main.async {
self.tracks = songs
print("ํŠธ๋ž™ ๋ชฉ๋ก: \(self.tracks)")
}
} else {
print("ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ์˜ ์ƒ์„ธ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.")
}
} else {
print("ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ๋ฅผ ์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.")
}
```

- MusicCatalogResourceRequest์„ ํ†ตํ•ด ๊ฒ€์ƒ‰์„ ํ†ตํ•ด ์ฐพ์€ playList ์•ˆ์— ์žˆ๋Š” ํŠธ๋ž™์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.

- ํŠธ๋ž™์— ๋‹ด๊ฒจ์žˆ๋Š” ๋…ธ๋ž˜๋“ค์„ songs ๋ฐฐ์—ด์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

### ๋…ธ๋ž˜ ์žฌ์ƒ

```swift
// MusicManager.swift

func songPlayForTime(_ song: Song, time: Double){
Task {
do {
print(song)
let storeID = song.id.rawValue
let descriptor = MPMusicPlayerStoreQueueDescriptor(storeIDs: [storeID])
musicPlayer.setQueue(with: descriptor)
musicPlayer.play()

DispatchQueue.main.asyncAfter(deadline: .now() + time){
self.musicPlayer.pause()
}
}
}
}
```

- ์žฌ์ƒํ•  ๋…ธ๋ž˜์˜ id๋ฅผ ๊ฐ€์ ธ์˜จ ํ›„ MPMusicPlayerStoreQueueDescriptor๋ฅผ ํ†ตํ•ด ์žฌ์ƒ ํ๋ฅผ ์„ค์ •ํ•  ๋””์Šคํฌ๋ฆฝํ„ฐ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

- ์žฌ์ƒ ํ๋ฅผ ์„ค์ •ํ•œ ํ›„์— ์Œ์•…์„ ์žฌ์ƒํ•ฉ๋‹ˆ๋‹ค.

- ์ง€์ •๋œ ์‹œ๊ฐ„ ํ›„ ์Œ์•…์„ ์ผ์‹œ ์ •์ง€ํ•ฉ๋‹ˆ๋‹ค.