Initial commit: Filebot Media Browser

Vue 3 + FastAPI Webapp zur Anzeige von Filebot/n8n-Medienmetadaten aus PostgreSQL.
Enthält Frontend, API, Nginx-Config, SQL-Migrationen und Docker-Compose-Setup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Housemann
2026-07-11 05:38:37 +02:00
co-authored by Claude Sonnet 4.6
commit 3f253ffe81
45 changed files with 4791 additions and 0 deletions
+104
View File
@@ -0,0 +1,104 @@
export interface DateTimeBound {
date: string
hour: string
minute: string
}
export const emptyDateTimeBound = (): DateTimeBound => ({
date: '',
hour: '00',
minute: '00',
})
export function boundToIso(bound: DateTimeBound): string | undefined {
if (!bound.date) return undefined
const local = new Date(
`${bound.date}T${bound.hour.padStart(2, '0')}:${bound.minute.padStart(2, '0')}:00`
)
if (Number.isNaN(local.getTime())) return undefined
return local.toISOString()
}
export function formatBoundLabel(bound: DateTimeBound): string {
if (!bound.date) return 'Datum wählen'
return `${formatDateDe(bound.date)} ${bound.hour.padStart(2, '0')}:${bound.minute.padStart(2, '0')}`
}
export function formatDateDe(isoDate: string): string {
const [y, m, d] = isoDate.split('-').map(Number)
if (!y || !m || !d) return isoDate
return new Date(y, m - 1, d).toLocaleDateString('de-DE', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
})
}
export function pad2(n: number): string {
return String(n).padStart(2, '0')
}
export const HOURS = Array.from({ length: 24 }, (_, i) => pad2(i))
export const MINUTES = Array.from({ length: 60 }, (_, i) => pad2(i))
const WEEKDAYS = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So']
const MONTHS = [
'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember',
]
export function getWeekdayLabels(): string[] {
return WEEKDAYS
}
export function getMonthLabel(monthIndex: number): string {
return MONTHS[monthIndex] ?? ''
}
/** Kalendertage inkl. Auffüllung für Mo-start Grid */
export function getCalendarDays(year: number, month: number) {
const first = new Date(year, month, 1)
const last = new Date(year, month + 1, 0)
let startPad = first.getDay() - 1
if (startPad < 0) startPad = 6
const days: { date: string; day: number; currentMonth: boolean }[] = []
for (let i = startPad - 1; i >= 0; i--) {
const d = new Date(year, month, -i)
days.push({
date: toIsoDate(d),
day: d.getDate(),
currentMonth: false,
})
}
for (let d = 1; d <= last.getDate(); d++) {
days.push({
date: toIsoDate(new Date(year, month, d)),
day: d,
currentMonth: true,
})
}
const totalCells = Math.ceil((startPad + last.getDate()) / 7) * 7
let nextDay = 1
while (days.length < totalCells) {
const d = new Date(year, month + 1, nextDay++)
days.push({
date: toIsoDate(d),
day: d.getDate(),
currentMonth: false,
})
}
return days
}
export function toIsoDate(d: Date): string {
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`
}
export function isBoundActive(bound: DateTimeBound): boolean {
return Boolean(bound.date)
}