Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3edebbdb6c | |||
|
|
98f0ea3bd2 | ||
| 0daf36719b | |||
| 38683bfe85 | |||
| 4066a6d5e3 | |||
| ee18929196 |
@@ -28,7 +28,7 @@ RUN CGO_ENABLED=0 GOOS=linux go build -o yt-dlp-webui
|
|||||||
FROM alpine:edge
|
FROM alpine:edge
|
||||||
|
|
||||||
RUN apk update && \
|
RUN apk update && \
|
||||||
apk add ffmpeg yt-dlp ca-certificates curl wget
|
apk add ffmpeg yt-dlp ca-certificates curl wget psmisc
|
||||||
|
|
||||||
VOLUME /downloads /config
|
VOLUME /downloads /config
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ services:
|
|||||||
- 3033:3033
|
- 3033:3033
|
||||||
volumes:
|
volumes:
|
||||||
- <your dir>:/downloads # replace <your dir> with a directory on your host system
|
- <your dir>:/downloads # replace <your dir> with a directory on your host system
|
||||||
|
- <your dir>:/config # directory where config.yml will be stored
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: curl -f http://localhost:3033 || exit 1
|
test: curl -f http://localhost:3033 || exit 1
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "yt-dlp-webui",
|
"name": "yt-dlp-webui",
|
||||||
"version": "3.0.9",
|
"version": "3.1.0",
|
||||||
"description": "Frontend compontent of yt-dlp-webui",
|
"description": "Frontend compontent of yt-dlp-webui",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useRecoilState, useRecoilValue } from 'recoil'
|
|||||||
import { loadingDownloadsState } from '../atoms/downloads'
|
import { loadingDownloadsState } from '../atoms/downloads'
|
||||||
import { listViewState } from '../atoms/settings'
|
import { listViewState } from '../atoms/settings'
|
||||||
import { loadingAtom } from '../atoms/ui'
|
import { loadingAtom } from '../atoms/ui'
|
||||||
import DownloadsCardView from './DownloadsCardView'
|
import DownloadsGridView from './DownloadsGridView'
|
||||||
import DownloadsTableView from './DownloadsTableView'
|
import DownloadsTableView from './DownloadsTableView'
|
||||||
|
|
||||||
const Downloads: React.FC = () => {
|
const Downloads: React.FC = () => {
|
||||||
@@ -21,7 +21,7 @@ const Downloads: React.FC = () => {
|
|||||||
|
|
||||||
if (tableView) return <DownloadsTableView />
|
if (tableView) return <DownloadsTableView />
|
||||||
|
|
||||||
return <DownloadsCardView />
|
return <DownloadsGridView />
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Downloads
|
export default Downloads
|
||||||
@@ -4,16 +4,19 @@ import { activeDownloadsState } from '../atoms/downloads'
|
|||||||
import { useToast } from '../hooks/toast'
|
import { useToast } from '../hooks/toast'
|
||||||
import { useI18n } from '../hooks/useI18n'
|
import { useI18n } from '../hooks/useI18n'
|
||||||
import { useRPC } from '../hooks/useRPC'
|
import { useRPC } from '../hooks/useRPC'
|
||||||
|
import { ProcessStatus, RPCResult } from '../types'
|
||||||
import DownloadCard from './DownloadCard'
|
import DownloadCard from './DownloadCard'
|
||||||
|
|
||||||
const DownloadsCardView: React.FC = () => {
|
const DownloadsGridView: React.FC = () => {
|
||||||
const downloads = useRecoilValue(activeDownloadsState)
|
const downloads = useRecoilValue(activeDownloadsState)
|
||||||
|
|
||||||
const { i18n } = useI18n()
|
const { i18n } = useI18n()
|
||||||
const { client } = useRPC()
|
const { client } = useRPC()
|
||||||
const { pushMessage } = useToast()
|
const { pushMessage } = useToast()
|
||||||
|
|
||||||
const abort = (id: string) => client.kill(id)
|
const stop = (r: RPCResult) => r.progress.process_status === ProcessStatus.Completed
|
||||||
|
? client.clear(r.id)
|
||||||
|
: client.kill(r.id)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={{ xs: 2, md: 2 }} columns={{ xs: 4, sm: 8, md: 12, xl: 12 }} pt={2}>
|
<Grid container spacing={{ xs: 2, md: 2 }} columns={{ xs: 4, sm: 8, md: 12, xl: 12 }} pt={2}>
|
||||||
@@ -22,7 +25,7 @@ const DownloadsCardView: React.FC = () => {
|
|||||||
<Grid item xs={4} sm={8} md={6} xl={4} key={download.id}>
|
<Grid item xs={4} sm={8} md={6} xl={4} key={download.id}>
|
||||||
<DownloadCard
|
<DownloadCard
|
||||||
download={download}
|
download={download}
|
||||||
onStop={() => abort(download.id)}
|
onStop={() => stop(download)}
|
||||||
onCopy={() => pushMessage(i18n.t('clipboardAction'), 'info')}
|
onCopy={() => pushMessage(i18n.t('clipboardAction'), 'info')}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -32,4 +35,4 @@ const DownloadsCardView: React.FC = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DownloadsCardView
|
export default DownloadsGridView
|
||||||
@@ -24,7 +24,7 @@ import { useRecoilValue } from 'recoil'
|
|||||||
import { activeDownloadsState } from '../atoms/downloads'
|
import { activeDownloadsState } from '../atoms/downloads'
|
||||||
import { serverURL } from '../atoms/settings'
|
import { serverURL } from '../atoms/settings'
|
||||||
import { useRPC } from '../hooks/useRPC'
|
import { useRPC } from '../hooks/useRPC'
|
||||||
import { RPCResult } from '../types'
|
import { ProcessStatus, RPCResult } from '../types'
|
||||||
import { base64URLEncode, formatSize, formatSpeedMiB } from "../utils"
|
import { base64URLEncode, formatSize, formatSpeedMiB } from "../utils"
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
@@ -123,8 +123,6 @@ const DownloadsTableView: React.FC = () => {
|
|||||||
const serverAddr = useRecoilValue(serverURL)
|
const serverAddr = useRecoilValue(serverURL)
|
||||||
const { client } = useRPC()
|
const { client } = useRPC()
|
||||||
|
|
||||||
const abort = (id: string) => client.kill(id)
|
|
||||||
|
|
||||||
const viewFile = (path: string) => {
|
const viewFile = (path: string) => {
|
||||||
const encoded = base64URLEncode(path)
|
const encoded = base64URLEncode(path)
|
||||||
window.open(`${serverAddr}/archive/v/${encoded}?token=${localStorage.getItem('token')}`)
|
window.open(`${serverAddr}/archive/v/${encoded}?token=${localStorage.getItem('token')}`)
|
||||||
@@ -135,6 +133,11 @@ const DownloadsTableView: React.FC = () => {
|
|||||||
window.open(`${serverAddr}/archive/d/${encoded}?token=${localStorage.getItem('token')}`)
|
window.open(`${serverAddr}/archive/d/${encoded}?token=${localStorage.getItem('token')}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const stop = (r: RPCResult) => r.progress.process_status === ProcessStatus.Completed
|
||||||
|
? client.clear(r.id)
|
||||||
|
: client.kill(r.id)
|
||||||
|
|
||||||
|
|
||||||
function rowContent(_index: number, download: RPCResult) {
|
function rowContent(_index: number, download: RPCResult) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -170,7 +173,7 @@ const DownloadsTableView: React.FC = () => {
|
|||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => abort(download.id)}
|
onClick={() => stop(download)}
|
||||||
>
|
>
|
||||||
{download.progress.percentage === '-1' ? <DeleteIcon /> : <StopCircleIcon />}
|
{download.progress.percentage === '-1' ? <DeleteIcon /> : <StopCircleIcon />}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const Footer: React.FC = () => {
|
|||||||
display: 'flex', gap: 1, justifyContent: 'space-between'
|
display: 'flex', gap: 1, justifyContent: 'space-between'
|
||||||
}}>
|
}}>
|
||||||
<div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
|
<div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
|
||||||
<Chip label="RPC v3.0.9" variant="outlined" size="small" />
|
<Chip label="RPC v3.1.0" variant="outlined" size="small" />
|
||||||
<VersionIndicator />
|
<VersionIndicator />
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex', gap: 4, 'alignItems': 'center' }}>
|
<div style={{ display: 'flex', gap: 4, 'alignItems': 'center' }}>
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ const HomeSpeedDial: React.FC<Props> = ({ onDownloadOpen, onEditorOpen }) => {
|
|||||||
const { i18n } = useI18n()
|
const { i18n } = useI18n()
|
||||||
const { client } = useRPC()
|
const { client } = useRPC()
|
||||||
|
|
||||||
const abort = () => client.killAll()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SpeedDial
|
<SpeedDial
|
||||||
ariaLabel="Home speed dial"
|
ariaLabel="Home speed dial"
|
||||||
@@ -47,7 +45,7 @@ const HomeSpeedDial: React.FC<Props> = ({ onDownloadOpen, onEditorOpen }) => {
|
|||||||
<SpeedDialAction
|
<SpeedDialAction
|
||||||
icon={<DeleteForeverIcon />}
|
icon={<DeleteForeverIcon />}
|
||||||
tooltipTitle={i18n.t('abortAllButton')}
|
tooltipTitle={i18n.t('abortAllButton')}
|
||||||
onClick={abort}
|
onClick={() => client.killAll()}
|
||||||
/>
|
/>
|
||||||
<SpeedDialAction
|
<SpeedDialAction
|
||||||
icon={<BuildCircleIcon />}
|
icon={<BuildCircleIcon />}
|
||||||
|
|||||||
@@ -132,6 +132,13 @@ export class RPCClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public clear(id: string) {
|
||||||
|
this.sendHTTP({
|
||||||
|
method: 'Service.Clear',
|
||||||
|
params: [id],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
public killAll() {
|
public killAll() {
|
||||||
this.sendHTTP({
|
this.sendHTTP({
|
||||||
method: 'Service.KillAll',
|
method: 'Service.KillAll',
|
||||||
|
|||||||
@@ -34,11 +34,18 @@ type DownloadInfo = {
|
|||||||
created_at: string
|
created_at: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ProcessStatus {
|
||||||
|
Pending = 0,
|
||||||
|
Downloading,
|
||||||
|
Completed,
|
||||||
|
Errored,
|
||||||
|
}
|
||||||
|
|
||||||
type DownloadProgress = {
|
type DownloadProgress = {
|
||||||
speed: number
|
speed: number
|
||||||
eta: number
|
eta: number
|
||||||
percentage: string
|
percentage: string
|
||||||
process_status: number
|
process_status: ProcessStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RPCResult = Readonly<{
|
export type RPCResult = Readonly<{
|
||||||
@@ -76,15 +83,14 @@ export type DirectoryEntry = {
|
|||||||
name: string
|
name: string
|
||||||
path: string
|
path: string
|
||||||
size: number
|
size: number
|
||||||
shaSum: string
|
|
||||||
modTime: string
|
modTime: string
|
||||||
isVideo: boolean
|
isVideo: boolean
|
||||||
isDirectory: boolean
|
isDirectory: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DeleteRequest = Pick<DirectoryEntry, 'path' | 'shaSum'>
|
export type DeleteRequest = Pick<DirectoryEntry, 'path'>
|
||||||
|
|
||||||
export type PlayRequest = Pick<DirectoryEntry, 'path'>
|
export type PlayRequest = DeleteRequest
|
||||||
|
|
||||||
export type CustomTemplate = {
|
export type CustomTemplate = {
|
||||||
id: string
|
id: string
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { pipe } from 'fp-ts/lib/function'
|
import { pipe } from 'fp-ts/lib/function'
|
||||||
import type { RPCResponse } from "./types"
|
import type { RPCResponse } from "./types"
|
||||||
|
import { ProcessStatus } from './types'
|
||||||
|
|
||||||
export function validateIP(ipAddr: string): boolean {
|
export function validateIP(ipAddr: string): boolean {
|
||||||
let ipRegex = /^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$/gm
|
let ipRegex = /^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$/gm
|
||||||
@@ -53,15 +54,15 @@ export function isRPCResponse(object: any): object is RPCResponse<any> {
|
|||||||
return 'result' in object && 'id' in object
|
return 'result' in object && 'id' in object
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mapProcessStatus(status: number) {
|
export function mapProcessStatus(status: ProcessStatus) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 0:
|
case ProcessStatus.Pending:
|
||||||
return 'Pending'
|
return 'Pending'
|
||||||
case 1:
|
case ProcessStatus.Downloading:
|
||||||
return 'Downloading'
|
return 'Downloading'
|
||||||
case 2:
|
case ProcessStatus.Completed:
|
||||||
return 'Completed'
|
return 'Completed'
|
||||||
case 3:
|
case ProcessStatus.Errored:
|
||||||
return 'Error'
|
return 'Error'
|
||||||
default:
|
default:
|
||||||
return 'Pending'
|
return 'Pending'
|
||||||
|
|||||||
@@ -113,7 +113,6 @@ export default function Downloaded() {
|
|||||||
modTime: '',
|
modTime: '',
|
||||||
name: '..',
|
name: '..',
|
||||||
path: upperLevel,
|
path: upperLevel,
|
||||||
shaSum: '',
|
|
||||||
size: 0,
|
size: 0,
|
||||||
}, ...r.filter(f => f.name !== '')]
|
}, ...r.filter(f => f.name !== '')]
|
||||||
: r.filter(f => f.name !== '')
|
: r.filter(f => f.name !== '')
|
||||||
@@ -144,7 +143,6 @@ export default function Downloaded() {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
path: entry.path,
|
path: entry.path,
|
||||||
shaSum: entry.shaSum,
|
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
matchW(
|
matchW(
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ export default function Settings() {
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Typography variant="h6" color="primary" sx={{ mt: 0.5, mb: 2 }}>
|
<Typography variant="h6" color="primary" sx={{ mt: 0.5, mb: 2 }}>
|
||||||
Appaerance
|
Appearance
|
||||||
</Typography>
|
</Typography>
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
<Grid item xs={12} md={6}>
|
<Grid item xs={12} md={6}>
|
||||||
|
|||||||
@@ -65,7 +65,9 @@ func (m *MessageQueue) downloadConsumer() {
|
|||||||
slog.String("id", p.getShortId()),
|
slog.String("id", p.getShortId()),
|
||||||
)
|
)
|
||||||
|
|
||||||
p.Start()
|
if p.Progress.Status != StatusCompleted {
|
||||||
|
p.Start()
|
||||||
|
}
|
||||||
|
|
||||||
m.logger.Info("started process",
|
m.logger.Info("started process",
|
||||||
slog.String("bus", queueName),
|
slog.String("bus", queueName),
|
||||||
@@ -92,6 +94,14 @@ func (m *MessageQueue) metadataSubscriber() {
|
|||||||
slog.String("id", p.getShortId()),
|
slog.String("id", p.getShortId()),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if p.Progress.Status == StatusCompleted {
|
||||||
|
m.logger.Warn("proccess has an illegal state",
|
||||||
|
slog.String("id", p.getShortId()),
|
||||||
|
slog.Int("status", p.Progress.Status),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := p.SetMetadata(); err != nil {
|
if err := p.SetMetadata(); err != nil {
|
||||||
m.logger.Error("failed to retrieve metadata",
|
m.logger.Error("failed to retrieve metadata",
|
||||||
slog.String("id", p.getShortId()),
|
slog.String("id", p.getShortId()),
|
||||||
|
|||||||
@@ -198,18 +198,22 @@ func (p *Process) Complete() {
|
|||||||
|
|
||||||
// Kill a process and remove it from the memory
|
// Kill a process and remove it from the memory
|
||||||
func (p *Process) Kill() error {
|
func (p *Process) Kill() error {
|
||||||
|
defer func() {
|
||||||
|
p.Progress.Status = StatusCompleted
|
||||||
|
}()
|
||||||
// yt-dlp uses multiple child process the parent process
|
// yt-dlp uses multiple child process the parent process
|
||||||
// has been spawned with setPgid = true. To properly kill
|
// has been spawned with setPgid = true. To properly kill
|
||||||
// all subprocesses a SIGTERM need to be sent to the correct
|
// all subprocesses a SIGTERM need to be sent to the correct
|
||||||
// process group
|
// process group
|
||||||
if p.proc != nil {
|
if p.proc == nil {
|
||||||
pgid, err := syscall.Getpgid(p.proc.Pid)
|
return errors.New("*os.Process not set")
|
||||||
if err != nil {
|
}
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = syscall.Kill(-pgid, syscall.SIGTERM)
|
|
||||||
|
|
||||||
p.Logger.Info("killed process", slog.String("id", p.Id))
|
pgid, err := syscall.Getpgid(p.proc.Pid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := syscall.Kill(-pgid, syscall.SIGTERM); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,10 +227,7 @@ func (p *Process) GetFormatsSync() (DownloadFormats, error) {
|
|||||||
|
|
||||||
stdout, err := cmd.Output()
|
stdout, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.Logger.Error(
|
p.Logger.Error("failed to retrieve metadata", slog.String("err", err.Error()))
|
||||||
"failed to retrieve metadata",
|
|
||||||
slog.String("err", err.Error()),
|
|
||||||
)
|
|
||||||
return DownloadFormats{}, err
|
return DownloadFormats{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package rpc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
@@ -30,8 +29,6 @@ func WebSocket(w http.ResponseWriter, r *http.Request) {
|
|||||||
for {
|
for {
|
||||||
mtype, reader, err := c.NextReader()
|
mtype, reader, err := c.NextReader()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
log.Println(err)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +37,6 @@ func WebSocket(w http.ResponseWriter, r *http.Request) {
|
|||||||
writer, err := c.NextWriter(mtype)
|
writer, err := c.NextWriter(mtype)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
log.Println(err)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
|
||||||
"github.com/marcopeocchi/yt-dlp-web-ui/server/internal"
|
"github.com/marcopeocchi/yt-dlp-web-ui/server/internal"
|
||||||
@@ -99,13 +100,19 @@ func (s *Service) Kill(args string, killed *string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if proc != nil {
|
if proc == nil {
|
||||||
err = proc.Kill()
|
return errors.New("nil process")
|
||||||
s.db.Delete(proc.Id)
|
}
|
||||||
|
|
||||||
|
if err := proc.Kill(); err != nil {
|
||||||
|
s.logger.Info("failed killing process", slog.String("id", proc.Id), slog.Any("err", err))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.db.Delete(proc.Id)
|
s.db.Delete(proc.Id)
|
||||||
return err
|
s.logger.Info("succesfully killed process", slog.String("id", proc.Id))
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// KillAll kills all process unconditionally and removes them from
|
// KillAll kills all process unconditionally and removes them from
|
||||||
@@ -114,8 +121,11 @@ func (s *Service) KillAll(args NoArgs, killed *string) error {
|
|||||||
s.logger.Info("Killing all spawned processes")
|
s.logger.Info("Killing all spawned processes")
|
||||||
|
|
||||||
var (
|
var (
|
||||||
keys = s.db.Keys()
|
keys = s.db.Keys()
|
||||||
err error
|
removeFunc = func(p *internal.Process) error {
|
||||||
|
defer s.db.Delete(p.Id)
|
||||||
|
return p.Kill()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, key := range *keys {
|
for _, key := range *keys {
|
||||||
@@ -124,13 +134,24 @@ func (s *Service) KillAll(args NoArgs, killed *string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if proc != nil {
|
if proc == nil {
|
||||||
proc.Kill()
|
s.db.Delete(key)
|
||||||
s.db.Delete(proc.Id)
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := removeFunc(proc); err != nil {
|
||||||
|
s.logger.Info(
|
||||||
|
"failed killing process",
|
||||||
|
slog.String("id", proc.Id),
|
||||||
|
slog.Any("err", err),
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.Info("succesfully killed process", slog.String("id", proc.Id))
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove a process from the db rendering it unusable if active
|
// Remove a process from the db rendering it unusable if active
|
||||||
|
|||||||
Reference in New Issue
Block a user