Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
242 changes: 222 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

A powerful React Native library for accessing local music files and getting full metadata. Built with React Native's New Architecture (TurboModules) for optimal performance.


<div style="display: flex; gap: 10px; flex-wrap: wrap; justify-content: center;">
<img src="./assets/home.jpg" alt="Home Screen" style="width: 150px; height: auto;" />
<img src="./assets/track-list.jpg" alt="Track List" style="width: 150px; height: auto;" />
Expand All @@ -18,15 +17,15 @@ A powerful React Native library for accessing local music files and getting full

## ✨ Features

- 🎵 **Rich Metadata** - Access local music with full metadata including lyrics
- 🎵 **Rich Metadata** - Access local music with full metadata including lyrics, bitrate, sample rate, and more
- 🚀 **TurboModules** - Built with React Native's New Architecture for maximum performance
- 📄 **Pagination** - Efficient handling of large music collections
- 📄 **Pagination** - Cursor-based pagination for efficiently handling large music collections
- 🔍 **Flexible Sorting** - Multiple sorting options for tracks, albums, and artists
- 📁 **Directory Filtering** - Filter music by specific directories
- 📁 **Directory Filtering** - Filter tracks by specific directories (Android)
- 🔄 **TypeScript** - Full type definitions and type safety
- 🎨 **Album Artwork** - Support for album artwork and cover images
- 🤖 **Android Support** - Full native Android implementation
- 📱 **iOS Support** - Coming soon
- 🤖 **Android** - Full native Android implementation
- 🍎 **iOS** - Full native iOS implementation via MediaPlayer framework

## 🚀 Quick Start

Expand All @@ -38,31 +37,234 @@ npm install @nodefinity/react-native-music-library
yarn add @nodefinity/react-native-music-library
```

### Permissions

**Android** — add to `android/app/src/main/AndroidManifest.xml`:

```xml
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
```

**iOS** — add to `Info.plist`:

```xml
<key>NSAppleMusicUsageDescription</key>
<string>This app needs access to your music library.</string>
```

### Basic Usage

```js
import { getTracksAsync, getAlbumsAsync, getArtistsAsync } from '@nodefinity/react-native-music-library';
import {
getTracksAsync,
getAlbumsAsync,
getArtistsAsync,
} from '@nodefinity/react-native-music-library';

// Get tracks
const tracks = await getTracksAsync();
// Get first 20 tracks sorted by title
const result = await getTracksAsync({ sortBy: ['title', true] });
console.log(result.items); // Track[]
console.log(result.hasNextPage); // boolean
console.log(result.endCursor); // string | undefined

