diff --git a/Taskfile.yml b/Taskfile.yml index d3ceddb7..22403187 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -159,6 +159,67 @@ tasks: cmds: - go run ./internal/tools/i18n-validate ./... + # --- Core IDE (Wails v3) --- + ide:dev: + desc: "Run Core IDE in Wails dev mode" + dir: cmd/core-ide + cmds: + - cd frontend && npm install && npm run build + - wails3 dev + + ide:build: + desc: "Build Core IDE production binary" + dir: cmd/core-ide + cmds: + - cd frontend && npm install && npm run build + - wails3 build + + ide:frontend: + desc: "Build Core IDE frontend only" + dir: cmd/core-ide/frontend + cmds: + - npm install + - npm run build + + # --- Core App (FrankenPHP + Wails v3) --- + app:setup: + desc: "Install PHP-ZTS build dependency for Core App" + cmds: + - brew tap shivammathur/php 2>/dev/null || true + - brew install shivammathur/php/php@8.4-zts + + app:composer: + desc: "Install Laravel dependencies for Core App" + dir: cmd/core-app/laravel + cmds: + - composer install --no-dev --optimize-autoloader --no-interaction + + app:build: + desc: "Build Core App (FrankenPHP + Laravel desktop binary)" + dir: cmd/core-app + env: + CGO_ENABLED: "1" + CGO_CFLAGS: + sh: /opt/homebrew/opt/php@8.4-zts/bin/php-config --includes + CGO_LDFLAGS: + sh: "echo -L/opt/homebrew/opt/php@8.4-zts/lib $(/opt/homebrew/opt/php@8.4-zts/bin/php-config --ldflags) $(/opt/homebrew/opt/php@8.4-zts/bin/php-config --libs)" + cmds: + - go build -tags nowatcher -o ../../bin/core-app . + + app:dev: + desc: "Build and run Core App" + dir: cmd/core-app + env: + CGO_ENABLED: "1" + CGO_CFLAGS: + sh: /opt/homebrew/opt/php@8.4-zts/bin/php-config --includes + CGO_LDFLAGS: + sh: "echo -L/opt/homebrew/opt/php@8.4-zts/lib $(/opt/homebrew/opt/php@8.4-zts/bin/php-config --ldflags) $(/opt/homebrew/opt/php@8.4-zts/bin/php-config --libs)" + DYLD_LIBRARY_PATH: "/opt/homebrew/opt/php@8.4-zts/lib" + cmds: + - go build -tags nowatcher -o ../../bin/core-app . + - ../../bin/core-app + # --- Multi-repo (when in workspace) --- dev:health: desc: "Check health of all repos" diff --git a/cmd/bugseti/frontend/src/app/app.routes.ts b/cmd/bugseti/frontend/src/app/app.routes.ts index 8367d07a..76725edb 100644 --- a/cmd/bugseti/frontend/src/app/app.routes.ts +++ b/cmd/bugseti/frontend/src/app/app.routes.ts @@ -21,5 +21,9 @@ export const routes: Routes = [ { path: 'onboarding', loadComponent: () => import('./onboarding/onboarding.component').then(m => m.OnboardingComponent) + }, + { + path: 'jellyfin', + loadComponent: () => import('./jellyfin/jellyfin.component').then(m => m.JellyfinComponent) } ]; diff --git a/cmd/bugseti/frontend/src/app/jellyfin/jellyfin.component.ts b/cmd/bugseti/frontend/src/app/jellyfin/jellyfin.component.ts new file mode 100644 index 00000000..0f7c8382 --- /dev/null +++ b/cmd/bugseti/frontend/src/app/jellyfin/jellyfin.component.ts @@ -0,0 +1,187 @@ +import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; + +type Mode = 'web' | 'stream'; + +@Component({ + selector: 'app-jellyfin', + standalone: true, + imports: [CommonModule, FormsModule], + template: ` +
+
+
+

Jellyfin Player

+

Quick embed for media.lthn.ai or any Jellyfin host.

+
+
+ + +
+
+ +
+
+ + +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+ +
+ +
+ +
+ +

Set Item ID and API key to build stream URL.

+
+
+ `, + styles: [` + .jellyfin { + display: flex; + flex-direction: column; + gap: var(--spacing-md); + padding: var(--spacing-md); + height: 100%; + overflow: auto; + background: var(--bg-secondary); + } + + .jellyfin__header { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--spacing-md); + } + + .jellyfin__header h1 { + margin-bottom: var(--spacing-xs); + } + + .mode-switch { + display: flex; + gap: var(--spacing-xs); + } + + .mode-switch .btn.is-active { + border-color: var(--accent-primary); + color: var(--accent-primary); + } + + .jellyfin__config { + display: flex; + flex-direction: column; + gap: var(--spacing-sm); + } + + .stream-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: var(--spacing-sm); + } + + .actions { + display: flex; + gap: var(--spacing-sm); + } + + .jellyfin__viewer { + flex: 1; + min-height: 420px; + padding: 0; + overflow: hidden; + } + + .jellyfin-frame, + .jellyfin-video { + border: 0; + width: 100%; + height: 100%; + min-height: 420px; + background: #000; + } + + .stream-hint { + padding: var(--spacing-md); + margin: 0; + } + `] +}) +export class JellyfinComponent { + mode: Mode = 'web'; + loaded = false; + + serverUrl = 'https://media.lthn.ai'; + itemId = ''; + apiKey = ''; + mediaSourceId = ''; + + safeWebUrl: SafeResourceUrl = this.sanitizer.bypassSecurityTrustResourceUrl('https://media.lthn.ai/web/index.html'); + streamUrl = ''; + + constructor(private sanitizer: DomSanitizer) {} + + load(): void { + const base = this.normalizeBase(this.serverUrl); + this.safeWebUrl = this.sanitizer.bypassSecurityTrustResourceUrl(`${base}/web/index.html`); + this.streamUrl = this.buildStreamUrl(base); + this.loaded = true; + } + + reset(): void { + this.loaded = false; + this.itemId = ''; + this.apiKey = ''; + this.mediaSourceId = ''; + this.streamUrl = ''; + } + + private normalizeBase(value: string): string { + const raw = value.trim() || 'https://media.lthn.ai'; + const withProtocol = raw.startsWith('http://') || raw.startsWith('https://') ? raw : `https://${raw}`; + return withProtocol.replace(/\/+$/, ''); + } + + private buildStreamUrl(base: string): string { + if (!this.itemId.trim() || !this.apiKey.trim()) { + return ''; + } + + const url = new URL(`${base}/Videos/${encodeURIComponent(this.itemId.trim())}/stream`); + url.searchParams.set('api_key', this.apiKey.trim()); + url.searchParams.set('static', 'true'); + if (this.mediaSourceId.trim()) { + url.searchParams.set('MediaSourceId', this.mediaSourceId.trim()); + } + return url.toString(); + } +} diff --git a/cmd/bugseti/frontend/src/app/tray/tray.component.ts b/cmd/bugseti/frontend/src/app/tray/tray.component.ts index 4a7ebec8..f6232e90 100644 --- a/cmd/bugseti/frontend/src/app/tray/tray.component.ts +++ b/cmd/bugseti/frontend/src/app/tray/tray.component.ts @@ -66,6 +66,9 @@ interface TrayStatus {