### Setup Web Development Environment Source: https://github.com/5rahim/seanime/blob/main/DEVELOPMENT_AND_BUILD.md Commands to install dependencies and start the frontend development server. ```bash cd seanime-web ``` ```bash npm install ``` ```bash npm run dev ``` -------------------------------- ### Run the Development Server Source: https://github.com/5rahim/seanime/blob/main/DEVELOPMENT_AND_BUILD.md Starts the Go backend with a specified data directory. ```bash go run main.go --datadir="path/to/datadir" ``` -------------------------------- ### Initialize Repository and Remote Setup Source: https://github.com/5rahim/seanime/blob/main/CONTRIBUTING.md Commands to clone your fork, navigate to the project directory, and configure the upstream remote to track the original repository. ```shell git clone https://github.com//seanime.git cd seanime git remote add upstream https://github.com/5rahim/seanime.git ``` -------------------------------- ### Install gqlgenc for GraphQL Schema Generation Source: https://github.com/5rahim/seanime/blob/main/DEVELOPMENT_AND_BUILD.md Installs gqlgenc, a tool used for generating Go code from GraphQL schemas. This is necessary when the GraphQL schema is modified. ```bash go get github.com/gqlgo/gqlgenc@v0.33.1 ``` -------------------------------- ### Initialize Test Environment with AniList Provider Source: https://github.com/5rahim/seanime/blob/main/DEVELOPMENT_AND_BUILD.md Sets up a test environment using the testutil package, specifically initializing it with AniList provider configurations. This is a common setup for tests interacting with AniList. ```go env := testutil.NewTestEnv(t, testutil.Anilist()) database := env.MustNewDatabase(util.NewLogger()) _ = database ``` -------------------------------- ### Go: Get DOM Element Children with Event Listener Source: https://github.com/5rahim/seanime/blob/main/internal/plugin/ui/DOCS.md Retrieves child elements of a DOM element by registering an event listener for client updates. It sends a request to the client to get children and waits for a response or timeout. Dependencies include goja for JavaScript runtime and time for timeouts. It returns a slice of goja.Object representing the child elements. ```go func (d *DOMManager) getElementChildren(elementID string) []*goja.Object { // Listen for changes from the client eventListener := d.ctx.RegisterEventListener(ClientDOMElementUpdatedEvent) defer d.ctx.UnregisterEventListener(eventListener.ID) payload := ClientDOMElementUpdatedEventPayload{} doneCh := make(chan []*goja.Object) go func(eventListener *EventListener) { for event := range eventListener.Channel { if event.ParsePayloadAs(ClientDOMElementUpdatedEvent, &payload) { if payload.Action == "getChildren" && payload.ElementId == elementID { if v, ok := payload.Result.([]interface{}); ok { arr := make([]*goja.Object, 0, len(v)) for _, elem := range v { if elemData, ok := elem.(map[string]interface{}); ok { arr = append(arr, d.createDOMElementObject(elemData)) } } doneCh <- arr return } } } } }(eventListener) d.ctx.SendEventToClient(ServerDOMManipulateEvent, &ServerDOMManipulateEventPayload{ ElementId: elementID, Action: "getChildren", Params: map[string]interface{}{}, }) timeout := time.After(4 * time.Second) select { case <-timeout: return []*goja.Object{} case res := <-doneCh: return res } } ``` -------------------------------- ### Tidy Go Modules Source: https://github.com/5rahim/seanime/blob/main/DEVELOPMENT_AND_BUILD.md Updates and cleans up the Go module dependencies after potential changes to the project's dependencies, such as installing new tools. ```bash go mod tidy ``` -------------------------------- ### Set up and Run Seanime Desktop Development Source: https://github.com/5rahim/seanime/blob/main/seanime-denshi/README.md Steps to set up the Node.js environment for the Seanime Electron desktop client and run it in development mode. Includes instructions on using a dummy data directory for testing. ```shell # Working dir: ./seanime-denshi npm install # Working dir: ./seanime-desktop TEST_DATADIR="/path/to/data/dir" npm run dev ``` -------------------------------- ### Build the Web Interface Source: https://github.com/5rahim/seanime/blob/main/DEVELOPMENT_AND_BUILD.md Compiles the React frontend into the required output directory. ```bash npm run build ``` -------------------------------- ### Build the Go Server Source: https://github.com/5rahim/seanime/blob/main/DEVELOPMENT_AND_BUILD.md Compiles the Go backend for various platforms and configurations. ```bash set CGO_ENABLED=1 go build -o seanime.exe -trimpath -ldflags="-s -w -H=windowsgui -extldflags '-static'" ``` ```bash go build -o seanime.exe -trimpath -ldflags="-s -w" -tags=nosystray ``` ```bash go build -o seanime -trimpath -ldflags="-s -w" ``` -------------------------------- ### Build Seanime Server (Sidecar) with Go Source: https://github.com/5rahim/seanime/blob/main/seanime-denshi/README.md Instructions for building the Seanime server (sidecar) using Go. This includes cross-compilation for Windows, Linux, and macOS, with specific flags for optimization and platform targeting. ```go # Working dir: . # Windows go build -o seanime.exe -trimpath -ldflags="-s -w" -tags=nosystray # Linux, macOS go build -o seanime -trimpath -ldflags="-s -w" ``` -------------------------------- ### Build Seanime Electron Desktop Client Source: https://github.com/5rahim/seanime/blob/main/seanime-denshi/README.md Commands to build the Seanime Electron desktop client for various platforms. Supports building for all platforms, or specific ones like macOS, Windows, and Linux. ```shell # To build the desktop client for all platforms: npm run build # To build for specific platforms: npm run build:mac npm run build:win npm run build:linux ``` -------------------------------- ### Initialize DenshiCast Receiver Source: https://github.com/5rahim/seanime/blob/main/seanime-denshi/src/cast/receiver/index.html Initializes a new DenshiCastReceiver instance when the window loads. ```javascript window.addEventListener("load", () => { new DenshiCastReceiver() }) ``` -------------------------------- ### Return to Project Root Directory Source: https://github.com/5rahim/seanime/blob/main/DEVELOPMENT_AND_BUILD.md Navigates back to the root directory of the project after performing operations within the internal API directories. ```bash cd ../../.. ``` -------------------------------- ### Build Seanime Web Interface Source: https://github.com/5rahim/seanime/blob/main/seanime-denshi/README.md Commands to build the web interface for Seanime. It involves running npm build scripts and moving the output to the correct directories. ```shell # Working dir: ./seanime-web npm run build npm run build:denshi # UNIX command mv ./seanime-web/out ./web mv ./seanime-web/out-denshi ./seanime-denshi/web-denshi ``` -------------------------------- ### Generate AniList API Client Code Source: https://github.com/5rahim/seanime/blob/main/DEVELOPMENT_AND_BUILD.md Executes gqlgenc to generate Go client code and types based on the AniList GraphQL schema. This command should be run after updating the schema. ```bash go run github.com/gqlgo/gqlgenc ``` -------------------------------- ### Record AniList Fixtures with Package Targeting Source: https://github.com/5rahim/seanime/blob/main/DEVELOPMENT_AND_BUILD.md Executes the AniList fixture recording script, targeting specific packages to widen the scope of recorded fixtures. This is useful for updating fixtures for particular modules. ```bash go run ./scripts/record_anilist_fixtures ./internal/api/anilist ./internal/library/scanner ``` -------------------------------- ### Navigate to AniList API Directory Source: https://github.com/5rahim/seanime/blob/main/DEVELOPMENT_AND_BUILD.md Changes the current directory to the AniList API client generation location. This is a prerequisite step before running the code generation command. ```bash cd internal/api/anilist ``` -------------------------------- ### Record AniList Fixtures with Live Test Targeting Source: https://github.com/5rahim/seanime/blob/main/DEVELOPMENT_AND_BUILD.md Runs the AniList fixture recording script while targeting specific live refresh tests using a regular expression. This allows for focused updating of fixtures for particular test cases. ```bash go run ./scripts/record_anilist_fixtures -run 'TestGetAnimeByIdLive|TestBaseAnime_FetchMediaTree_BaseAnimeLive' ``` -------------------------------- ### Implement DenshiCastReceiver Class Source: https://github.com/5rahim/seanime/blob/main/seanime-denshi/src/cast/receiver/index.html Core class for managing the Cast Receiver lifecycle, custom message handling, and JASSUB subtitle integration. ```javascript "use strict" const CUSTOM_NAMESPACE = "urn:x-cast:com.denshi.cast" const DEFAULT_FONT_NAME = "roboto medium" const DEFAULT_SUBTITLE_HEADER = `[Script Info] Title: Default ScriptType: v4.00+ WrapStyle: 0 PlayResX: 640 PlayResY: 360 ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding Style: Default, Roboto Medium,24,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,1.3,0,2,20,20,23,0 [Events] ` class DenshiCastReceiver { constructor() { this.context = cast.framework.CastReceiverContext.getInstance() this.playerManager = this.context.getPlayerManager() this.jassubRenderer = null this.videoElement = null this.tracks = {} this.currentTrackNumber = -1 this.fontsLoaded = [] this.subtitleEventsCache = new Map() // trackNumber -> events[] this._init() } _init() { const mediaPlayer = document.querySelector("cast-media-player") this._waitForVideoElement(mediaPlayer).then((video) => { this.videoElement = video console.log("[DenshiReceiver] Video element found") // Init JASSUB this._initJassub() }) // Custom message handler this.context.addCustomMessageListener(CUSTOM_NAMESPACE, (event) => { this._handleCustomMessage(event.data) }) // Intercept LOAD to accept spoofed MIME types this.playerManager.setMessageInterceptor( cast.framework.messages.MessageType.LOAD, (request) => { console.log("[DenshiReceiver] LOAD intercepted:", request.media?.contentType) // Clear previous subtitle state this._clearSubtitles() return request } ) this.playerManager.addEventListener( cast.framework.events.EventType.MEDIA_STATUS, (event) => { } ) this.playerManager.addEventListener( cast.framework.events.EventType.SEEKED, () => { // Re-sync subtitles on seek if (this.jassubRenderer) { this.jassubRenderer.resize() } } ) const options = new cast.framework.CastReceiverOptions() options.disableIdleTimeout = true options.customNamespaces = {} options.customNamespaces[CUSTOM_NAMESPACE] = cast.framework.system.MessageType.JSON this.context.start(options) console.log("[DenshiReceiver] Receiver started") // Notify sender that receiver is ready this._sendToSender({ type: "receiverReady" }) } async _waitForVideoElement(mediaPlayer, maxAttempts = 50) { for (let i = 0; i < maxAttempts; i++) { if (mediaPlayer.shadowRoot) { const video = mediaPlayer.shadowRoot.querySelector("video") if (video) return video } // Try direct child const video = mediaPlayer.querySelector("video") if (video) return video const docVideo = document.querySelector("video") if (docVideo) return docVideo await new Promise(r => setTimeout(r, 200)) } throw new Error("Could not find video element") } _initJassub() { if (!this.videoElement || this.jassubRenderer) return try { this.jassubRenderer = new JASSUB({ video: this.videoElement, subContent: DEFAULT_SUBTITLE_HEADER, workerUrl: "jassub-worker.js", wasmUrl: "jassub-worker.wasm", modernWasmUrl: "jassub-worker-modern.wasm", fonts: [], defaultFont: DEFAULT_FONT_NAME, availableFonts: { [DEFAULT_FONT_NAME]: "fonts/Roboto-Medium.ttf", }, debug: false, }) console.log("[DenshiReceiver] JASSUB initialized") } catch (e) { console.error("[DenshiReceiver] Failed to initialize JASSUB:", e) this._sendToSender({ type: "error", payload: "Failed to init JASSUB: " + e.message }) } } _handleCustomMessage(data) { console.log("[DenshiReceiver] Custom message:", data.type) switch (data.type) { case "subtitleEvents": this._onSubtitleEvents(data.payload) break case "setTracks": this._onSetTracks(data.payload) break case "switchTrack": this._onSwitchTrack(data.payload.trackNumber) break case "fonts": this._onFonts(data.payload) break case "subtitleHeader": this._onSubtitleHeader(data.payload) break case "disableSubtitles": this._clearSubtitles() break } } // Feed subtitle events into JASSUB _onSubtitleEvents(events) { if (!this.jassubRenderer || !events || !Array.isArray(events)) return for (const event of events) { if (!this.subtitleEventsCache.has(event.trackNumber)) { this.subtitleEventsCache.set(event.trackNumber, []) } this.subtitleEventsCache.get(event.trackNumber).push(event) // Only render for the active track if (event.trackNumber === this.currentTrackNumber) { this._addEventToJassub(event) } } } _addEventToJassub(event) { if (!this.jassubRenderer) return try { const assEvent = { Start: event.startTime / 1000, Duration: event.duration / 1000, Styl ``` -------------------------------- ### Sync Fork with Upstream Main Source: https://github.com/5rahim/seanime/blob/main/CONTRIBUTING.md Commands to synchronize your local main branch with the upstream repository and push the updates to your fork. ```shell git fetch --all git checkout main git rebase upstream/main git push -u origin main ``` -------------------------------- ### Sync Local Repository with Upstream Source: https://github.com/5rahim/seanime/blob/main/CONTRIBUTING.md Commands to fetch the latest changes from the upstream repository and rebase your local branch to keep it up to date. ```shell git fetch --all git rebase upstream/main ``` -------------------------------- ### Create and Push Feature Branch Source: https://github.com/5rahim/seanime/blob/main/CONTRIBUTING.md Commands to create a new feature branch from main and push the changes to your remote fork. ```shell git checkout -b main # After committing changes git push -u origin ``` -------------------------------- ### Seanime Web Project Directory Structure Source: https://github.com/5rahim/seanime/blob/main/seanime-web/README.md This snippet displays the file and directory layout for the Seanime Web project. It shows the organization of source files, public assets, API clients, application components, and routing configurations. ```txt .\nseanime-web/\n ├── public\n └── src/\n ├── api/\n │ ├── client\n │ ├── generated\n │ └── hooks\n ├── app/\n │ └── (main)/\n │ ├── _atoms\n │ ├── _electron\n │ ├── _features\n │ ├── _hooks\n │ ├── _listeners\n │ ├── auth\n │ └── ...\n ├── components\n ├── hooks\n ├── lib\n ├── routes/\t\n │ ├── _main\n │ └── ...\n └── types ``` -------------------------------- ### Extract Go Package Symbols with Yaegi CLI Source: https://github.com/5rahim/seanime/blob/main/internal/extension_repo/README.md This snippet demonstrates how to navigate to the interpreter directory and use the yaegi extract command to generate symbol files for external Go packages. This is a prerequisite for making third-party libraries available within the Yaegi runtime. ```bash cd internal/yaegi_interp yaegi extract "github.com/5rahim/hibike/a/b" yaegi extract "github.com/a/b/c" ``` -------------------------------- ### Record AniList Fixtures Source: https://github.com/5rahim/seanime/blob/main/DEVELOPMENT_AND_BUILD.md A script to record or refresh AniList API fixtures. This is used for testing purposes when you need to capture live API responses to be used as mock data in subsequent test runs. ```bash go run ./scripts/record_anilist_fixtures ``` -------------------------------- ### Go: Schedule DOM Element Object Creation Source: https://github.com/5rahim/seanime/blob/main/internal/plugin/ui/DOCS.md Schedules the creation of DOM element objects within the Go runtime's asynchronous task scheduler. This is a safer alternative to direct manipulation within potentially synchronous functions. It ensures that DOM operations are handled off the main thread. Dependencies include goja for JavaScript objects. ```go d.ctx.ScheduleAsync(func() error { arr := make([]*goja.Object, 0, len(v)) for _, elem := range v { if elemData, ok := elem.(map[string]interface{}); ok { arr = append(arr, d.createDOMElementObject(elemData)) } } return nil }) ``` -------------------------------- ### Switch Subtitle Track Source: https://github.com/5rahim/seanime/blob/main/seanime-denshi/src/cast/receiver/index.html Switches to a specified subtitle track. Clears subtitles if the track is not found. Sets the ASS header and re-adds cached events for the new track. Resizes the renderer and notifies the sender. ```typescript this._onSwitchTrack(trackNumber) { console.log("\n[DenshiReceiver] Switching to track:", trackNumber) this.currentTrackNumber = trackNumber const track = this.tracks[trackNumber] if (!track) { this._clearSubtitles() return } const header = track.codecPrivate || DEFAULT_SUBTITLE_HEADER if (this.jassubRenderer) { this.jassubRenderer.setTrack(header) const cachedEvents = this.subtitleEventsCache.get(trackNumber) if (cachedEvents) { for (const event of cachedEvents) { this._addEventToJassub(event) } } this.jassubRenderer.resize() } this._sendToSender({ type: "subtitleTrackChanged", payload: { trackNumber }, }) } ``` -------------------------------- ### Configure CSS for Cast Media Player Source: https://github.com/5rahim/seanime/blob/main/seanime-denshi/src/cast/receiver/index.html Defines the visual layout and hides default captions for the cast media player. ```css html, body { margin: 0; padding: 0; width: 100%; height: 100%; background: #000; overflow: hidden; } cast-media-player { --background-image: none; --logo-image: none; width: 100%; height: 100%; } #subtitle-canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 10; } cast-media-player::part(captions) { display: none !important; } ``` -------------------------------- ### Receive and Set Tracks Source: https://github.com/5rahim/seanime/blob/main/seanime-denshi/src/cast/receiver/index.html Receives track information from the sender and stores it. Logs the number of tracks set. ```typescript this._onSetTracks(tracks) { this.tracks = {} for (const track of tracks) { this.tracks[track.number] = track } console.log("\n[DenshiReceiver] Tracks set:", Object.keys(this.tracks).length) } ``` -------------------------------- ### Set Subtitle Header Source: https://github.com/5rahim/seanime/blob/main/seanime-denshi/src/cast/receiver/index.html Sets the subtitle track header for the Jassub renderer and resizes it. Returns early if the renderer is not available. ```typescript this._onSubtitleHeader(header) { if (!this.jassubRenderer) return this.jassubRenderer.setTrack(header) this.jassubRenderer.resize() } ``` -------------------------------- ### Regenerate API Types Source: https://github.com/5rahim/seanime/blob/main/DEVELOPMENT_AND_BUILD.md Updates TypeScript definitions based on Go handler modifications. ```bash go generate ./codegen/main.go ``` -------------------------------- ### Load Fonts Asynchronously Source: https://github.com/5rahim/seanime/blob/main/seanime-denshi/src/cast/receiver/index.html Loads new fonts into the Jassub renderer if they haven't been loaded already. Handles potential errors during font loading and logs them. ```typescript this._onFonts(fontUrls) { if (!this.jassubRenderer || !fontUrls) return try { const newFonts = fontUrls.filter(url => !this.fontsLoaded.includes(url)) if (newFonts.length > 0) { await this.jassubRenderer.addFonts(newFonts) this.fontsLoaded.push(...newFonts) console.log("\n[DenshiReceiver] Loaded", newFonts.length, "fonts") } } catch (e) { console.error("\n[DenshiReceiver] Error loading fonts:", e) } } ``` -------------------------------- ### Add Subtitle Event to Jassub Renderer Source: https://github.com/5rahim/seanime/blob/main/seanime-denshi/src/cast/receiver/index.html Adds a subtitle event to the Jassub renderer. Handles potential errors during creation and logs them. Caches events for later re-addition. ```typescript this.jassubRenderer.createEvent(assEvent) } catch (e) { console.error("\n[DenshiReceiver] Error adding subtitle event:", e) } } ``` -------------------------------- ### Update Local Main Branch Source: https://github.com/5rahim/seanime/blob/main/CONTRIBUTING.md Commands to pull the latest changes from the upstream main branch into your local repository during development. ```shell git pull --rebase upstream main ``` -------------------------------- ### Send Custom Message to Sender Source: https://github.com/5rahim/seanime/blob/main/seanime-denshi/src/cast/receiver/index.html Sends a custom message to the sender using the provided context. Handles potential errors during message sending and logs them. ```typescript this._sendToSender(message) { try { this.context.sendCustomMessage(CUSTOM_NAMESPACE, undefined, message) } catch (e) { console.error("\n[DenshiReceiver] Error sending to sender:", e) } } ``` -------------------------------- ### Clear Subtitles Source: https://github.com/5rahim/seanime/blob/main/seanime-denshi/src/cast/receiver/index.html Clears the current subtitles by resetting the track to the default header and resizing the renderer. Updates the current track number. ```typescript this._clearSubtitles() { this.currentTrackNumber = -1 if (this.jassubRenderer) { this.jassubRenderer.setTrack(DEFAULT_SUBTITLE_HEADER) this.jassubRenderer.resize() } } ``` -------------------------------- ### TypeScript: Register Event Handler for DOM Interaction Source: https://github.com/5rahim/seanime/blob/main/internal/plugin/ui/DOCS.md Registers an event handler in TypeScript that queries a DOM element and calls its getChildren method. This snippet demonstrates how to interact with the DOM from the client-side. It assumes the existence of a global `ctx` object with DOM manipulation capabilities. The primary purpose is to show a typical client-side event-driven interaction. ```typescript ctx.registerEventHandler("test", () => { const el = ctx.dom.queryOne("#test") el.getChildren() }) ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.