support media player api
This commit is contained in:
@@ -35,24 +35,18 @@ let state = {
|
|||||||
parents: initialNode.parents,
|
parents: initialNode.parents,
|
||||||
base: initialNode.base,
|
base: initialNode.base,
|
||||||
|
|
||||||
|
// When navigating into a file or directory the siblings array will be
|
||||||
|
// populated with the previous base's children
|
||||||
|
siblings: [],
|
||||||
|
|
||||||
path_root: "/d/"+initialNode.bucket.id,
|
path_root: "/d/"+initialNode.bucket.id,
|
||||||
loading: true,
|
loading: true,
|
||||||
viewer_type: ""
|
viewer_type: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tallys
|
// Tallys
|
||||||
$: total_directories = state.base.children.reduce((acc, cur) => {
|
$: total_directories = state.base.children.reduce((acc, cur) => cur.type === "dir" ? acc + 1 : acc, 0)
|
||||||
if (cur.type === "dir") {
|
$: total_files = state.base.children.reduce((acc, cur) => cur.type === "file" ? acc + 1 : acc, 0)
|
||||||
acc++
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
}, 0)
|
|
||||||
$: total_files = state.base.children.reduce((acc, cur) => {
|
|
||||||
if (cur.type === "file") {
|
|
||||||
acc++
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
}, 0)
|
|
||||||
$: total_file_size = state.base.children.reduce((acc, cur) => acc + cur.file_size, 0)
|
$: total_file_size = state.base.children.reduce((acc, cur) => acc + cur.file_size, 0)
|
||||||
|
|
||||||
const navigate = (path, pushHist) => {
|
const navigate = (path, pushHist) => {
|
||||||
@@ -77,16 +71,19 @@ const navigate = (path, pushHist) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const openNode = (node) => {
|
const sort_children = children => {
|
||||||
// Sort directory children
|
children.sort((a, b) => {
|
||||||
node.base.children.sort((a, b) => {
|
|
||||||
// Sort directories before files
|
// Sort directories before files
|
||||||
console.log(a)
|
|
||||||
if (a.type !== b.type) {
|
if (a.type !== b.type) {
|
||||||
return a.type === "file" ? 1 : -1
|
return a.type === "dir" ? -1 : 1
|
||||||
}
|
}
|
||||||
return a.name.localeCompare(b.name)
|
return a.name.localeCompare(b.name)
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const openNode = (node) => {
|
||||||
|
// Sort directory children
|
||||||
|
sort_children(node.base.children)
|
||||||
|
|
||||||
// Update shared state
|
// Update shared state
|
||||||
state.bucket = node.bucket
|
state.bucket = node.bucket
|
||||||
@@ -119,8 +116,50 @@ const openNode = (node) => {
|
|||||||
}
|
}
|
||||||
onMount(() => openNode(initialNode))
|
onMount(() => openNode(initialNode))
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// negative number to move backward
|
||||||
|
const open_sibling = offset => {
|
||||||
|
state.loading = true
|
||||||
|
|
||||||
|
// Get the parent directory
|
||||||
|
fs_get_node(
|
||||||
|
state.bucket.id, state.parents[state.parents.length - 1].path,
|
||||||
|
).then(resp => {
|
||||||
|
// Sort directory children
|
||||||
|
sort_children(resp.base.children)
|
||||||
|
|
||||||
|
// Loop over the parent node's children to find the one which is
|
||||||
|
// currently open. Then, if possible, we save the one which comes before
|
||||||
|
// or after it
|
||||||
|
let next_sibling = null
|
||||||
|
for (let i = 0; i < resp.base.children.length; i++) {
|
||||||
|
if (
|
||||||
|
resp.base.children[i].name === state.base.name &&
|
||||||
|
i+offset >= 0 && // Prevent underflow
|
||||||
|
i+offset < resp.base.children.length // Prevent overflow
|
||||||
|
) {
|
||||||
|
next_sibling = resp.base.children[i+offset]
|
||||||
|
console.debug("Next sibling is", next_sibling)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we found a sibling we open it
|
||||||
|
if (next_sibling !== null) {
|
||||||
|
navigate(next_sibling.path, true)
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.error(err)
|
||||||
|
alert(err)
|
||||||
|
}).finally(() => {
|
||||||
|
state.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture browser back and forward navigation buttons
|
||||||
window.onpopstate = (e) => {
|
window.onpopstate = (e) => {
|
||||||
if(e.state){
|
if(e.state){
|
||||||
|
// Get the part of the URL after the bucket ID and navigate to it
|
||||||
let locsplit = document.location.pathname.split(state.bucket.id+"/", 2)
|
let locsplit = document.location.pathname.split(state.bucket.id+"/", 2)
|
||||||
navigate(decodeURIComponent(locsplit[1]))
|
navigate(decodeURIComponent(locsplit[1]))
|
||||||
}
|
}
|
||||||
@@ -142,7 +181,7 @@ const download = () => {
|
|||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window on:keydown={keydown}/>
|
<svelte:window on:keydown={keydown} on:/>
|
||||||
|
|
||||||
<div bind:this={file_viewer} class="file_viewer">
|
<div bind:this={file_viewer} class="file_viewer">
|
||||||
{#if state.loading}
|
{#if state.loading}
|
||||||
@@ -205,11 +244,11 @@ const download = () => {
|
|||||||
{#if state.viewer_type === "dir"}
|
{#if state.viewer_type === "dir"}
|
||||||
<FileManager state={state} on:navigate={e => {navigate(e.detail, true)}} on:loading={e => {state.loading = e.detail}}></FileManager>
|
<FileManager state={state} on:navigate={e => {navigate(e.detail, true)}} on:loading={e => {state.loading = e.detail}}></FileManager>
|
||||||
{:else if state.viewer_type === "audio"}
|
{:else if state.viewer_type === "audio"}
|
||||||
<Audio state={state}></Audio>
|
<Audio state={state} on:open_sibling={e => {open_sibling(e.detail)}}></Audio>
|
||||||
{:else if state.viewer_type === "image"}
|
{:else if state.viewer_type === "image"}
|
||||||
<Image state={state}></Image>
|
<Image state={state} on:open_sibling={e => {open_sibling(e.detail)}}></Image>
|
||||||
{:else if state.viewer_type === "video"}
|
{:else if state.viewer_type === "video"}
|
||||||
<Video state={state}></Video>
|
<Video state={state} on:open_sibling={e => {open_sibling(e.detail)}}></Video>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,20 +1,64 @@
|
|||||||
<script>
|
<script>
|
||||||
import { fs_get_file_url } from "../FilesystemAPI.svelte";
|
import { fs_get_file_url } from "../FilesystemAPI.svelte";
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher, onMount } from 'svelte'
|
||||||
let dispatch = createEventDispatcher()
|
let dispatch = createEventDispatcher()
|
||||||
|
|
||||||
export let state;
|
export let state
|
||||||
|
let player
|
||||||
|
let playing = false
|
||||||
|
let media_session = false
|
||||||
|
|
||||||
|
const toggle_play = () => playing ? player.pause() : player.play()
|
||||||
|
|
||||||
|
// Detect when the song changes
|
||||||
|
$: update_session_meta(state.base.name)
|
||||||
|
|
||||||
|
const update_session_meta = name => {
|
||||||
|
if (media_session) {
|
||||||
|
navigator.mediaSession.metadata = new MediaMetadata({
|
||||||
|
title: name,
|
||||||
|
artist: "unknown",
|
||||||
|
album: "unknown",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if ('mediaSession' in navigator) {
|
||||||
|
media_session = true
|
||||||
|
update_session_meta(state.base.name)
|
||||||
|
|
||||||
|
navigator.mediaSession.setActionHandler('play', () => player.play());
|
||||||
|
navigator.mediaSession.setActionHandler('pause', () => player.pause());
|
||||||
|
navigator.mediaSession.setActionHandler('stop', () => player.stop());
|
||||||
|
navigator.mediaSession.setActionHandler('previoustrack', () => dispatch("open_sibling", -1));
|
||||||
|
navigator.mediaSession.setActionHandler('nexttrack', () => dispatch("open_sibling", 1));
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{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>
|
||||||
|
<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/>
|
<br/><br/>
|
||||||
<audio
|
<audio
|
||||||
|
bind:this={player}
|
||||||
class="player"
|
class="player"
|
||||||
src={fs_get_file_url(state.bucket.id, state.base.path)}
|
src={fs_get_file_url(state.bucket.id, state.base.path)}
|
||||||
autoplay="autoplay"
|
autoplay="autoplay"
|
||||||
controls="controls"
|
controls="controls"
|
||||||
on:ended={() => { dispatch("next") }}>
|
on:pause={() => playing = false }
|
||||||
|
on:play={() => playing = true }
|
||||||
|
on:ended={() => dispatch("open_sibling", 1) }>
|
||||||
<track kind="captions"/>
|
<track kind="captions"/>
|
||||||
</audio>
|
</audio>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -12,7 +12,7 @@ export let state;
|
|||||||
src={fs_get_file_url(state.bucket.id, state.base.path)}
|
src={fs_get_file_url(state.bucket.id, state.base.path)}
|
||||||
autoplay="autoplay"
|
autoplay="autoplay"
|
||||||
controls="controls"
|
controls="controls"
|
||||||
on:ended={() => { dispatch("next") }}>
|
on:ended={() => { dispatch("open_sibling", 1) }}>
|
||||||
<track kind="captions"/>
|
<track kind="captions"/>
|
||||||
</video>
|
</video>
|
||||||
</div>
|
</div>
|
||||||
|
Reference in New Issue
Block a user