mirror of
https://github.com/ragetrip/audio-pluglist.git
synced 2026-07-22 06:44:43 +00:00
Add files via upload
Versions 1.2.6 Release, Guide, and Fixes.
This commit is contained in:
parent
10a8f99227
commit
638b2ad5fa
7 changed files with 328 additions and 58 deletions
|
|
@ -1,5 +1,13 @@
|
|||
# Changelog
|
||||
|
||||
## 1.2.5 - Quality of life improvements
|
||||
- Added **macOS** support.
|
||||
- Added **Hotlink** support.
|
||||
- Added **Re-scan Playlists** & **Re-scan playlist** features to Tab & Footer views.
|
||||
- Added **Icons** in drop down selector for easy playlist type identification.
|
||||
- Added **YouTube Privacy** toggle for privacy-enhanced domain on embeds to help with Error 153 issues.
|
||||
|
||||
|
||||
## 1.2.5 - First aimed community release
|
||||
- First aimed community release of **Audio Pluglist**.
|
||||
- Added **External link** support.
|
||||
|
|
|
|||
136
FULLGUIDE.md
Normal file
136
FULLGUIDE.md
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
# Audio PlugList - How To
|
||||
|
||||
This guide walks you through setup, use, hotkeys, and common fixes for **Audio PlugList**.
|
||||
It's very easy without the guild but full details are below if needed.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. In Obsidian, go to **Settings → Community plugins → Audio PlugList → Enable**.
|
||||
- **Alternative Method** - If installing via **BRAT**: Select **Add beta plugin**, then enter the repo address: https://github.com/ragetrip/audio-pluglist and select the latest release.
|
||||
- **Alternative Method** - If manual install. Simply download the latest release (main.js, manifest.json, and styles.css) and unzip into your vault at **.obsidian/plugins/audio-pluglist**
|
||||
2. Open the plugin’s **Tab view** (the sidebar view), or use the **footer mini-player**.
|
||||
3. Create at least one playlist:
|
||||
- **Folder playlist (📂)**: points to a local folder of audio files in your vault.
|
||||
- **Link playlist (🔗)**: paste a playlist URL (YouTube, SoundCloud, Spotify, etc.).
|
||||
|
||||
> Tip: In dropdowns, folder playlists are prefixed **📂**, link playlists are **🔗** (only in dropdowns; names in Settings remain clean).
|
||||
|
||||
---
|
||||
|
||||
## Creating Playlists
|
||||
|
||||
### 📂 Folder-based
|
||||
- In **Settings → Audio PlugList**, click **Add Playlist From Folder**.
|
||||
- Choose a folder in your vault containing audio files.
|
||||
- Name the playlist.
|
||||
|
||||
**Behavior**
|
||||
- Full control via the plugin’s `<audio>` element.
|
||||
- Supports autoplay, hotkeys, and seek/skip (depending on your UI config).
|
||||
|
||||
### 🔗 Link-based
|
||||
- In **Settings → Audio PlugList**, click **Add Playlist From Link**.
|
||||
- Paste a share link to a playlist (YouTube, SoundCloud, Spotify, etc.).
|
||||
- Name the playlist.
|
||||
|
||||
**Behavior**
|
||||
- Renders an **embedded player** in the Tab view (iframe).
|
||||
- Controls are provided by the service; browser policies may block autoplay.
|
||||
- Optional **Open In Browser** launches the playlist in your default browser/app.
|
||||
|
||||
> Note: Some providers (e.g., Spotify without Premium SDK) only allow ~30s previews in embeds. “Open in Browser” is the recommended experience.
|
||||
|
||||
---
|
||||
|
||||
## Using the Footer Mini-Player
|
||||
|
||||
- Choose a playlist from the **footer dropdown** (📂/🔗 prefixes show type).
|
||||
- Use the **Shuffle** and **Repeat** pills as desired.
|
||||
- Footer controls fully manage **📂 folder** playlists.
|
||||
- For **🔗 link** playlists, use the **Tab view** embed controls or **Open In Browser**.
|
||||
|
||||
---
|
||||
|
||||
## Using the Tab View
|
||||
|
||||
- Open Audio PlugList via Ribbon Icon (🎵) or via Commands.
|
||||
- Select a playlist from the dropdown (📂/🔗 prefixes).
|
||||
- For link based playlists, the embed renders in an **embed box**.
|
||||
|
||||
---
|
||||
|
||||
## Re-scan Options
|
||||
|
||||
- **Re-scan playlists (All)**: Refreshes all playlists (detect new tracks / update embeds).
|
||||
- **Re-scan playlist (Single)**: In the Tab view, next to **Optional: Open In Browser**, click **Re-scan playlist** to refresh only the selected playlist.
|
||||
|
||||
---
|
||||
|
||||
## Hotkeys (Per-Playlist Commands)
|
||||
|
||||
**What you get**
|
||||
- The plugin registers **one command per playlist** automatically:
|
||||
- `Toggle Play/Pause - <Playlist Name>`
|
||||
- Bind keys in **Obsidian Settings → Hotkeys** or from **Community plugins → Audio PlugList → Hotkeys**.
|
||||
|
||||
**Behavior**
|
||||
- If a different playlist is active, the command switches to that playlist and toggles play/pause.
|
||||
- After **adding/renaming** playlists, the plugin updates command labels and callbacks when settings are saved (or after a re-scan).
|
||||
|
||||
> Tip: If you reorder playlists and a binding seems off, open the plugin settings and re-scan. Commands refresh in place without losing your keybindings.
|
||||
|
||||
---
|
||||
|
||||
## Known Limitations (Embeds)
|
||||
|
||||
- **Autoplay & control** for 🔗 link playlists depend on the provider:
|
||||
- YouTube/SoundCloud allow limited programmatic control; autoplay may still be blocked by browser policies.
|
||||
- **Spotify** embeds without the Web Playback SDK are limited to **previews**. Full playback requires Premium + OAuth + SDK, which this plugin deliberately avoids (use **Open In Browser**).
|
||||
- **Deezer** login overlays can block embed controls; when detected, use **Open In Browser**.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### I can’t enable the plugin (red error)
|
||||
- Ensure your plugin folder contains exactly:
|
||||
- `main.js`, `manifest.json`, `styles.css`.
|
||||
- Confirm `manifest.json` has a valid `"version"` and `"minAppVersion"` that matches your Obsidian.
|
||||
- If you’ve hand-edited `main.js`, check for missing braces/commas (use a linter or open dev tools).
|
||||
- Restart Obsidian after replacing files or making changes to any of these three files.
|
||||
|
||||
### Audio keeps playing when I switch playlists between Footer/Tab
|
||||
- Make sure you're running build 1.2.6 (current release at time of writing) or later, where switching playlists calls a universal stop (Footer audio pauses and Tab embeds are blanked).
|
||||
- If an embed still keeps playing, open the Tab and pause/close the provider player; some providers buffer aggressively.
|
||||
|
||||
### My per-playlist hotkeys disappeared / look wrong
|
||||
- Open **Settings → Audio PlugList** and click **re-scan**. Commands update in place without losing existing bindings.
|
||||
- Re-bind only if you renamed and want a new label visible in Hotkeys search.
|
||||
|
||||
### Link playlist won’t autoplay in the Tab
|
||||
- This is expected in many browsers due to autoplay policies. Use the embed’s native play button or **Open In Browser**.
|
||||
|
||||
### Deezer shows a login / blocked controls
|
||||
- Use **Open In Browser**. Some Deezer embeds require an active session and won’t play reliably inside the iframe.
|
||||
|
||||
---
|
||||
|
||||
## Support / Issues
|
||||
|
||||
- If you run into something odd:
|
||||
- Copy the first error from **Developer Tools → Console** (View → Toggle Developer Tools).
|
||||
- Note which provider you’re using (YouTube, SoundCloud, Spotify, etc.).
|
||||
- Include your Obsidian version and plugin version.
|
||||
- Post this in the **Issues** section here: https://github.com/ragetrip/audio-pluglist/issues
|
||||
|
||||
---
|
||||
|
||||
## Feature Request / Suggestions
|
||||
|
||||
- If you'd like to suggest a feature or recommendation:
|
||||
- Visit the Repo's **Discussions** section here: https://github.com/ragetrip/audio-pluglist/discussions
|
||||
|
||||
|
||||
Happy listening!
|
||||
47
README.md
47
README.md
|
|
@ -1,8 +1,8 @@
|
|||
# Audio PlugList
|
||||
|
||||
Playlist-based audio player for Obsidian. Point a playlist at a vault **folder** (auto-index audio files), an external **link** (YouTube, Spotify, Deezer, SoundCloud, or External Media File), then control playback from a clean **tab view** and a compact **footer mini-player** with shuffle/repeat and optional fade-out stop.
|
||||
Playlist-based audio player for Obsidian. Point a playlist at a vault **folder** (auto-index audio files), an external **link** (with multi site support), then control playback from a clean **tab view** and a compact **footer mini-player** with shuffle/repeat, optional fade-out stop, and hotkey support.
|
||||
|
||||
> **Version:** 1.2.5 · **Obsidian:** 1.5.0+ · **Platforms:** Desktop & Mobile
|
||||
> **Version:** 1.2.6 - **Obsidian:** 1.5.0+ - **Platforms:** Desktop & Mobile
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -10,43 +10,45 @@ Playlist-based audio player for Obsidian. Point a playlist at a vault **folder**
|
|||
|
||||
- **Playlists**
|
||||
- Create from a **Folder** (recursively indexes supported audio files).
|
||||
- Create from a **Link** (direct audio URL).
|
||||
- Create from a **Link** (external site support).
|
||||
- **Footer mini-player** with segmented controls (⏮ ⏯ ⏭ ⏹ 🔀 🔁), seek, volume, and a marquee **Now Playing** title.
|
||||
- **Tab view** showing current playlist, track list, and playback controls.
|
||||
- **Shuffle / Repeat** (Off · All · One).
|
||||
- **Shuffle / Repeat** (Off - All - One).
|
||||
- **Fade-out on Stop** with adjustable duration.
|
||||
- **Optional columns**: show Artist and/or Album in the track list.
|
||||
- **Quick re-scan**: index one playlist or **Scan All** from settings.
|
||||
- **File name parser**: `Artist - Title` (em dash or hyphen) convenience.
|
||||
- **Hotkey support** for assignable play/pause command per playlist.
|
||||
- **Browser Button** for easy access to external links if wanted or needed.
|
||||
|
||||
**Supported formats:** `mp3`, `wav`, `m4a`, `flac`, `ogg`, `aac`.
|
||||
|
||||
---
|
||||
|
||||
## 📸 PREVIEW | Easily create playlists from internal library or external link
|
||||
| Add Playlist from Library or Link | Name and Configure |
|
||||
## 📸 PREVIEW | Easily create playlists from internal library or external links
|
||||
| Library Based Playlist | External Link Based Playlist |
|
||||
| --- | --- |
|
||||
| .png) | .png) |
|
||||
| .png) | .png) |
|
||||
|
||||
---
|
||||
|
||||
## 📸 PREVIEW | Tab support, or Footer Mini-Player support (toggle mini-player on/off)
|
||||
## 📸 PREVIEW | Tab support, or Footer Mini-Player support (toggle mini-player on/off) with easy playlist selections
|
||||
| Tab View | Footer Only View |
|
||||
| --- | --- |
|
||||
| .png) | .png) |
|
||||
| .png) | .png) |
|
||||
|
||||
---
|
||||
|
||||
## 📸 PREVIEW | Easy playlist access and external playlist support
|
||||
| Playlist Drop Down | External Support |
|
||||
## 📸 PREVIEW | Easy menu and hotlink support
|
||||
| Settings | Hotlink Support |
|
||||
| --- | --- |
|
||||
| .png) | .png) |
|
||||
| .png) | .png) |
|
||||
|
||||
---
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
### Option A: Manual (first GitHub upload)
|
||||
### Option A: Manual
|
||||
1. Download the latest release assets (`main.js`, `manifest.json`, `styles.css`) from this repository.
|
||||
2. Create a folder inside your vault at:
|
||||
`.obsidian/plugins/audio-pluglist/`
|
||||
|
|
@ -90,27 +92,30 @@ After the plugin is accepted into the Obsidian Community Plugin store, you’ll
|
|||
- **Folder** (for folder playlists) + **Browse** + **Scan now**
|
||||
- **Link** (for link playlists)
|
||||
- **Delete playlist**
|
||||
- **Re-scan all playlists**
|
||||
- **Show Album in titles**
|
||||
- **Show Artist in titles**
|
||||
- **Show Footer mini-player**
|
||||
- **Fade out on Stop**
|
||||
- **Fade duration (ms)**
|
||||
- **Re-scan all playlists** - Index local folder playlists, re-sync external link connections. Also available on Tab and Footer Mini-Player.
|
||||
- **YouTube privacy (youtube-nocookie.com)** - Assigns privacy-enhanced domain for youtube embeds to help with Error 153 from blocked youtube links.
|
||||
- **Show Album in titles** - Based on local file data.
|
||||
- **Show Artist in titles** - Based on local file data.
|
||||
- **Show Footer mini-player** - Toggles Footer Mini-Player on/off.
|
||||
- **Fade out on Stop** - Toggle to enable/disable a user configured fade out on Stop command.
|
||||
- **Fade duration (ms)** - Adjust by ms to user preferred amount.
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Known Limitations
|
||||
|
||||
- **External links**: Some linked playlists require **log-in**; some services block external playback.
|
||||
- **External links**: Some linked playlists require **log-in**; some services block external playback. **Re-scan playlis(s)** feature added to help re-attempt broken links.
|
||||
- **Mobile platforms** Behavior depends on OS media policies (e.g., background playback limits).
|
||||
|
||||
---
|
||||
|
||||
## 📝 Changelog
|
||||
|
||||
### 1.2.5 - Release Notes
|
||||
### 1.2.6 - Release Notes
|
||||
- First expected community release of **Audio PlugList**.
|
||||
- Dynamic playlists (Folder/Link), footer mini-player, shuffle/repeat, fade-out stop, artist/album columns, and scan tools.
|
||||
- **Hotkey support** for single command play/pause or external link call.
|
||||
- Added **FULLGUIDE.md** for detailed guide and **troubleshooting tips**.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
153
main.js
153
main.js
|
|
@ -47,6 +47,17 @@ function getSpotifyPlaylistId(input){
|
|||
}
|
||||
function spotifyEmbedSrc(id){ return `https://open.spotify.com/embed/playlist/${id}`; }
|
||||
|
||||
function getDeezerPlaylistId(input){
|
||||
try{
|
||||
const u = new URL(input);
|
||||
const m = u.pathname.match(/\/playlist\/(\d+)/i);
|
||||
if (m) return m[1];
|
||||
}catch(e){}
|
||||
const rx = /deezer\.com\/(?:[a-z]{2}\/)?playlist\/(\d+)/i;
|
||||
const m2 = (input||'').match(rx);
|
||||
return m2 ? m2[1] : null;
|
||||
}
|
||||
|
||||
|
||||
function clamp(n, a, b){ if(n<a)return a; if(n>b)return b; return n; }
|
||||
function fmtTime(s) {
|
||||
|
|
@ -67,24 +78,17 @@ function parseArtistTitle(basename){
|
|||
if (parts.length >= 2) return { artist: parts[0], title: parts.slice(1).join(' - ') };
|
||||
return { artist: '', title: basename };
|
||||
}
|
||||
function fillPlaylistOptions(plugin, sel){
|
||||
clearEl(sel);
|
||||
if (!plugin.settings.playlists.length){
|
||||
var opt = mk('option', '', 'No playlists');
|
||||
opt.value = '-1';
|
||||
sel.appendChild(opt);
|
||||
sel.disabled = true;
|
||||
return;
|
||||
function fillPlaylistOptions(self, sel){
|
||||
sel.innerHTML='';
|
||||
var pls = self.settings.playlists;
|
||||
var opt0 = document.createElement('option'); opt0.value='-1'; opt0.textContent='(no playlist selected)'; sel.appendChild(opt0);
|
||||
for (var i=0;i<pls.length;i++){
|
||||
var cfg = pls[i]||{};
|
||||
var name = (cfg.name||('Playlist '+(i+1)));
|
||||
if (cfg.type==='folder') name = '📂 ' + name;
|
||||
else if (cfg.type==='link') name = '🔗 ' + name;
|
||||
var opt = document.createElement('option'); opt.value=String(i); opt.textContent=name; sel.appendChild(opt);
|
||||
}
|
||||
sel.disabled = false;
|
||||
for (var i=0;i<plugin.settings.playlists.length;i++){
|
||||
var p = plugin.settings.playlists[i] || {};
|
||||
var label = (p.name && p.name.length) ? p.name : ('Playlist ' + (i+1));
|
||||
var opt = mk('option', '', label);
|
||||
opt.value = String(i);
|
||||
sel.appendChild(opt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Simple modal folder picker */
|
||||
|
|
@ -270,10 +274,20 @@ SettingsTab.prototype.display = function(){
|
|||
// Break line above "Re-scan all playlists" //
|
||||
containerEl.createEl('div', { cls: 'apl-section-break' });
|
||||
|
||||
// Keep remaining settings area as-is for better grouping. Looks better in on smaller devices //
|
||||
// Keep remaining settings area as-is for better grouping. Looks better in on smaller devices like iPad and mobile devices //
|
||||
new Setting(containerEl)
|
||||
.setName('Re-scan all playlists')
|
||||
.addButton(function(btn){ btn.setButtonText('Scan All').onClick(() => self.plugin.indexAll()); });
|
||||
.addButton(function(btn){ btn.setButtonText('Scan All').onClick(() => self.plugin.indexAll());
|
||||
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('YouTube privacy (youtube-nocookie.com)')
|
||||
.setDesc('Use the privacy-enhanced domain for YouTube embeds. Helps avoid Error 153.')
|
||||
.addToggle(t=>{
|
||||
t.setValue(self.plugin.settings.youtubePrivacy === true)
|
||||
.onChange(async v=>{ self.plugin.settings.youtubePrivacy = !!v; await self.plugin.saveSettings(); });
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Show Album in titles')
|
||||
|
|
@ -338,7 +352,13 @@ View.prototype.render = function(){
|
|||
col.appendChild(rowTop);
|
||||
var sel = mk('select');
|
||||
rowTop.appendChild(sel);
|
||||
fillPlaylistOptions(this.plugin, sel); if (this.plugin.currentPlaylist>=0) sel.value = String(this.plugin.currentPlaylist);
|
||||
fillPlaylistOptions(this.plugin, sel);
|
||||
// Add refresh button next to playlist dropdown
|
||||
var refreshBtn = mk('button','ap-refresh-btn','⟳');
|
||||
refreshBtn.title = 'Re-scan playlists';
|
||||
refreshBtn.onclick = function(){ try{ this.plugin.indexAll(); new Notice('Audio PlugList: Re-scanning playlists…'); }catch(e){} }.bind(this);
|
||||
rowTop.appendChild(refreshBtn);
|
||||
if (this.plugin.currentPlaylist>=0) sel.value = String(this.plugin.currentPlaylist);
|
||||
sel.onchange = function(){ if (sel.value!=='-1') this.plugin.setCurrentPlaylist(Number(sel.value)); }.bind(this);
|
||||
|
||||
// Now playing title //
|
||||
|
|
@ -403,6 +423,12 @@ var wrap = mk('div','ap-embedbox');
|
|||
var btn = mk('button','ap-open-ext ap-open-ext--mini','Optional: Open In Browser');
|
||||
btn.onclick = function(){ try{ window.open(_openSrc, '_blank'); }catch(e){} };
|
||||
btnRow.appendChild(btn);
|
||||
// Add single-playlist re-scan button (mini)
|
||||
var resBtn = mk('button','ap-open-ext ap-open-ext--mini','Re-scan playlist');
|
||||
resBtn.style.marginLeft = '8px';
|
||||
resBtn.onclick = function(){ try{ var _cp = cp; this.plugin.indexPlaylist(_cp).then(()=>{ new Notice('Audio PlugList: re-scanned this playlist.'); if (this.plugin.onUiRefresh) this.plugin.onUiRefresh(); }); }catch(e){} }.bind(this);
|
||||
btnRow.appendChild(resBtn);
|
||||
|
||||
col.appendChild(btnRow);
|
||||
} catch(e){}
|
||||
|
||||
|
|
@ -523,7 +549,8 @@ module.exports = class AudioPlugList extends Plugin {
|
|||
showArtist: false,
|
||||
showFooter: true,
|
||||
fadeOnStop: true,
|
||||
fadeMs: 3000
|
||||
fadeMs: 3000,
|
||||
youtubePrivacy: true
|
||||
}, await this.loadData());
|
||||
|
||||
this.tracksByPlaylist = Array.from({length: this.settings.playlists.length}, ()=>[]);
|
||||
|
|
@ -532,8 +559,8 @@ module.exports = class AudioPlugList extends Plugin {
|
|||
this.queue = [];
|
||||
this.index = -1;
|
||||
this.audio = new Audio();
|
||||
this.shuffle = true; // default 'true' by request //
|
||||
this.repeatMode = 'all'; // default 'all' by request //
|
||||
this.shuffle = true; // default 'true' by user request //
|
||||
this.repeatMode = 'all'; // default 'all' by user request //
|
||||
this.onUiRefresh = null;
|
||||
|
||||
this.audio.addEventListener('ended', this._onEnded.bind(this));
|
||||
|
|
@ -552,6 +579,9 @@ module.exports = class AudioPlugList extends Plugin {
|
|||
|
||||
this.addSettingTab(new SettingsTab(this.app, this));
|
||||
|
||||
// Register per-playlist hotkeys (one command per playlist)
|
||||
this._registerPlaylistHotkeys();
|
||||
|
||||
if (this.settings.autoIndexOnLoad){
|
||||
const run = async ()=>{ try{ await this.indexAll(); }catch(e){} };
|
||||
if (this.app.workspace && this.app.workspace.onLayoutReady){ this.app.workspace.onLayoutReady(run); }
|
||||
|
|
@ -567,7 +597,7 @@ module.exports = class AudioPlugList extends Plugin {
|
|||
this.audio.pause(); this.audio.src = '';
|
||||
}
|
||||
|
||||
async saveSettings(){ await this.saveData(this.settings); }
|
||||
async saveSettings(){ await this.saveData(this.settings); try{ this._registerPlaylistHotkeys(); }catch(e){} }
|
||||
|
||||
async activateView(){
|
||||
var leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE);
|
||||
|
|
@ -587,7 +617,8 @@ module.exports = class AudioPlugList extends Plugin {
|
|||
var el = mk('div', 'ap-footer-player');
|
||||
|
||||
var sel = mk('select');
|
||||
fillPlaylistOptions(this, sel); if (this.currentPlaylist>=0) sel.value = String(this.currentPlaylist);
|
||||
fillPlaylistOptions(this, sel);
|
||||
if (this.currentPlaylist>=0) sel.value = String(this.currentPlaylist);
|
||||
sel.onchange = function(){ if (sel.value!=='-1') this.setCurrentPlaylist(Number(sel.value)); }.bind(this);
|
||||
|
||||
// Segmented controls ⏮ | ⏯ | ⏭ | ⏹ | 🔀 | 🔁 - some of these had to be done the way that are. Switched from 1.0.3 due to mobile and device view issues so below ones are different but working. //
|
||||
|
|
@ -667,15 +698,72 @@ module.exports = class AudioPlugList extends Plugin {
|
|||
});
|
||||
}
|
||||
|
||||
setCurrentPlaylist(i){ if (!this.settings.playlists.length){ this.currentPlaylist=-1; this._applyPlaylistQueue(); if(this.onUiRefresh) this.onUiRefresh(); return; } this.currentPlaylist = clamp(i,0,this.settings.playlists.length-1);
|
||||
setCurrentPlaylist(i){
|
||||
if (!this.settings.playlists.length){
|
||||
this.currentPlaylist = -1;
|
||||
this._applyPlaylistQueue();
|
||||
if (typeof this.onUiRefresh === 'function') this.onUiRefresh();
|
||||
return;
|
||||
}
|
||||
var next = clamp(i,0,this.settings.playlists.length-1);
|
||||
if (this.currentPlaylist !== next){
|
||||
try{ this._stopAllPlayback(); }catch(_){ }
|
||||
}
|
||||
this.currentPlaylist = next;
|
||||
this.settings.currentPlaylist = this.currentPlaylist;
|
||||
this.saveSettings();
|
||||
this._applyPlaylistQueue();
|
||||
if (typeof this.onUiRefresh === 'function') this.onUiRefresh();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Per-playlist Hotkeys //
|
||||
togglePlaylistByIndex(i){
|
||||
try{
|
||||
i = Number(i);
|
||||
if (!isFinite(i)) return;
|
||||
if (i<0 || i >= (this.settings.playlists ? this.settings.playlists.length : 0)) return;
|
||||
if (this.currentPlaylist !== i) this.setCurrentPlaylist(i);
|
||||
this.togglePlay();
|
||||
}catch(e){ try{console.error('APL togglePlaylistByIndex error', e);}catch(_){} }
|
||||
}
|
||||
|
||||
_registerPlaylistHotkeys(){
|
||||
try{
|
||||
const cmdApi = (this.app && this.app.commands) ? this.app.commands : null;
|
||||
const cmds = cmdApi && cmdApi.commands ? cmdApi.commands : {};
|
||||
const pls = (this.settings && Array.isArray(this.settings.playlists)) ? this.settings.playlists : [];
|
||||
for (let i=0;i<pls.length;i++){
|
||||
const cfg = pls[i] || {};
|
||||
const id = `apl-toggle-playlist-${i}`;
|
||||
const label = `Toggle Play/Pause — ${cfg.name || ('Playlist ' + (i+1))}`;
|
||||
if (!(cmds && cmds[id])){
|
||||
this.addCommand({ id, name: label, callback: ()=> this.togglePlaylistByIndex(i) });
|
||||
} else {
|
||||
// Update name & callback in place so bindings persist //
|
||||
try { cmds[id].name = label; } catch(_){}
|
||||
try { cmds[id].callback = ()=> this.togglePlaylistByIndex(i); } catch(_){}
|
||||
}
|
||||
}
|
||||
}catch(e){ try{console.error('APL _registerPlaylistHotkeys error', e);}catch(_){} }
|
||||
}
|
||||
|
||||
|
||||
// Indexing //
|
||||
async indexAll(){ for (var i=0;i<this.settings.playlists.length;i++) await this.indexPlaylist(i); this._applyPlaylistQueue(); }
|
||||
|
||||
// Stop any active playback (internal audio + external embeds) //
|
||||
_stopAllPlayback(){
|
||||
try{
|
||||
if (this.audio){ try{ this.audio.pause(); }catch(_){ } try{ this.audio.currentTime = 0; }catch(_){ } }
|
||||
try{
|
||||
var frames = document.querySelectorAll('.ap-embedbox iframe');
|
||||
frames.forEach(function(fr){ try{ fr.src = 'about:blank'; }catch(_){ } });
|
||||
}catch(_){ }
|
||||
}catch(_){ }
|
||||
try{ if (this.onUiRefresh) this.onUiRefresh(); }catch(_){ }
|
||||
}
|
||||
async indexAll(){ for (var i=0;i<this.settings.playlists.length;i++) await this.indexPlaylist(i); this._applyPlaylistQueue(); }
|
||||
async indexPlaylist(i){
|
||||
var cfg = this.settings.playlists[i] || {};
|
||||
// LINK-based playlist support //
|
||||
|
|
@ -690,7 +778,7 @@ module.exports = class AudioPlugList extends Plugin {
|
|||
var ytid = getYouTubePlaylistId(link);
|
||||
if (ytid){
|
||||
this.tracksByPlaylist[i] = [];
|
||||
this.embedByPlaylist[i] = { provider:'youtube', src: ytPlaylistEmbedSrc(ytid), height: 380, original: link };
|
||||
this.embedByPlaylist[i] = { provider:'youtube', src: (self?self.plugin.settings.youtubePrivacy:this.settings.youtubePrivacy) ? `https://www.youtube-nocookie.com/embed/videoseries?list=${encodeURIComponent(ytid)}&rel=0&modestbranding=1&enablejsapi=1` : `https://www.youtube.com/embed/videoseries?list=${encodeURIComponent(ytid)}&rel=0&modestbranding=1&enablejsapi=1`, height: 480, original: link };
|
||||
new Notice('Audio PlugList: embedded YouTube playlist.');
|
||||
return;
|
||||
}
|
||||
|
|
@ -711,6 +799,15 @@ module.exports = class AudioPlugList extends Plugin {
|
|||
this.embedByPlaylist[i] = { provider:'spotify', src: spotifyEmbedSrc(spid), height: 380, original: link };
|
||||
new Notice('Audio PlugList: embedded Spotify playlist.');
|
||||
return;
|
||||
// Deezer playlist (no iframe embed; open externally due to login overlay) //
|
||||
var dzid = getDeezerPlaylistId(link);
|
||||
if (dzid){
|
||||
this.tracksByPlaylist[i] = []; // avoid trying to parse tracks here
|
||||
this.embedByPlaylist[i] = null; // no embed; rely on action row buttons
|
||||
try { new Notice('Deezer playlist detected. Use "Open In Browser" to play.'); } catch(e) {}
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Direct audio URL //
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"id": "audio-pluglist",
|
||||
"name": "Audio PlugList",
|
||||
"version": "1.2.5",
|
||||
"version": "1.2.6",
|
||||
"minAppVersion": "1.5.0",
|
||||
"description": "Create playlists from folders, files, or links (YouTube/SoundCloud/Spotify/direct audio). Tab view + footer controls.",
|
||||
"description": "Create playlists from folders, files, or external links. Tab view + footer controls. Hotlink support",
|
||||
"author": "Ragetrip",
|
||||
"authorUrl": "https://github.com/ragetrip",
|
||||
"fundingUrl": "https://buymeacoffee.com/ragetrip",
|
||||
|
|
|
|||
34
styles.css
34
styles.css
|
|
@ -11,7 +11,7 @@
|
|||
border-top:1px solid var(--background-modifier-border);
|
||||
background:var(--background-primary);
|
||||
z-index:1000;
|
||||
width:50vw;
|
||||
width:52vw;
|
||||
}
|
||||
.ap-footer-player select{ font-size:12px }
|
||||
.ap-footer-player .ap-title{ position:relative; overflow:hidden; white-space:nowrap; width:26ch; max-width:26ch; }
|
||||
|
|
@ -198,7 +198,7 @@
|
|||
.ap-diag-panel { margin-top: 6px; padding: 6px; border: 1px dashed var(--background-modifier-border); border-radius: 6px; font-size: 0.85em; }
|
||||
.ap-diag-line { margin: 2px 0; word-break: break-all; }
|
||||
|
||||
.ap-embedbox{min-height:180px}
|
||||
.ap-embedbox{height:205px} /* set at 205px for youtube tracklist so you can see track2 to skip in case of error 153 */
|
||||
.ap-embedbox iframe{display:block;width:100%;height:100%}
|
||||
|
||||
/* Make repeat pill match shuffle pill */
|
||||
|
|
@ -222,8 +222,32 @@
|
|||
height: 14px;
|
||||
}
|
||||
|
||||
/* APL footer: Better alignment for the repeat button that carries over to smaller hardware/devices */
|
||||
/* APL footer: Used for smaller hardware/devices */
|
||||
.ap-footer-player .ap-footer-group button[title^="Repeat"] {
|
||||
padding-left: 4px !important;
|
||||
padding-right: 16px !important;
|
||||
}
|
||||
padding-right: 10px !important;
|
||||
}
|
||||
|
||||
/* small refresh button next to playlist dropdowns */
|
||||
.ap-refresh-btn{
|
||||
margin-left: 8px;
|
||||
padding: 2px 8px;
|
||||
height: 24px;
|
||||
line-height: 20px;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 6px;
|
||||
background: var(--background-secondary);
|
||||
cursor: pointer;
|
||||
}
|
||||
.ap-refresh-btn:hover{ background: var(--background-modifier-hover); }
|
||||
|
||||
|
||||
/* Balance shuffle/repeat pill widths */
|
||||
.ap-shuffle button, .ap-shuffle {
|
||||
padding-left: 6px !important;
|
||||
padding-right: 6px !important;
|
||||
}
|
||||
.ap-repeat button, .ap-repeat {
|
||||
padding-left: 6px !important;
|
||||
padding-right: 6px !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"1.2.5": "1.5.0"
|
||||
}
|
||||
"1.2.6": "1.5.0"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue