From 605902c6c0302ef8b6c736036749f68bfdfb81dc Mon Sep 17 00:00:00 2001 From: Sora <8521327+SoraKasvgano@users.noreply.github.com> Date: Tue, 16 Dec 2025 09:05:07 +0800 Subject: [PATCH 01/24] add aplayer for shared url support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # APlayer Integration for Navidrome Shares This integration allows you to share music from Navidrome using APlayer, a beautiful HTML5 music player, without requiring authentication. ## Features - 🎵 Beautiful, responsive music player interface - 🔐 No authentication required - works with public share links - ⏰ Respects share expiration dates - 🎨 Clean, modern design - 📱 Mobile-friendly - 🔗 Easy to embed on external websites ## How to Use ### 1. Create a Share in Navidrome 1. In Navidrome, select songs, albums, or playlists you want to share 2. Click the share button and create a share link 3. Configure the share settings (expiration, description, etc.) ### 2. Get the APlayer URL 1. Go to the Navidrome admin panel 2. Navigate to "Shares" in the menu 3. Click on your share to edit it 4. You'll see two URLs: - **Share URL**: The regular Navidrome share page - **APlayer Embed URL**: The APlayer player page ### 3. Share or Embed You can either: - **Direct link**: Share the APlayer URL directly for people to listen in their browser - **Embed in website**: Use an iframe to embed the player on your own website #### Embed Example ```html ``` ## Technical Details ### How It Works 1. The APlayer page loads the share data from the server (no authentication needed) 2. Track streaming uses JWT tokens embedded in the share link 3. Tokens automatically expire when the share expires 4. All streaming is done through Navidrome's public API endpoints ### Security - No username/password required - Uses the same security model as regular Navidrome shares - JWT tokens are scoped to specific shares - Respects share expiration dates - Cannot access data outside the shared content ### Files Added/Modified **New Files:** - `resources/aplayer.html` - HTML template for the APlayer page - `resources/aplayer-share.js` - JavaScript that initializes APlayer with share data **Modified Files:** - `server/public/public.go` - Added route for `/share/:id/aplayer` - `server/public/handle_shares.go` - Added handler for APlayer page - `ui/src/utils/urls.js` - Added `shareAPlayerUrl()` function - `ui/src/share/ShareEdit.jsx` - Added APlayer URL display ## Customization ### Styling You can customize the appearance by modifying `resources/aplayer.html`. The default theme uses a purple gradient background, but you can change: - Colors and gradients - Player theme color - Layout and spacing - Font styles ### Player Options Edit `resources/aplayer-share.js` to modify APlayer settings: ```javascript const ap = new APlayer({ autoplay: false, // Auto-start playback theme: '#b7daff', // Player color theme loop: 'all', // Loop mode (all/one/none) volume: 0.7, // Default volume (0-1) // ... more options }); ``` For all available options, see [APlayer documentation](https://aplayer.js.org/). ## Credits - [Navidrome](https://github.com/navidrome/navidrome) - Modern Music Server - [APlayer](https://github.com/DIYgod/APlayer) - Beautiful HTML5 Music Player - [AplayerForNavidrome](https://github.com/maytom2016/AplayerForNavidrome) - Original inspiration ## License This integration follows the same license as Navidrome (GPL-3.0). --- APLAYER_INTEGRATION.md | 114 ++++++++++++++++++++++++++ go.mod | 1 + go.sum | 2 + resources/aplayer-share.js | 96 ++++++++++++++++++++++ resources/aplayer.html | 128 +++++++++++++++++++++++++++++ server/public/handle_shares.go | 145 +++++++++++++++++++++++++++++++++ server/public/public.go | 1 + ui/src/share/ShareEdit.jsx | 24 ++++-- ui/src/utils/urls.js | 8 ++ 9 files changed, 514 insertions(+), 5 deletions(-) create mode 100644 APLAYER_INTEGRATION.md create mode 100644 resources/aplayer-share.js create mode 100644 resources/aplayer.html diff --git a/APLAYER_INTEGRATION.md b/APLAYER_INTEGRATION.md new file mode 100644 index 000000000..58e4cb739 --- /dev/null +++ b/APLAYER_INTEGRATION.md @@ -0,0 +1,114 @@ +# APlayer Integration for Navidrome Shares + +This integration allows you to share music from Navidrome using APlayer, a beautiful HTML5 music player, without requiring authentication. + +## Features + +- 🎵 Beautiful, responsive music player interface +- 🔐 No authentication required - works with public share links +- ⏰ Respects share expiration dates +- 🎨 Clean, modern design +- 📱 Mobile-friendly +- 🔗 Easy to embed on external websites + +## How to Use + +### 1. Create a Share in Navidrome + +1. In Navidrome, select songs, albums, or playlists you want to share +2. Click the share button and create a share link +3. Configure the share settings (expiration, description, etc.) + +### 2. Get the APlayer URL + +1. Go to the Navidrome admin panel +2. Navigate to "Shares" in the menu +3. Click on your share to edit it +4. You'll see two URLs: + - **Share URL**: The regular Navidrome share page + - **APlayer Embed URL**: The APlayer player page + +### 3. Share or Embed + +You can either: + +- **Direct link**: Share the APlayer URL directly for people to listen in their browser +- **Embed in website**: Use an iframe to embed the player on your own website + +#### Embed Example + +```html + +``` + +## Technical Details + +### How It Works + +1. The APlayer page loads the share data from the server (no authentication needed) +2. Track streaming uses JWT tokens embedded in the share link +3. Tokens automatically expire when the share expires +4. All streaming is done through Navidrome's public API endpoints + +### Security + +- No username/password required +- Uses the same security model as regular Navidrome shares +- JWT tokens are scoped to specific shares +- Respects share expiration dates +- Cannot access data outside the shared content + +### Files Added/Modified + +**New Files:** +- `resources/aplayer.html` - HTML template for the APlayer page +- `resources/aplayer-share.js` - JavaScript that initializes APlayer with share data + +**Modified Files:** +- `server/public/public.go` - Added route for `/share/:id/aplayer` +- `server/public/handle_shares.go` - Added handler for APlayer page +- `ui/src/utils/urls.js` - Added `shareAPlayerUrl()` function +- `ui/src/share/ShareEdit.jsx` - Added APlayer URL display + +## Customization + +### Styling + +You can customize the appearance by modifying `resources/aplayer.html`. The default theme uses a purple gradient background, but you can change: + +- Colors and gradients +- Player theme color +- Layout and spacing +- Font styles + +### Player Options + +Edit `resources/aplayer-share.js` to modify APlayer settings: + +```javascript +const ap = new APlayer({ + autoplay: false, // Auto-start playback + theme: '#b7daff', // Player color theme + loop: 'all', // Loop mode (all/one/none) + volume: 0.7, // Default volume (0-1) + // ... more options +}); +``` + +For all available options, see [APlayer documentation](https://aplayer.js.org/). + +## Credits + +- [Navidrome](https://github.com/navidrome/navidrome) - Modern Music Server +- [APlayer](https://github.com/DIYgod/APlayer) - Beautiful HTML5 Music Player +- [AplayerForNavidrome](https://github.com/maytom2016/AplayerForNavidrome) - Original inspiration + +## License + +This integration follows the same license as Navidrome (GPL-3.0). diff --git a/go.mod b/go.mod index 8b741caff..478f81263 100644 --- a/go.mod +++ b/go.mod @@ -125,6 +125,7 @@ require ( github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/wtolson/go-taglib v0.0.0-20210406152913-79209c280058 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect diff --git a/go.sum b/go.sum index f5916c912..d85baf346 100644 --- a/go.sum +++ b/go.sum @@ -277,6 +277,8 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/unrolled/secure v1.17.0 h1:Io7ifFgo99Bnh0J7+Q+qcMzWM6kaDPCA5FroFZEdbWU= github.com/unrolled/secure v1.17.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40= +github.com/wtolson/go-taglib v0.0.0-20210406152913-79209c280058 h1:/kj9W8wSHTlwt/i4n6902i/YOPYNIXiDR/PAmgbrDyc= +github.com/wtolson/go-taglib v0.0.0-20210406152913-79209c280058/go.mod h1:p+WHGfN/a+Ol37Pm7EIOO/6Cylieb2qn1jmKfxtSsUg= github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg= github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= diff --git a/resources/aplayer-share.js b/resources/aplayer-share.js new file mode 100644 index 000000000..d94e08fa2 --- /dev/null +++ b/resources/aplayer-share.js @@ -0,0 +1,96 @@ +/** + * APlayer integration for Navidrome Share Links + * Works with public share links without authentication + */ +(function() { + 'use strict'; + + // Wait for DOM and APlayer to be ready + function initAPlayer() { + if (typeof APlayer === 'undefined') { + console.error('APlayer library not loaded'); + return; + } + + // Get share info from the page (injected by server) + const shareInfoElement = document.getElementById('share-info'); + if (!shareInfoElement) { + console.error('Share info not found'); + return; + } + + let shareInfo; + try { + shareInfo = JSON.parse(shareInfoElement.textContent); + } catch (e) { + console.error('Failed to parse share info:', e); + return; + } + + if (!shareInfo || !shareInfo.tracks || shareInfo.tracks.length === 0) { + console.error('No tracks found in share'); + return; + } + + // Get base URL from the page + const baseURL = window.NavidromeConfig?.baseURL || ''; + + // Convert share tracks to APlayer format + const playlist = shareInfo.tracks.map(function(track) { + // Stream URL uses the encoded track ID (contains JWT token) + const streamUrl = baseURL + '/public/s/' + track.id; + + // Cover art URL - we'll construct it from the share's image + const coverUrl = shareInfo.imageUrl || baseURL + '/android-chrome-192x192.png'; + + return { + name: track.title || 'Unknown Title', + artist: track.artist || 'Unknown Artist', + url: streamUrl, + cover: coverUrl, + theme: '#b7daff' + }; + }); + + // Initialize APlayer + const container = document.getElementById('aplayer'); + if (!container) { + console.error('APlayer container not found'); + return; + } + + const ap = new APlayer({ + container: container, + lrcType: 0, + audio: playlist, + autoplay: false, + theme: '#b7daff', + loop: 'all', + order: 'list', + preload: 'auto', + volume: 0.7, + mutex: true, + listFolded: false, + listMaxHeight: 90, + }); + + // Log initialization + console.log('APlayer initialized with', playlist.length, 'tracks'); + + // Optional: Add event listeners + ap.on('play', function() { + console.log('Playing:', ap.list.audios[ap.list.index].name); + }); + + ap.on('error', function() { + console.error('Playback error'); + }); + } + + // Initialize when DOM is ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initAPlayer); + } else { + initAPlayer(); + } +})(); diff --git a/resources/aplayer.html b/resources/aplayer.html new file mode 100644 index 000000000..8f5272a59 --- /dev/null +++ b/resources/aplayer.html @@ -0,0 +1,128 @@ + + +
+ + +Shared Music Player
+",t+=r(e[1]),t+="
\n"}),t}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i,a=function(){function e(e,t){for(var n=0;n