// Get albums with sorting
const albums = await getAlbumsAsync({
sortBy: ['title', true], // Sort by title ascending
first: 50
// Get next page
const nextPage = await getTracksAsync({
sortBy: ['title', true],
first: 20,
after: result.endCursor,
});
```

## 📖 API

// Get artists
const artists = await getArtistsAsync();
### `getTracksAsync(options?)`

Returns a paginated list of tracks from the music library.

```ts
getTracksAsync(options?: TrackOptions): Promise<PaginatedResult<Track>>
```

### Android Permissions
**TrackOptions**

Add to `android/app/src/main/AndroidManifest.xml`:
| Property | Type | Default | Description |
| ----------- | -------------------------------------------------------- | ----------- | --------------------------------------- |
| `first` | `number` | `20` | Max number of items to return |
| `after` | `string` | — | Cursor from previous page's `endCursor` |
| `sortBy` | `TrackSortByKey \| [TrackSortByKey, boolean] \| (...)[]` | `'default'` | Sort key, or tuple `[key, ascending]` |
| `directory` | `string` | — | Filter by directory path (Android only) |

```xml
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
**TrackSortByKey**: `'default' \| 'title' \| 'artist' \| 'album' \| 'duration' \| 'createdAt' \| 'modifiedAt' \| 'fileSize'`

---

### `getTrackMetadataAsync(trackId)`

Returns detailed audio metadata for a single track.

```ts
getTrackMetadataAsync(trackId: string): Promise<TrackMetadata>
```

---

### `getTracksByAlbumAsync(albumId)`

Returns all tracks in an album (no pagination).

```ts
getTracksByAlbumAsync(albumId: string): Promise<Track[]>
```

---

### `getTracksByArtistAsync(artistId, options?)`

Returns a paginated list of tracks by an artist.

```ts
getTracksByArtistAsync(artistId: string, options?: TrackOptions): Promise<PaginatedResult<Track>>
```

---

### `getAlbumsAsync(options?)`

Returns a paginated list of albums.

```ts
getAlbumsAsync(options?: AlbumOptions): Promise<PaginatedResult<Album>>
```

**AlbumOptions**

| Property | Type | Default | Description |
| -------- | -------------------------------------------------------- | ----------- | --------------------------------------- |
| `first` | `number` | `20` | Max number of items to return |
| `after` | `string` | — | Cursor from previous page's `endCursor` |
| `sortBy` | `AlbumSortByKey \| [AlbumSortByKey, boolean] \| (...)[]` | `'default'` | Sort key, or tuple `[key, ascending]` |

**AlbumSortByKey**: `'default' \| 'title' \| 'artist' \| 'trackCount' \| 'year'`

---

### `getAlbumsByArtistAsync(artistId)`

Returns all albums by an artist (no pagination).

```ts
getAlbumsByArtistAsync(artistId: string): Promise<Album[]>
```

---

### `getArtistsAsync(options?)`

Returns a paginated list of artists.

```ts
getArtistsAsync(options?: ArtistOptions): Promise<PaginatedResult<Artist>>
```

**ArtistOptions**

| Property | Type | Default | Description |
| -------- | ---------------------------------------------------------- | ----------- | --------------------------------------- |
| `first` | `number` | `20` | Max number of items to return |
| `after` | `string` | — | Cursor from previous page's `endCursor` |
| `sortBy` | `ArtistSortByKey \| [ArtistSortByKey, boolean] \| (...)[]` | `'default'` | Sort key, or tuple `[key, ascending]` |

**ArtistSortByKey**: `'default' \| 'title' \| 'trackCount' \| 'albumCount'`

---

## 📦 Types

### `Track`

| Field | Type | Description |
| ------------ | --------- | ----------------------------------- |
| `id` | `string` | Unique identifier |
| `title` | `string` | Track title |
| `artist` | `string` | Artist name |
| `artwork` | `string?` | Artwork file URI (may be undefined) |
| `album` | `string` | Album name |
| `duration` | `number` | Duration in seconds |
| `url` | `string` | File URI |
| `createdAt` | `number` | Unix timestamp (seconds) |
| `modifiedAt` | `number` | Unix timestamp (seconds) |
| `fileSize` | `number` | File size in bytes |

### `TrackMetadata`

| Field | Type | Description |
| ------------- | -------- | ------------------------------------ |
| `id` | `string` | Track ID |
| `duration` | `number` | Duration in seconds |
| `bitrate` | `number` | Bitrate in kbps |
| `sampleRate` | `number` | Sample rate in Hz |
| `channels` | `string` | Number of audio channels |
| `format` | `string` | Audio format (e.g. `"AAC"`, `"MP3"`) |
| `title` | `string` | Title tag |
| `artist` | `string` | Artist tag |
| `album` | `string` | Album tag |
| `year` | `number` | Release year |
| `genre` | `string` | Genre tag |
| `track` | `number` | Track number |
| `disc` | `number` | Disc number |
| `composer` | `string` | Composer tag |
| `lyricist` | `string` | Lyricist tag |
| `lyrics` | `string` | Embedded lyrics |
| `albumArtist` | `string` | Album artist tag |
| `comment` | `string` | Comment tag |

### `Album`

| Field | Type | Description |
| ------------ | --------- | ------------------------------ |
| `id` | `string` | Unique identifier |
| `title` | `string` | Album name |
| `artist` | `string` | Primary artist |
| `artwork` | `string?` | Artwork URI (may be undefined) |
| `trackCount` | `number` | Number of tracks |
| `year` | `number?` | Release year |

### `Artist`

| Field | Type | Description |
| ------------ | -------- | ---------------------- |
| `id` | `string` | Unique identifier |
| `title` | `string` | Artist name |
| `albumCount` | `number` | Number of albums |
| `trackCount` | `number` | Total number of tracks |

### `PaginatedResult<T>`

| Field | Type | Description |
| ------------- | --------- | ----------------------------------------- |
| `items` | `T[]` | Array of results |
| `hasNextPage` | `boolean` | Whether more items are available |
| `endCursor` | `string?` | Pass to `after` to fetch next page |
| `totalCount` | `number?` | Total count (may be expensive to compute) |

---

## 🔄 Pagination Example

```ts
async function fetchAllTracks() {
const allTracks = [];
let cursor: string | undefined;

do {
const result = await getTracksAsync({ first: 50, after: cursor });
allTracks.push(...result.items);
cursor = result.hasNextPage ? result.endCursor : undefined;
} while (cursor);

return allTracks;
}
```

## 🤝 Contributing
Expand Down
Loading
Loading