Add tracklist to audio player
This commit is contained in:
@@ -29,7 +29,7 @@ export const navigate = async (path, push_history) => {
|
|||||||
console.debug("Navigating to path", path, push_history)
|
console.debug("Navigating to path", path, push_history)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let resp = await fs_get_node(path)
|
const resp = await fs_get_node(path)
|
||||||
open_node(resp, push_history)
|
open_node(resp, push_history)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.value && err.value === "path_not_found") {
|
if (err.value && err.value === "path_not_found") {
|
||||||
@@ -59,7 +59,7 @@ export const open_node = (node, push_history) => {
|
|||||||
// to a 404 page when refreshing after renaming a file
|
// to a 404 page when refreshing after renaming a file
|
||||||
if (history_enabled) {
|
if (history_enabled) {
|
||||||
window.document.title = node.path[node.base_index].name+" ~ pixeldrain"
|
window.document.title = node.path[node.base_index].name+" ~ pixeldrain"
|
||||||
let url = "/d"+ fs_encode_path(node.path[node.base_index].path)
|
const url = "/d"+ fs_encode_path(node.path[node.base_index].path)
|
||||||
if (push_history) {
|
if (push_history) {
|
||||||
window.history.pushState({}, window.document.title, url)
|
window.history.pushState({}, window.document.title, url)
|
||||||
} else {
|
} else {
|
||||||
@@ -72,8 +72,8 @@ export const open_node = (node, push_history) => {
|
|||||||
if (node.path.length > 1 && node.path[node.path.length-2].path === state.base.path) {
|
if (node.path.length > 1 && node.path[node.path.length-2].path === state.base.path) {
|
||||||
console.debug("Current parent path and new node path match. Saving siblings")
|
console.debug("Current parent path and new node path match. Saving siblings")
|
||||||
|
|
||||||
siblings_path = node.path[node.path.length-1].path
|
cached_siblings_path = node.path[node.path.length-1].path
|
||||||
siblings = state.children
|
cached_siblings = state.children
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort directory children
|
// Sort directory children
|
||||||
@@ -104,8 +104,25 @@ export const open_node = (node, push_history) => {
|
|||||||
// directory. The siblings_path variable is used to verify that the parent
|
// directory. The siblings_path variable is used to verify that the parent
|
||||||
// directory is still the same. If it's sifferent the siblings array is not
|
// directory is still the same. If it's sifferent the siblings array is not
|
||||||
// used
|
// used
|
||||||
let siblings_path = ""
|
let cached_siblings_path = ""
|
||||||
let siblings = null
|
let cached_siblings = null
|
||||||
|
|
||||||
|
export const get_siblings = async () => {
|
||||||
|
// Check if we already have siblings cached
|
||||||
|
if (cached_siblings === null || cached_siblings_path !== state.path[state.path.length - 2].path) {
|
||||||
|
console.debug("Cached siblings not available. Fetching new")
|
||||||
|
const resp = await fs_get_node(state.path[state.path.length - 2].path)
|
||||||
|
|
||||||
|
// Sort directory children to make sure the order is consistent
|
||||||
|
sort_children(resp.children)
|
||||||
|
|
||||||
|
// Save new siblings in navigator state
|
||||||
|
cached_siblings_path = state.path[state.path.length - 2].path
|
||||||
|
cached_siblings = resp.children
|
||||||
|
}
|
||||||
|
|
||||||
|
return cached_siblings
|
||||||
|
}
|
||||||
|
|
||||||
// Opens a sibling of the currently open file. The offset is relative to the
|
// Opens a sibling of the currently open file. The offset is relative to the
|
||||||
// file which is currently open. Give a positive number to move forward and a
|
// file which is currently open. Give a positive number to move forward and a
|
||||||
@@ -117,26 +134,14 @@ export const open_sibling = async offset => {
|
|||||||
|
|
||||||
dispatch("loading", true)
|
dispatch("loading", true)
|
||||||
|
|
||||||
// Check if we already have siblings cached
|
let siblings
|
||||||
if (siblings != null && siblings_path == state.path[state.path.length - 2].path) {
|
try {
|
||||||
console.debug("Using cached siblings", siblings)
|
siblings = await get_siblings()
|
||||||
} else {
|
} catch (err) {
|
||||||
console.debug("Cached siblings not available. Fetching new")
|
console.error(err)
|
||||||
try {
|
alert(err)
|
||||||
let resp = await fs_get_node(state.path[state.path.length - 2].path)
|
dispatch("loading", false)
|
||||||
|
return
|
||||||
// Sort directory children to make sure the order is consistent
|
|
||||||
sort_children(resp.children)
|
|
||||||
|
|
||||||
// Save new siblings in navigator state
|
|
||||||
siblings_path = state.path[state.path.length - 2].path
|
|
||||||
siblings = resp.children
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
alert(err)
|
|
||||||
dispatch("loading", false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let next_sibling = null
|
let next_sibling = null
|
||||||
@@ -191,7 +196,7 @@ const sort_children = children => {
|
|||||||
// Capture browser back and forward navigation buttons
|
// Capture browser back and forward navigation buttons
|
||||||
window.onpopstate = (e) => {
|
window.onpopstate = (e) => {
|
||||||
// Get the part of the URL after the fs root and navigate to it
|
// Get the part of the URL after the fs root and navigate to it
|
||||||
let path = document.location.pathname.replace("/d/", "")
|
const path = document.location.pathname.replace("/d/", "")
|
||||||
navigate(decodeURIComponent(path), false)
|
navigate(decodeURIComponent(path), false)
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@@ -83,7 +83,7 @@ export let large_icons = false
|
|||||||
.node {
|
.node {
|
||||||
display: table-row;
|
display: table-row;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: var(--text-color);
|
color: var(--body_text-color);
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
}
|
}
|
||||||
.node:not(:last-child) {
|
.node:not(:last-child) {
|
||||||
|
@@ -1,58 +1,47 @@
|
|||||||
<script>
|
<script>
|
||||||
import { createEventDispatcher, onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
import { fs_path_url } from '../FilesystemUtil';
|
import { fs_encode_path, fs_node_icon, fs_path_url } from '../FilesystemUtil';
|
||||||
import FileTitle from '../../file_viewer/viewers/FileTitle.svelte';
|
import FileTitle from '../../file_viewer/viewers/FileTitle.svelte';
|
||||||
let dispatch = createEventDispatcher()
|
import TextBlock from '../../file_viewer/viewers/TextBlock.svelte';
|
||||||
|
|
||||||
|
export let fs_navigator
|
||||||
export let state
|
export let state
|
||||||
let player
|
let player
|
||||||
let playing = false
|
let playing = false
|
||||||
let media_session = false
|
let media_session = false
|
||||||
|
let siblings = []
|
||||||
|
|
||||||
const toggle_play = () => playing ? player.pause() : player.play()
|
const toggle_play = () => playing ? player.pause() : player.play()
|
||||||
|
|
||||||
// Detect when the song changes
|
export const update = async () => {
|
||||||
$: update_session_meta(state.base.name)
|
|
||||||
|
|
||||||
const update_session_meta = name => {
|
|
||||||
if (media_session) {
|
if (media_session) {
|
||||||
navigator.mediaSession.metadata = new MediaMetadata({
|
navigator.mediaSession.metadata = new MediaMetadata({
|
||||||
title: name,
|
title: state.base.name,
|
||||||
artist: "pixeldrain",
|
artist: "pixeldrain",
|
||||||
album: "unknown",
|
album: "unknown",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
siblings = await fs_navigator.get_siblings()
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if ('mediaSession' in navigator) {
|
if ('mediaSession' in navigator) {
|
||||||
media_session = true
|
media_session = true
|
||||||
update_session_meta(state.base.name)
|
|
||||||
|
|
||||||
navigator.mediaSession.setActionHandler('play', () => player.play());
|
navigator.mediaSession.setActionHandler('play', () => player.play());
|
||||||
navigator.mediaSession.setActionHandler('pause', () => player.pause());
|
navigator.mediaSession.setActionHandler('pause', () => player.pause());
|
||||||
navigator.mediaSession.setActionHandler('stop', () => player.stop());
|
navigator.mediaSession.setActionHandler('stop', () => player.stop());
|
||||||
navigator.mediaSession.setActionHandler('previoustrack', () => dispatch("open_sibling", -1));
|
navigator.mediaSession.setActionHandler('previoustrack', () => fs_navigator.open_sibling(-1));
|
||||||
navigator.mediaSession.setActionHandler('nexttrack', () => dispatch("open_sibling", 1));
|
navigator.mediaSession.setActionHandler('nexttrack', () => fs_navigator.open_sibling(1));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<div class="container">
|
|
||||||
<FileTitle title={state.base.name}/>
|
<FileTitle title={state.base.name}/>
|
||||||
<button on:click={() => dispatch("open_sibling", -1) }><i class="icon">skip_previous</i></button>
|
|
||||||
<button on:click={() => player.currentTime -= 10 }><i class="icon">replay_10</i></button>
|
<TextBlock>
|
||||||
<button on:click={toggle_play}>
|
|
||||||
{#if playing}
|
|
||||||
<i class="icon">pause</i>
|
|
||||||
{:else}
|
|
||||||
<i class="icon">play_arrow</i>
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
<button on:click={() => player.currentTime += 10 }><i class="icon">forward_10</i></button>
|
|
||||||
<button on:click={() => dispatch("open_sibling", 1) }><i class="icon">skip_next</i></button>
|
|
||||||
<br/><br/>
|
|
||||||
<audio
|
<audio
|
||||||
bind:this={player}
|
bind:this={player}
|
||||||
class="player"
|
class="player"
|
||||||
@@ -61,18 +50,59 @@ onMount(() => {
|
|||||||
controls="controls"
|
controls="controls"
|
||||||
on:pause={() => playing = false }
|
on:pause={() => playing = false }
|
||||||
on:play={() => playing = true }
|
on:play={() => playing = true }
|
||||||
on:ended={() => dispatch("open_sibling", 1) }>
|
on:ended={() => fs_navigator.open_sibling(1) }>
|
||||||
<track kind="captions"/>
|
<track kind="captions"/>
|
||||||
</audio>
|
</audio>
|
||||||
</div>
|
<div style="text-align: center;">
|
||||||
|
<button on:click={() => fs_navigator.open_sibling(-1) }><i class="icon">skip_previous</i></button>
|
||||||
|
<button on:click={() => player.currentTime -= 10 }><i class="icon">replay_10</i></button>
|
||||||
|
<button on:click={toggle_play}>
|
||||||
|
{#if playing}
|
||||||
|
<i class="icon">pause</i>
|
||||||
|
{:else}
|
||||||
|
<i class="icon">play_arrow</i>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
<button on:click={() => player.currentTime += 10 }><i class="icon">forward_10</i></button>
|
||||||
|
<button on:click={() => fs_navigator.open_sibling(1) }><i class="icon">skip_next</i></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Tracklist</h2>
|
||||||
|
{#each siblings as sibling (sibling.path)}
|
||||||
|
<a
|
||||||
|
href={"/d"+fs_encode_path(sibling.path)}
|
||||||
|
on:click|preventDefault={() => fs_navigator.navigate(sibling.path, true)}
|
||||||
|
class="node"
|
||||||
|
>
|
||||||
|
{#if sibling.path === state.base.path}
|
||||||
|
<i class="play_arrow icon">play_arrow</i>
|
||||||
|
{:else}
|
||||||
|
<img src={fs_node_icon(sibling, 64, 64)} class="node_icon" alt="icon"/>
|
||||||
|
{/if}
|
||||||
|
<span>{sibling.name}</span>
|
||||||
|
<br/>
|
||||||
|
</a>
|
||||||
|
{/each}
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.container {
|
|
||||||
padding: 0;
|
|
||||||
overflow-y: auto;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.player {
|
.player {
|
||||||
width: 90%;
|
width: 100%;
|
||||||
|
}
|
||||||
|
.node {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
color: var(--body_text_color);
|
||||||
|
text-decoration: none;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid var(--separator);
|
||||||
|
}
|
||||||
|
.node_icon {
|
||||||
|
margin: 4px;
|
||||||
|
width: 1.5em;
|
||||||
|
height: 1.5em;
|
||||||
|
}
|
||||||
|
.play_arrow {
|
||||||
|
margin: 4px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@@ -56,7 +56,7 @@ const state_update = async (base) => {
|
|||||||
<CustomBanner path={state.path}/>
|
<CustomBanner path={state.path}/>
|
||||||
</FileManager>
|
</FileManager>
|
||||||
{:else if viewer_type === "audio"}
|
{:else if viewer_type === "audio"}
|
||||||
<Audio state={state} on:open_sibling>
|
<Audio bind:this={viewer} fs_navigator={fs_navigator} state={state}>
|
||||||
<CustomBanner path={state.path}/>
|
<CustomBanner path={state.path}/>
|
||||||
</Audio>
|
</Audio>
|
||||||
{:else if viewer_type === "image"}
|
{:else if viewer_type === "image"}
|
||||||
|
Reference in New Issue
Block a user