Skip to content

Commit 39d054d

Browse files
committed
feat: extract values from Objective-C types with proper casting
1 parent 98377e4 commit 39d054d

File tree

12 files changed

+560
-387
lines changed

12 files changed

+560
-387
lines changed

example/ios/MusicLibraryExample.xcodeproj/project.pbxproj

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -384,10 +384,7 @@
384384
"-DFOLLY_CFG_NO_COROUTINES=1",
385385
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
386386
);
387-
OTHER_LDFLAGS = (
388-
"$(inherited)",
389-
" ",
390-
);
387+
OTHER_LDFLAGS = "$(inherited) ";
391388
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
392389
SDKROOT = iphoneos;
393390
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
@@ -457,10 +454,7 @@
457454
"-DFOLLY_CFG_NO_COROUTINES=1",
458455
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
459456
);
460-
OTHER_LDFLAGS = (
461-
"$(inherited)",
462-
" ",
463-
);
457+
OTHER_LDFLAGS = "$(inherited) ";
464458
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
465459
SDKROOT = iphoneos;
466460
SWIFT_COMPILATION_MODE = wholemodule;

ios/MusicLibrary.mm

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,14 @@ - (instancetype) init {
2121
- (void)getTracksAsync:(JS::NativeMusicLibrary::InternalTrackOptions &)options resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
2222
NSLog(@"🎵 [MusicLibrary.mm] getTracksAsync called from Objective-C");
2323

24-
NSDictionary *optionsDict = @{}; // Convert JS options to NSDictionary
25-
NSDictionary *result = [musicLibrary getTracksAsync:optionsDict];
24+
// Extract values from Objective-C types with proper casting
25+
int first = (int)options.first();
26+
NSString *after = options.after();
27+
NSString *directory = options.directory();
28+
NSArray *sortBy = (NSArray *)options.sortBy();
29+
30+
// Call Swift function directly
31+
NSDictionary *result = [musicLibrary getTracksAsyncWithFirst:first after:after sortBy:sortBy directory:directory];
2632

2733
NSLog(@"🎵 [MusicLibrary.mm] getTracksAsync resolved with result: %@", result);
2834
resolve(result);
@@ -49,8 +55,14 @@ - (void)getTracksByAlbumAsync:(nonnull NSString *)albumId resolve:(nonnull RCTPr
4955
- (void)getTracksByArtistAsync:(nonnull NSString *)artistId options:(JS::NativeMusicLibrary::InternalTrackOptions &)options resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
5056
NSLog(@"🎵 [MusicLibrary.mm] getTracksByArtistAsync called with artistId: %@", artistId);
5157

52-
NSDictionary *optionsDict = @{}; // Convert JS options to NSDictionary
53-
NSDictionary *result = [musicLibrary getTracksByArtistAsync:artistId options:optionsDict];
58+
// Extract values from Objective-C types with proper casting
59+
int first = (int)options.first();
60+
NSString *after = options.after();
61+
NSString *directory = options.directory();
62+
NSArray *sortBy = (NSArray *)options.sortBy();
63+
64+
// Call Swift function directly
65+
NSDictionary *result = [musicLibrary getTracksByArtistAsync:artistId first:first after:after sortBy:sortBy directory:directory];
5466

5567
NSLog(@"🎵 [MusicLibrary.mm] getTracksByArtistAsync resolved with result: %@", result);
5668
resolve(result);
@@ -59,8 +71,13 @@ - (void)getTracksByArtistAsync:(nonnull NSString *)artistId options:(JS::NativeM
5971
- (void)getAlbumsAsync:(JS::NativeMusicLibrary::InternalAlbumOptions &)options resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
6072
NSLog(@"🎵 [MusicLibrary.mm] getAlbumsAsync called from Objective-C");
6173

62-
NSDictionary *optionsDict = @{}; // Convert JS options to NSDictionary
63-
NSDictionary *result = [musicLibrary getAlbumsAsync:optionsDict];
74+
// Extract values from Objective-C types with proper casting
75+
int first = (int)options.first();
76+
NSString *after = options.after();
77+
NSArray *sortBy = (NSArray *)options.sortBy();
78+
79+
// Call Swift function directly
80+
NSDictionary *result = [musicLibrary getAlbumsAsyncWithFirst:first after:after sortBy:sortBy];
6481

6582
NSLog(@"🎵 [MusicLibrary.mm] getAlbumsAsync resolved with result: %@", result);
6683
resolve(result);
@@ -78,8 +95,13 @@ - (void)getAlbumsByArtistAsync:(nonnull NSString *)artistId resolve:(nonnull RCT
7895
- (void)getArtistsAsync:(JS::NativeMusicLibrary::InternalArtistOptions &)options resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
7996
NSLog(@"🎵 [MusicLibrary.mm] getArtistsAsync called from Objective-C");
8097

81-
NSDictionary *optionsDict = @{}; // Convert JS options to NSDictionary
82-
NSDictionary *result = [musicLibrary getArtistsAsync:optionsDict];
98+
// Extract values from Objective-C types with proper casting
99+
int first = (int)options.first();
100+
NSString *after = options.after();
101+
NSArray *sortBy = (NSArray *)options.sortBy();
102+
103+
// Call Swift function directly
104+
NSDictionary *result = [musicLibrary getArtistsAsyncWithFirst:first after:after sortBy:sortBy];
83105

84106
NSLog(@"🎵 [MusicLibrary.mm] getArtistsAsync resolved with result: %@", result);
85107
resolve(result);

ios/MusicLibraryImpl.swift

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,54 @@ import Foundation
88

99
@objc public class MusicLibraryImpl: NSObject {
1010

11-
@objc public func getTracksAsync(_ options: [String: Any]) -> [String: Any] {
12-
NSLog("🎵 [MusicLibrary] getTracksAsync called with options: %@", options)
11+
@objc public func getTracksAsync(first: Int, after: String?, sortBy: [String], directory: String?) -> [String: Any] {
12+
NSLog("🎵 [MusicLibrary] getTracksAsync called with first: %d, after: %@, sortBy: %@, directory: %@", first, after ?? "nil", sortBy, directory ?? "nil")
1313

14-
let result = PaginatedResult<Track>(items: [], hasNextPage: false)
15-
let resultDict = result.toDictionary()
16-
17-
NSLog("🎵 [MusicLibrary] getTracksAsync returning: %@", resultDict)
18-
return resultDict
14+
do {
15+
let trackOptions = TrackOptions(after: after, first: first, sortBy: sortBy, directory: directory)
16+
NSLog("🎵 [MusicLibrary] getTracksAsync trackOptions: %@", trackOptions)
17+
let result = GetTracksQuery.getTracks(options: trackOptions)
18+
let resultDict = result.toDictionary()
19+
20+
NSLog("🎵 [MusicLibrary] getTracksAsync returning: %@", resultDict)
21+
return resultDict
22+
} catch {
23+
NSLog("🎵 [MusicLibrary] getTracksAsync error: %@", error.localizedDescription)
24+
return DataConverter.createErrorDictionary(
25+
code: "QUERY_ERROR",
26+
message: "Failed to query tracks: \(error.localizedDescription)"
27+
)
28+
}
1929
}
2030

2131
@objc public func getTrackMetadataAsync(_ trackId: String) -> [String: Any] {
2232
NSLog("🎵 [MusicLibrary] getTrackMetadataAsync called with trackId: %@", trackId)
2333

24-
let metadata = TrackMetadata(id: trackId, duration: nil, bitrate: nil, sampleRate: nil, channels: nil, format: nil, title: nil, artist: nil, album: nil, year: nil, genre: nil, track: nil, disc: nil, composer: nil, lyricist: nil, lyrics: nil, albumArtist: nil, comment: nil)
25-
let resultDict = metadata.toDictionary()
26-
27-
NSLog("🎵 [MusicLibrary] getTrackMetadataAsync returning: %@", resultDict)
28-
return resultDict
34+
if let metadata = GetTrackMetadataQuery.getTrackMetadata(trackId: trackId) {
35+
let resultDict = metadata.toDictionary()
36+
NSLog("🎵 [MusicLibrary] getTrackMetadataAsync returning: %@", resultDict)
37+
return resultDict
38+
} else {
39+
NSLog("🎵 [MusicLibrary] getTrackMetadataAsync: track not found")
40+
return DataConverter.createErrorDictionary(
41+
code: "TRACK_NOT_FOUND",
42+
message: "Track with id \(trackId) not found"
43+
)
44+
}
2945
}
3046

3147
@objc public func getTracksByAlbumAsync(_ albumId: String) -> [[String: Any]] {
3248
NSLog("🎵 [MusicLibrary] getTracksByAlbumAsync called with albumId: %@", albumId)
3349

34-
let result: [[String: Any]] = []
50+
let tracks = GetTracksByAlbumQuery.getTracksByAlbum(albumId: albumId)
51+
let result = tracks.map { $0.toDictionary() }
3552

3653
NSLog("🎵 [MusicLibrary] getTracksByAlbumAsync returning: %@", result)
3754
return result
3855
}
3956

40-
@objc public func getTracksByArtistAsync(_ artistId: String, options: [String: Any]) -> [String: Any] {
41-
NSLog("🎵 [MusicLibrary] getTracksByArtistAsync called with artistId: %@, options: %@", artistId, options)
57+
@objc public func getTracksByArtistAsync(_ artistId: String, first: Int, after: String?, sortBy: [String], directory: String?) -> [String: Any] {
58+
NSLog("🎵 [MusicLibrary] getTracksByArtistAsync called with artistId: %@, first: %d, after: %@, sortBy: %@, directory: %@", artistId, first, after ?? "nil", sortBy, directory ?? "nil")
4259

4360
let result = PaginatedResult<Track>(items: [], hasNextPage: false)
4461
let resultDict = result.toDictionary()
@@ -47,8 +64,8 @@ import Foundation
4764
return resultDict
4865
}
4966

50-
@objc public func getAlbumsAsync(_ options: [String: Any]) -> [String: Any] {
51-
NSLog("🎵 [MusicLibrary] getAlbumsAsync called with options: %@", options)
67+
@objc public func getAlbumsAsync(first: Int, after: String?, sortBy: [String]) -> [String: Any] {
68+
NSLog("🎵 [MusicLibrary] getAlbumsAsync called with first: %d, after: %@, sortBy: %@", first, after ?? "nil", sortBy)
5269

5370
let result = PaginatedResult<Album>(items: [], hasNextPage: false)
5471
let resultDict = result.toDictionary()
@@ -66,8 +83,8 @@ import Foundation
6683
return result
6784
}
6885

69-
@objc public func getArtistsAsync(_ options: [String: Any]) -> [String: Any] {
70-
NSLog("🎵 [MusicLibrary] getArtistsAsync called with options: %@", options)
86+
@objc public func getArtistsAsync(first: Int, after: String?, sortBy: [String]) -> [String: Any] {
87+
NSLog("🎵 [MusicLibrary] getArtistsAsync called with first: %d, after: %@, sortBy: %@", first, after ?? "nil", sortBy)
7188

7289
let result = PaginatedResult<Artist>(items: [], hasNextPage: false)
7390
let resultDict = result.toDictionary()
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
//
2+
// GetTrackMetadataQuery.swift
3+
// MusicLibrary
4+
//
5+
//
6+
7+
import Foundation
8+
import MediaPlayer
9+
import AVFoundation
10+
11+
internal class GetTrackMetadataQuery {
12+
13+
static func getTrackMetadata(trackId: String) -> TrackMetadata? {
14+
let query = MPMediaQuery.songs()
15+
16+
// 创建ID筛选条件
17+
let idPredicate = MPMediaPropertyPredicate(
18+
value: NSNumber(value: UInt64(trackId) ?? 0),
19+
forProperty: MPMediaItemPropertyPersistentID
20+
)
21+
22+
query.filterPredicates = Set([idPredicate])
23+
24+
guard let items = query.items, let item = items.first else {
25+
return nil
26+
}
27+
28+
// 获取基本信息
29+
let duration = item.playbackDuration
30+
let title = item.title
31+
let artist = item.artist
32+
let album = item.albumTitle
33+
let year = item.releaseDate.map { Calendar.current.component(.year, from: $0) }
34+
let genre = item.genre
35+
let trackNumber = item.albumTrackNumber > 0 ? item.albumTrackNumber : nil
36+
let discNumber = item.discNumber > 0 ? item.discNumber : nil
37+
let composer = item.composer
38+
let albumArtist = item.albumArtist
39+
40+
// 尝试获取更详细的音频信息
41+
var bitrate: Int64? = nil
42+
var sampleRate: Int? = nil
43+
var channels: String? = nil
44+
var format: String? = nil
45+
var lyrics: String? = nil
46+
47+
if let assetURL = item.assetURL {
48+
let asset = AVAsset(url: assetURL)
49+
50+
// 获取音频轨道信息
51+
if let audioTrack = asset.tracks(withMediaType: .audio).first {
52+
// 获取格式信息
53+
if let formatDescriptions = audioTrack.formatDescriptions as? [CMFormatDescription],
54+
let formatDescription = formatDescriptions.first {
55+
56+
let audioStreamBasicDescription = CMAudioFormatDescriptionGetStreamBasicDescription(formatDescription)
57+
if let basicDescription = audioStreamBasicDescription {
58+
sampleRate = Int(basicDescription.pointee.mSampleRate)
59+
channels = "\(basicDescription.pointee.mChannelsPerFrame)"
60+
61+
// 获取格式信息
62+
let formatID = basicDescription.pointee.mFormatID
63+
format = formatIDToString(formatID)
64+
}
65+
}
66+
67+
// 估算比特率
68+
bitrate = estimateBitrate(for: asset, duration: duration)
69+
}
70+
71+
// 获取歌词(如果有)
72+
lyrics = getLyrics(from: item)
73+
}
74+
75+
return TrackMetadata(
76+
id: trackId,
77+
duration: duration,
78+
bitrate: bitrate,
79+
sampleRate: sampleRate,
80+
channels: channels,
81+
format: format,
82+
title: title,
83+
artist: artist,
84+
album: album,
85+
year: year,
86+
genre: genre,
87+
track: trackNumber,
88+
disc: discNumber,
89+
composer: composer,
90+
lyricist: nil, // MediaPlayer框架不直接提供lyricist信息
91+
lyrics: lyrics,
92+
albumArtist: albumArtist,
93+
comment: nil // MediaPlayer框架不直接提供comment信息
94+
)
95+
}
96+
97+
// MARK: - Private Helper Methods
98+
99+
private static func estimateBitrate(for asset: AVAsset, duration: Double) -> Int64? {
100+
guard duration > 0 else { return nil }
101+
102+
// 尝试从文件大小估算比特率
103+
if let url = (asset as? AVURLAsset)?.url {
104+
do {
105+
let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
106+
if let fileSize = attributes[.size] as? Int64 {
107+
// 比特率 = (文件大小 * 8) / 持续时间 / 1000 (kbps)
108+
let bitrate = (fileSize * 8) / Int64(duration * 1000)
109+
return bitrate
110+
}
111+
} catch {
112+
// 忽略错误
113+
}
114+
}
115+
116+
return nil
117+
}
118+
119+
private static func formatIDToString(_ formatID: AudioFormatID) -> String {
120+
switch formatID {
121+
case kAudioFormatLinearPCM:
122+
return "PCM"
123+
case kAudioFormatMPEG4AAC:
124+
return "AAC"
125+
case kAudioFormatMPEGLayer3:
126+
return "MP3"
127+
case kAudioFormatAppleLossless:
128+
return "ALAC"
129+
default:
130+
// 将formatID转换为四字符字符串
131+
let formatBytes = withUnsafeBytes(of: formatID.bigEndian) { bytes in
132+
Array(bytes)
133+
}
134+
return String(bytes: formatBytes, encoding: .ascii) ?? "Unknown"
135+
}
136+
}
137+
138+
private static func getLyrics(from item: MPMediaItem) -> String? {
139+
// MediaPlayer框架不直接提供歌词
140+
// 在实际应用中,您可能需要从其他源获取歌词或解析文件中的歌词标签
141+
return item.lyrics
142+
}
143+
}

ios/tracks/GetTracks.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//
2+
// GetTracks.swift
3+
// MusicLibrary
4+
//
5+
//
6+
7+
import Foundation
8+
9+
internal class GetTracks {
10+
private let options: TrackOptions
11+
private let completion: (Result<PaginatedResult<Track>, Error>) -> Void
12+
13+
init(options: TrackOptions, completion: @escaping (Result<PaginatedResult<Track>, Error>) -> Void) {
14+
self.options = options
15+
self.completion = completion
16+
}
17+
18+
func execute() {
19+
let result = GetTracksQuery.getTracks(options: options)
20+
completion(.success(result))
21+
}
22+
}
23+

ios/tracks/GetTracksByAlbum.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// GetTracksByAlbum.swift
3+
// MusicLibrary
4+
//
5+
//
6+
7+
import Foundation
8+
9+
internal class GetTracksByAlbum {
10+
private let albumId: String
11+
private let completion: (Result<[Track], Error>) -> Void
12+
13+
init(albumId: String, completion: @escaping (Result<[Track], Error>) -> Void) {
14+
self.albumId = albumId
15+
self.completion = completion
16+
}
17+
18+
func execute() {
19+
let tracks = GetTracksByAlbumQuery.getTracksByAlbum(albumId: albumId)
20+
completion(.success(tracks))
21+
}
22+
}

0 commit comments

Comments
 (0)