Skip to content

Commit 75aadd8

Browse files
committed
feat: updated ytdl trpc, plain scrollarea option, ytdlp & app updater, layout update, file sheet updated
1 parent 6a8f3a9 commit 75aadd8

19 files changed

+389
-124
lines changed

dev-app-update.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
provider: generic
2-
url: https://example.com/auto-updates
3-
updaterCacheDirName: biyori-ani-updater
1+
provider: github
2+
url: https://github.com/Venipa/ytdlp-deskop
3+
updaterCacheDirName: ytdlpd-updater

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"main": "./out/main/index.js",
66
"author": "Venipa <[email protected]>",
77
"homepage": "https://venipa.net",
8+
"repository": "https://github.com/Venipa/ytdlp-desktop",
89
"scripts": {
910
"format": "prettier --write .",
1011
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
@@ -14,6 +15,7 @@
1415
"start": "electron-vite preview",
1516
"dev": "electron-vite dev --watch",
1617
"release": "npm run git:info && cross-env NODE_ENV=production npm run build && electron-builder",
18+
"dev-release": "npm run git:info && cross-env NODE_ENV=development npm run build && electron-builder",
1719
"git:info": "node git-config.cjs",
1820
"build": "npm run typecheck && electron-vite build",
1921
"postinstall": "npm run git:info && electron-builder install-app-deps",

src/main/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ import { clamp } from 'lodash'
2626
import builderConfig from '../../electron-builder.yml'
2727
import { executableIsAvailable } from './lib/bin.utils'
2828
import { ClipboardMonitor } from './lib/clipboardMonitor'
29-
import { attachAutoUpdaterIPC } from './license'
3029
import { appStore } from './stores/app.store'
3130
import { runMigrate } from './stores/queue-database'
3231
import { trpcIpcHandler } from './trpc'
3332
import { createUrlOfPage, loadUrlOfWindow } from './trpc/dialog.utils'
3433
import { pushWindowState } from './trpc/window.api'
3534
import { checkBrokenLinks, ytdl } from './trpc/ytdlp.core'
3635
import { ytdlpEvents } from './trpc/ytdlp.ee'
36+
import { attachAutoUpdaterIPC } from './updater'
3737
const log = new Logger('App')
3838
const trayIcon = !platform.isWindows ? (platform.isMacOS ? trayIconAsset : iconWin) : icon
3939

src/main/trpc/internal.api.ts

+22-3
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,29 @@ export const internalRouter = router({
4545
)
4646
.mutation(async ({ input: { path: filePath, openParent } }) => {
4747
if (openParent) shell.openPath(dirname(filePath))
48-
else shell.showItemInFolder(filePath)
48+
else shell.showItemInFolder(filePath)
4949
}),
50-
checkUpdate: publicProcedure.mutation(() => {
51-
return autoUpdater.checkForUpdatesAndNotify()
50+
openFile: publicProcedure
51+
.input(
52+
z.object({
53+
path: z.string()
54+
})
55+
)
56+
.mutation(async ({ input: { path: filePath } }) => {
57+
await shell.openPath(filePath)
58+
}),
59+
checkUpdate: publicProcedure
60+
.input(z.boolean().default(false))
61+
.mutation(async ({ input: notifyIfUpdateAvailable }) => {
62+
if (notifyIfUpdateAvailable) return await autoUpdater.checkForUpdatesAndNotify()
63+
return await autoUpdater.checkForUpdates()
64+
}),
65+
downloadUpdate: publicProcedure.mutation(() => {
66+
try {
67+
return autoUpdater.downloadUpdate()
68+
} catch (ex: any) {
69+
throw new TRPCError({ message: ex.message, code: 'INTERNAL_SERVER_ERROR' })
70+
}
5271
}),
5372
quitAndInstallUpdate: publicProcedure.mutation(() => {
5473
try {

src/main/trpc/trpc.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@ export const publicProcedure = t.procedure.use(async ({ ctx, path, type, next })
1212
const log = new Logger(path).child(type)
1313
ctx.log = log
1414
ctx.path = path
15-
return next({ ctx }).finally(() => {
16-
log.debug("executed")
17-
})
15+
return next({ ctx })
1816
})
1917

2018
/**

src/main/trpc/ytdlp.utils.ts

+17-3
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,22 @@ export class YTDLP {
5555
}))) ||
5656
null
5757
log.debug('ytdlp version compare...', {
58-
latest: latestRelease?.version ?? '-',
58+
latest: latestRelease?.version,
5959
current: currentYtdlp.version,
60-
config: currentYtdlp
60+
config: currentYtdlp,
61+
platform: YTDLP_PLATFORM
6162
})
63+
let updated = false
64+
let previousVersion = currentYtdlp?.version ?? '-'
6265
if (
6366
latestRelease &&
6467
(forceLatestUpdate ||
6568
!currentYtdlp?.version ||
66-
!calvNewerThan(currentYtdlp.version, latestRelease.version))
69+
(currentYtdlp.version !== latestRelease.version &&
70+
!calvNewerThan(
71+
currentYtdlp.version.replace(/\./g, '-'),
72+
latestRelease.version.replace(/\./g, '-')
73+
)))
6774
) {
6875
log.debug('found new version of ytdlp, trying to download...', {
6976
tag_name: latestRelease.tag_name,
@@ -72,13 +79,20 @@ export class YTDLP {
7279
await this.downloadUpdate(latestRelease)
7380
.then(({ path, version }) => {
7481
appStore.set('ytdlp', { path, version, checkForUpdate: true })
82+
log.debug('downloaded new ytdlp executable:', path, version)
83+
updated = true
7584
})
7685
.catch((err) => {
7786
log.error('failed to download update...\n', err)
7887
})
7988
}
8089
if (appStore.store.ytdlp?.path) this.ytdlp.setBinaryPath(appStore.store.ytdlp.path)
8190
this._state = YTDLP_STATE.READY
91+
return {
92+
updated,
93+
currentVersion: appStore.store.ytdlp.version,
94+
previousVersion
95+
}
8296
}
8397
private async downloadUpdate(
8498
release: GithubRelease & { version: string }

src/main/license/index.ts src/main/updater/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export function attachAutoUpdaterIPC(win: BrowserWindow) {
3333
})
3434
.then(({ response }) => {
3535
if (response === 0) autoUpdater.quitAndInstall()
36-
else if (response === 1) autoUpdater.autoInstallOnAppQuit = true;
36+
else if (response === 1) autoUpdater.autoInstallOnAppQuit = true
3737
})
3838
})
3939
}

src/renderer/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
66
<meta
77
http-equiv="Content-Security-Policy"
8-
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
8+
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-src 'self' youtube.com www.youtube.com;"
99
/>
1010
</head>
1111

src/renderer/src/assets/main.scss

+3
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@
114114
.mask-to-t {
115115
mask-image: linear-gradient(to top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 90%)
116116
}
117+
.mask-to-b {
118+
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 90%)
119+
}
117120
.mask-gradient {
118121
mask-image: linear-gradient(to top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 64px)
119122
}

src/renderer/src/components/ui/ButtonLoading.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export default forwardRef(function ButtonLoading(
1111
{
1212
children,
1313
loading: refLoading,
14-
size,
1514
onClickWithLoading,
1615
fixWidth,
1716
...props

src/renderer/src/components/ui/app-navbar.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ export default function AppNavBar({
3535
<TooltipProvider delayDuration={100}>
3636
<WindowControlBar
3737
title={configState?.title || <Spinner />}
38-
state={{...windowState, title: ""} as any}
38+
state={{ ...windowState, title: '' } as any}
3939
className={cn('h-10 px-1.5', className)}
4040
{...{ onMinimize, onMaximize, onClose: isQuitRoute ? close : onClose }}
4141
{...props}
4242
>
4343
{windowState && (
4444
<>
4545
<div className="flex items-center space-x-2 mr-2">
46-
<QTooltip content="Theme">
46+
<QTooltip content="Theme" align="center">
4747
<ThemeToggle size="sm" />
4848
</QTooltip>
4949
{(props.variant !== 'transparent' && (

src/renderer/src/components/ui/scroll-area.tsx

+25-10
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,52 @@ import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'
44
import * as React from 'react'
55

66
import { cn } from '@renderer/lib/utils'
7+
import { logger } from '@shared/logger'
78
import { motion, useScroll, useTransform } from 'motion/react'
89

910
const ScrollArea = React.forwardRef<
1011
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
11-
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
12-
>(({ className, children, ...props }, ref) => {
13-
const scrollRef = React.useRef()
12+
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & { plain?: boolean }
13+
>(({ className, children, plain, ...props }, ref) => {
14+
const scrollRef = React.useRef<HTMLDivElement>()
1415
const { scrollY } = useScroll({
1516
container: scrollRef as any,
1617
layoutEffect: true,
1718
axis: 'y'
1819
})
19-
const showScrollBlur = useTransform(scrollY, [0, 50], [0, 1])
20+
const scrollHeight = React.useMemo(() => scrollRef.current?.scrollHeight ?? 50, [scrollRef])
21+
const showStartScrollBlur = useTransform(scrollY, [0, 50], [0, 1])
22+
const showEndScrollBlur = useTransform(scrollY, [0, scrollHeight - 50, scrollHeight], [1, 1, 0])
23+
logger.debug('scroller', { showEndScrollBlur: showEndScrollBlur.get(), scrollHeight })
2024
return (
2125
<ScrollAreaPrimitive.Root
2226
ref={ref}
2327
className={cn('relative overflow-hidden', className)}
2428
{...props}
2529
>
26-
<motion.div
27-
className={cn(
28-
'absolute top-0 inset-x-0 backdrop-blur-sm h-12 pointer-events-auto mask-to-t z-10'
29-
)}
30-
style={{ opacity: showScrollBlur }}
31-
></motion.div>
30+
{!plain && (
31+
<motion.div
32+
className={cn(
33+
'absolute top-0 inset-x-0 backdrop-blur-sm h-12 pointer-events-auto mask-to-t z-10'
34+
)}
35+
style={{ opacity: showStartScrollBlur }}
36+
></motion.div>
37+
)}
3238
<ScrollAreaPrimitive.Viewport
3339
className="h-full w-full rounded-[inherit] pb-6"
3440
ref={scrollRef as any}
3541
>
3642
{children}
3743
</ScrollAreaPrimitive.Viewport>
44+
45+
{!plain && (
46+
<motion.div
47+
className={cn(
48+
'absolute bottom-0 inset-x-0 backdrop-blur-sm h-8 pointer-events-auto mask-to-b z-10'
49+
)}
50+
style={{ opacity: showEndScrollBlur }}
51+
></motion.div>
52+
)}
3853
<ScrollBar />
3954
<ScrollAreaPrimitive.Corner />
4055
</ScrollAreaPrimitive.Root>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import type { SelectDownload } from '@main/stores/queue-database.helpers'
2+
import { Button } from '@renderer/components/ui/button'
3+
import { ScrollArea } from '@renderer/components/ui/scroll-area'
4+
import { Sheet, SheetContent, SheetTitle, SheetTrigger } from '@renderer/components/ui/sheet'
5+
import { trpc } from '@renderer/lib/trpc-link'
6+
import { formatDistanceToNow } from 'date-fns'
7+
import prettyBytes from 'pretty-bytes'
8+
import { PropsWithChildren, useMemo } from 'react'
9+
const isYTDomain = /^((www|music)\.)?youtube.com/
10+
export default function FileSheet({
11+
item: { title, source, url, metaId, filepath, filesize, meta, type, created },
12+
children
13+
}: PropsWithChildren<{ item: SelectDownload }>) {
14+
const { isYoutube, frameSource } = useMemo(() => {
15+
const isYoutube = isYTDomain.test(source)
16+
const frameSource =
17+
(isYoutube &&
18+
metaId &&
19+
`https://www.youtube.com/embed/${metaId}?rel=0&showinfo=0&controls=1&iv_load_policy=3&playsinline=1&fs=0`) ||
20+
null
21+
return { isYoutube, frameSource }
22+
}, [source])
23+
const { mutateAsync: openFile } = trpc.internals.openFile.useMutation()
24+
const createdAt = useMemo(
25+
() => (created && formatDistanceToNow(new Date(created), { includeSeconds: false })) || null,
26+
[created]
27+
)
28+
return (
29+
<>
30+
<Sheet modal>
31+
<SheetTrigger asChild>{children}</SheetTrigger>
32+
<SheetContent className="flex flex-col gap-6 sm:max-w-2xl h-full">
33+
<SheetTitle className="text-lg font-semibold tracking-wider whitespace-pre-wrap mr-8 break-words line-clamp-2 flex-shrink-0">
34+
{title}
35+
</SheetTitle>
36+
{isYoutube && frameSource && (
37+
<div className="-mx-6 flex-shrink-0">
38+
<iframe
39+
className="w-full aspect-video"
40+
src={frameSource}
41+
title="YouTube video player"
42+
allow="encrypted-media;"
43+
referrerPolicy="strict-origin-when-cross-origin"
44+
></iframe>
45+
</div>
46+
)}
47+
<ScrollArea className="flex-auto flex flex-col -m-6 h-full" plain>
48+
<div className="file-info-row">
49+
<span>Filename</span>
50+
<span>{meta?.filename || '-'}</span>
51+
</div>
52+
<div className="file-info-row">
53+
<span>Filesize</span>
54+
<span>{filesize && prettyBytes(filesize) || '0B'}</span>
55+
</div>
56+
<div className="file-info-row">
57+
<span>Type</span>
58+
<span>{type || '-'}</span>
59+
</div>
60+
<div className="file-info-row">
61+
<span>Source</span>
62+
<span>{source || '-'}</span>
63+
</div>
64+
<div className="file-info-row">
65+
<span>Uploader</span>
66+
<div className="flex items-center gap-2">
67+
{meta?.uploader && meta?.uploader !== meta?.channel && (
68+
<>
69+
<span>{meta?.uploader || '-'}</span>
70+
<span className='bg-muted-foreground size-1 rounded-full'></span>
71+
</>
72+
)}
73+
<span>{meta?.channel || '-'}</span>
74+
</div>
75+
</div>
76+
<div className="file-info-row">
77+
<span></span>
78+
<span>downloaded {createdAt || '?'} ago</span>
79+
</div>
80+
</ScrollArea>
81+
<div className="flex items-center gap-3 -mx-6 px-6 pt-6 border-t border-t-muted justify-end flex-shrink-0">
82+
<Button asChild variant={'outline'}>
83+
<a href={url} target="_blank">
84+
Open in Browser
85+
</a>
86+
</Button>
87+
<Button variant={'outline'} onClick={() => openFile({ path: filepath })}>
88+
Open File
89+
</Button>
90+
</div>
91+
</SheetContent>
92+
</Sheet>
93+
<style jsx>
94+
{`
95+
.file-info-row {
96+
@apply h-10 grid grid-cols-[160px_1fr] gap-6 items-center text-sm flex-shrink-0;
97+
}
98+
.file-info-row:nth-child(even) {
99+
@apply bg-muted/20;
100+
}
101+
.file-info-row > span:first-of-type {
102+
@apply flex justify-end;
103+
}
104+
.file-info-row > span:nth-child(2) {
105+
@apply truncate pr-8;
106+
}
107+
108+
`}
109+
</style>
110+
</>
111+
)
112+
}

src/renderer/src/pages/components/group-section.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
import { PropsWithChildren, ReactNode } from 'react'
22
interface GroupSectionProps {
33
title: string | ReactNode
4+
titleRight?: ReactNode
45
}
5-
export default function GroupSection({ title, children }: PropsWithChildren<GroupSectionProps>) {
6+
export default function GroupSection({ title, titleRight, children }: PropsWithChildren<GroupSectionProps>) {
67
return (
78
<div className="grid grid-cols-[18px_1fr] gap-2 group/parent">
89
<div className="grid grid-rows-[18px_1fr] gap-2 justify-items-center select-none">
910
<div className="size-1.5 bg-muted-foreground rounded-full self-end transition-colors duration-200 ease-out group-hover/parent:bg-primary"></div>
1011
<div className="w-px bg-muted transition-colors ease-in-out duration-200 origin-center group-hover/parent:bg-primary mask-gradient rounded-b-full"></div>
1112
</div>
1213
<div className="grid grid-rows-[40px_1fr] pb-2">
13-
<h1 className="text-lg font-bold tracking-wider select-none">{title}</h1>
14+
<div className="flex gap-3">
15+
<h1 className="text-lg font-bold tracking-wider select-none">{title}</h1>
16+
{titleRight}
17+
</div>
1418
<div className="flex flex-col">{children}</div>
1519
</div>
1620
</div>

0 commit comments

Comments
 (0)