file browser overhaul
This commit is contained in:
@@ -33,6 +33,7 @@ import { BehaviorSubject, Subject, combineLatestWith, map, share } from 'rxjs'
|
|||||||
import { useObservable } from './hooks/observable'
|
import { useObservable } from './hooks/observable'
|
||||||
import { RootState } from './stores/store'
|
import { RootState } from './stores/store'
|
||||||
import { DeleteRequest, DirectoryEntry } from './types'
|
import { DeleteRequest, DirectoryEntry } from './types'
|
||||||
|
import { roundMiB } from './utils'
|
||||||
|
|
||||||
export default function Downloaded() {
|
export default function Downloaded() {
|
||||||
const settings = useSelector((state: RootState) => state.settings)
|
const settings = useSelector((state: RootState) => state.settings)
|
||||||
@@ -52,7 +53,9 @@ export default function Downloaded() {
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ subdir: '' })
|
body: JSON.stringify({
|
||||||
|
subdir: '',
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(data => files$.next(data))
|
.then(data => files$.next(data))
|
||||||
@@ -150,7 +153,7 @@ export default function Downloaded() {
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
}}>
|
}}>
|
||||||
<Typography pb={0} variant="h5" color="primary">
|
<Typography py={1} variant="h5" color="primary">
|
||||||
{'Archive'}
|
{'Archive'}
|
||||||
</Typography>
|
</Typography>
|
||||||
<List sx={{ width: '100%', bgcolor: 'background.paper' }}>
|
<List sx={{ width: '100%', bgcolor: 'background.paper' }}>
|
||||||
@@ -159,11 +162,20 @@ export default function Downloaded() {
|
|||||||
<ListItem
|
<ListItem
|
||||||
key={idx}
|
key={idx}
|
||||||
secondaryAction={
|
secondaryAction={
|
||||||
!file.isDirectory && <Checkbox
|
<div>
|
||||||
|
{!file.isDirectory && <Typography
|
||||||
|
variant="caption"
|
||||||
|
component="span"
|
||||||
|
>
|
||||||
|
{roundMiB(file.size)}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
{!file.isDirectory && <Checkbox
|
||||||
edge="end"
|
edge="end"
|
||||||
checked={file.selected}
|
checked={file.selected}
|
||||||
onChange={() => addSelected(file.name)}
|
onChange={() => addSelected(file.name)}
|
||||||
/>
|
/>}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
disablePadding
|
disablePadding
|
||||||
>
|
>
|
||||||
@@ -180,7 +192,10 @@ export default function Downloaded() {
|
|||||||
: <InsertDriveFileIcon />
|
: <InsertDriveFileIcon />
|
||||||
}
|
}
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary={file.name} />
|
<ListItemText
|
||||||
|
primary={file.name}
|
||||||
|
secondary={file.name != '..' && new Date(file.modTime).toLocaleString()}
|
||||||
|
/>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
|
|||||||
12
frontend/src/types/index.d.ts
vendored
12
frontend/src/types/index.d.ts
vendored
@@ -65,16 +65,14 @@ export type DLFormat = {
|
|||||||
export type DirectoryEntry = {
|
export type DirectoryEntry = {
|
||||||
name: string
|
name: string
|
||||||
path: string
|
path: string
|
||||||
|
size: number
|
||||||
shaSum: string
|
shaSum: string
|
||||||
isVideo: boolean,
|
modTime: string
|
||||||
|
isVideo: boolean
|
||||||
isDirectory: boolean
|
isDirectory: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DeleteRequest = Omit<
|
export type DeleteRequest = Pick<DirectoryEntry, 'path' | 'shaSum'>
|
||||||
DirectoryEntry, 'name' | 'isDirectory' | 'isVideo'
|
|
||||||
>
|
|
||||||
|
|
||||||
export type PlayRequest = Omit<
|
export type PlayRequest = Pick<DirectoryEntry, 'path'>
|
||||||
DirectoryEntry, 'shaSum' | 'name' | 'isDirectory' | 'isVideo'
|
|
||||||
>
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/marcopeocchi/yt-dlp-web-ui/server/config"
|
"github.com/marcopeocchi/yt-dlp-web-ui/server/config"
|
||||||
@@ -16,7 +18,9 @@ import (
|
|||||||
type DirectoryEntry struct {
|
type DirectoryEntry struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
SHASum string `json:"shaSum"`
|
SHASum string `json:"shaSum"`
|
||||||
|
ModTime time.Time `json:"modTime"`
|
||||||
IsVideo bool `json:"isVideo"`
|
IsVideo bool `json:"isVideo"`
|
||||||
IsDirectory bool `json:"isDirectory"`
|
IsDirectory bool `json:"isDirectory"`
|
||||||
}
|
}
|
||||||
@@ -36,12 +40,19 @@ func walkDir(root string) (*[]DirectoryEntry, error) {
|
|||||||
|
|
||||||
path := filepath.Join(root, d.Name())
|
path := filepath.Join(root, d.Name())
|
||||||
|
|
||||||
|
info, err := d.Info()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
files = append(files, DirectoryEntry{
|
files = append(files, DirectoryEntry{
|
||||||
Path: path,
|
Path: path,
|
||||||
Name: d.Name(),
|
Name: d.Name(),
|
||||||
|
Size: info.Size(),
|
||||||
SHASum: utils.ShaSumString(path),
|
SHASum: utils.ShaSumString(path),
|
||||||
IsVideo: utils.IsVideo(d),
|
IsVideo: utils.IsVideo(d),
|
||||||
IsDirectory: d.IsDir(),
|
IsDirectory: d.IsDir(),
|
||||||
|
ModTime: info.ModTime(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,6 +61,7 @@ func walkDir(root string) (*[]DirectoryEntry, error) {
|
|||||||
|
|
||||||
type ListRequest struct {
|
type ListRequest struct {
|
||||||
SubDir string `json:"subdir"`
|
SubDir string `json:"subdir"`
|
||||||
|
OrderBy string `json:"orderBy"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListDownloaded(ctx *fiber.Ctx) error {
|
func ListDownloaded(ctx *fiber.Ctx) error {
|
||||||
@@ -66,6 +78,12 @@ func ListDownloaded(ctx *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if req.OrderBy == "modtime" {
|
||||||
|
sort.SliceStable(*files, func(i, j int) bool {
|
||||||
|
return (*files)[i].ModTime.After((*files)[j].ModTime)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
ctx.Status(http.StatusOK)
|
ctx.Status(http.StatusOK)
|
||||||
return ctx.JSON(files)
|
return ctx.JSON(files)
|
||||||
}
|
}
|
||||||
@@ -105,13 +123,18 @@ func SendFile(ctx *fiber.Ctx) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
decodedStr := string(decoded)
|
||||||
|
|
||||||
root := config.Instance().GetConfig().DownloadPath
|
root := config.Instance().GetConfig().DownloadPath
|
||||||
|
|
||||||
// TODO: further path / file validations
|
// TODO: further path / file validations
|
||||||
if strings.Contains(filepath.Dir(string(decoded)), root) {
|
if strings.Contains(filepath.Dir(decodedStr), root) {
|
||||||
|
// ctx.Response().Header.Set(
|
||||||
|
// "Content-Disposition",
|
||||||
|
// "inline; filename="+filepath.Base(decodedStr),
|
||||||
|
// )
|
||||||
ctx.SendStatus(fiber.StatusOK)
|
ctx.SendStatus(fiber.StatusOK)
|
||||||
return ctx.SendFile(string(decoded))
|
return ctx.SendFile(decodedStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.SendStatus(fiber.StatusUnauthorized)
|
return ctx.SendStatus(fiber.StatusUnauthorized)
|
||||||
|
|||||||
Reference in New Issue
Block a user