64 Commits
Author SHA1 Message Date
foxyz 1fd4081461 Release v1.1.0
ci / updater-windows (push) Successful in 32s
ci / core (push) Successful in 27s
release / windows (push) Failing after 20s
ci / server (push) Successful in 54s
release / linux (push) Successful in 3m46s
2026-10-09 23:53:53 +03:00
foxyzandClaude Opus 5.5 2d39018a73 One updater for app and server: updater in core; watch-together-server --upgrade / --check-update; update.sh uses the server
The server's version is stamped like the app's (APP_VERSION) when packaged,
so it can compare against the manifest; --upgrade swaps the whole install
folder (app and server) and asks for a restart. update.sh/.bat no longer
need GTK.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:53:52 +03:00
foxyzandClaude Opus 5.5 059b7dd306 Solo: no idle unready, no party corner in fullscreen; a cancelled Invite returns to solo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:39:48 +03:00
foxyzandClaude Opus 5.5 0115d54ab8 Strip shows the joined server's name (its --name via /health), the address until known
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:36:30 +03:00
foxyzandClaude Opus 5.5 fe5d6c3b3f Relay HLS/DASH-only pages as pieces tracks (protocol 11)
A track can be served as pieces: the server writes a VOD playlist whose
entries are its own piece URLs (segments, init maps, AES-128 keys; byte
ranges folded in; yt-dlp's DASH fragments become the same list). A piece
buffer per track fills like the byte buffer. Picks a single file, then a
split byte pair, then pieces. DRM, live, master playlists are refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:35:31 +03:00
foxyzandClaude Opus 5.5 05f9e794a9 Cookies: keep every site of an imported file (no site picker); only the cookies box takes drops on Settings
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:26:55 +03:00
foxyzandClaude Opus 5.5 75065f90cd Welcome band only for newcomers, with Don't show again
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:13:34 +03:00
foxyzandClaude Opus 5.5 e0db5cc655 Solo watching and a clearer offline start
Putting anything up offline starts a solo session: an in-app server bound
to 127.0.0.1 only, joined like hosting, so tracking, next episode and the
player all work. Invite friends re-hosts on the Host card's port and
password with the same media and position; Stop needs no confirmation.
Offline, the Library opens with a welcome band (Join a friend, Host, Watch
solo); without a Jellyfin account it leads with Files and Link, Jellyfin
is an optional card. Play buttons work offline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:08:11 +03:00
foxyzandClaude Opus 5.5 8186fbb5d3 yt-dlp cookies: from a browser or a file (cookies.txt or extension JSON), per site, private; server --yt-dlp-cookies
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 22:59:10 +03:00
foxyzandClaude Opus 5.5 8b81da4069 Protocol 11: sit out replaces kicking; nudge
Anyone can sit anyone out (host included); only you bring yourself back.
Out members stay in chat and the party, never block or count, and load no
media; a card with the live position and Join back (Space) replaces the
video. The mini player's x sits you out; optionally idle does too. Nudge:
the server forwards it to the target only (3/s, 20/min per sender); the
target hears the nudge sound, gets a toast and a taskbar attention
request. Kick, its messages, errors and dialog are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:05:35 +03:00
foxyzandClaude Opus 5.5 dbfd351ee4 Relay picks the most compact video codec (HEVC, AV1, VP9, then h264)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:44:14 +03:00
foxyzandClaude Opus 5.5 a4be24a2a5 Sounds: three presets (Cosmic, Abyss, Wood & glass), chosen overall or per sound; nudge sound
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:21:43 +03:00
foxyzandClaude Opus 5.5 b038706dff Protocol 11: split-track relay (YouTube video+audio), chunked upstream reads
Relayed media can carry 2-4 byte tracks (video, audio): yt-dlp picks a
split pair when no single-file format exists (h264 first), the source
channel serves each track, the server keeps a buffer per track (budget
split by size, 8 MiB floor) and viewers open an mpv edl:// of the track
URLs. Expired URLs re-resolve once for all tracks. Upstream GETs are cut
into <= http_chunk_size (10 MiB) ranges: YouTube throttles larger ones.
Live streams and DRM are refused; HLS/DASH pieces are a follow-up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 19:59:41 +03:00
foxyzandClaude Opus 5.5 b2b77a6b3f Hot-restart guard comes from fox_utils 0.1.8 (shared with other media_kit apps)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:58:08 +03:00
foxyzandClaude Opus 5.5 d002ff6941 Linux debug: hot restart no longer crashes with live mpv players
Hot restart frees the old isolate's NativeCallables while mpv handles stay
alive; media_kit then quits them on the next start and mpv calls the dead
wakeup callback (SIGSEGV on the mpv 'core' thread). A debug-only runner
hook clears every live handle's wakeup callback on the engine's
on-pre-engine-restart signal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:53:07 +03:00
foxyzandClaude Opus 5.5 151ec95289 Show the app as Starsync (window titles, launcher entry, About, README); internals unchanged
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:47:35 +03:00
foxyzandClaude Opus 5.5 745717a4a0 yt-dlp for page links in every Link mode; app icon and Linux launcher entry
yt-dlp: a managed, checksum-verified, daily-updated yt-dlp in the app and
the dedicated server (--[no-]yt-dlp, --yt-dlp-dir). Pasted links are
probed: direct files stay links, pages are resolved for title, uploader,
duration and thumbnail. Everyone fetches it: mpv's ytdl hook on the
managed copy. I pass it on / the server fetches it: the best single-file
format, re-resolved once when its URL expires. Relay names never split
the /r/ path; viewers of server-fetched pages wait for the relay to go
live. No wire change.

Icon: two stars orbiting a play button (assets/icon/icon.svg, PNGs, the
Windows .ico). GTK app id moe.senkochan.watch_together; release builds
install their .desktop file and icons into ~/.local/share so Wayland
shows the icon; the window icon is set for X11. README shows the icon.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:21:22 +03:00
foxyzandClaude Opus 5.5 9ddd85de90 Esc steps out one level: chat, fullscreen, big player to mini, then ask to leave
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:41:38 +03:00
foxyzandClaude Opus 5.5 24fc137e4f No buffering rings at the end of short videos; links never guess an AniList show
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:23:43 +03:00
foxyzandClaude Opus 5.5 9a4b04672b Schedule: AniList entries split within a season (anilist_map, per-episode AniList id) count as on Jellyfin
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:12:43 +03:00
foxyz ff51d52fbb Release v1.0.1
ci / updater-windows (push) Successful in 47s
ci / core (push) Successful in 27s
ci / server (push) Successful in 45s
release / linux (push) Successful in 3m49s
release / windows (push) Failing after 5m53s
2026-10-09 08:18:29 +03:00
foxyzandClaude Opus 5.5 f2272d7980 Space never presses a focused button; chat toasts open the chat; plain wheel always scrolls the page
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 07:52:48 +03:00
foxyzandClaude Opus 5.5 cf80bb2167 End of video plays out, no false 'loading' near/after the end; bigger status pill avatars; start card fits the mini
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 07:31:03 +03:00
foxyzandClaude Opus 5.5 8effa573a0 Protocol 10 presence gate, drop/paste anywhere, quick UI fixes
Ready gate: whoever is in the server while it is not playing counts (joining
while playing does not pause anyone); a swap counts everyone present and
keeps ready flags; the quorum rule is gone. Optional 'Unready when the
video changes'. No 'Skip 1:30' guess when the media has chapters.

Drag & drop and Ctrl+V anywhere (fox_utils): files, folders and links get a
small card asking how to open them. Files series start collapsed.

UI: Tab never moves focus and Space is always ready outside text fields;
chat focuses its field; unread badge on uosc and a chip in fullscreen;
people-only chat with an Activity toggle; skip pills show 5 s then with
the controls; volume up to 150%; own avatar same size as the party.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 07:06:48 +03:00
foxyzandClaude Opus 5.5 5f22be6c20 Roadmap: wishlist placed (ready gate, quick UI, drop/paste, Files, yt-dlp, AniList, ops)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 06:54:08 +03:00
foxyzandClaude Opus 5.5 740273745b Desktop: depend on fox_utils v0.1.7 (git) for drag & drop and clipboard
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 06:32:22 +03:00
foxyzandClaude Opus 5.5 27b4e1dc0c Sounds: full volume by default; everyone ready is the bell alone
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 06:28:53 +03:00
foxyzandClaude Opus 5.5 fce392e425 Sounds: Japanese-house sound set in the app, layout demo removed
Eleven sounds for events that carry news (ready, unready, everyone ready,
join, leave, kick, new video, others' chat, idle warning, connection
lost/back); none for what the video already says. New voices: koto
(Karplus-Strong, bendable), furin chime, rin bowl, bamboo, hyoshigi,
shoji slide, in the Yo scale on D. Played by SynthSfx at the Settings
volume with per-sound switches and previews. The debug layout mock is
gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 06:23:22 +03:00
foxyzandClaude Opus 5.5 d8fd464e33 Sounds: synthesized sound set and a debug sound board
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 05:45:14 +03:00
foxyz 0c38528520 Release v1.0.0
ci / core (push) Successful in 25s
ci / updater-windows (push) Successful in 48s
ci / server (push) Successful in 49s
release / linux (push) Successful in 3m34s
release / windows (push) Successful in 4m38s
2026-10-09 01:39:35 +03:00
foxyzandClaude Opus 5.5 b534c8c7ea Player: now-playing title with cover, start banner, one status pill; media art for everyone
The swapper sends the media's poster and banner once (`art`, Jellyfin
images or AniList by parsed name); state carries their hashes and every
member fetches them from /i/ like avatars. state.media.swapBy names who
put the media up. uosc and the standard bar show cover, title and
episode; a banner card shows before playback starts. The top-left health
pill and the waiting pill become one frosted status pill in plain words.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 01:19:34 +03:00
foxyzandClaude Opus 5.5 d092ea371a 1.0 library + player settings: Jellyfin/Link/Files library with details panel, player gear, settings cards, host flag
Library: source switch, selectable cards with a details panel, one merged
Continue row, Latest grouped per series without re-releases, Link page with
previews and recent links, Files embedded. Player: gear popover in both
designs, progress line while controls hide, live hwdec. Settings rebuilt as
cards. State marks the host member (proto stays 9, unreleased); hosting
shows LAN invite addresses; window buttons in fullscreen. fadeThrough keeps
the outgoing view's state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:58:12 +03:00
foxyzandClaude Opus 5.5 99cda7ed04 Schedule: mouse drag scrolls the board sideways only; wheel stays vertical
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:31:58 +03:00
foxyzandClaude Opus 5.5 d29c12e4f0 1.0 shell: one window with a party strip; protocol 9 profile + avatars by hash
The shell (lib/src/shell/) replaces the Home and session screens:
- Party strip: profile, Library / Schedule / Settings tabs, Join and
  Host cards (hosting no longer takes media), status-ring avatar stack
  with the member list, chat & activity panel, Leave.
- The real session player is mounted once and shown full, as a
  draggable corner-snapping mini player, or fullscreen (window_manager,
  party and toasts overlaid); the Video is never mounted twice.
- Profile popover with inline rename, colour and a crop picker (256 px
  PNG), pushed live to the session; avatar toasts; waiting pill.
- Motion kit (fastEaseInToSlowEaseOut, fade-through, popovers,
  mouse-drag/wheel horizontal scrolling).
- Library = the existing picker for now (restyled next round); Schedule
  = the new SchedulePage; Settings = left nav over the existing panels.

Protocol 9 (packages/core, packages/server, sync client):
- {"t":"profile"} renames / changes the picture mid-session ("rename",
  "avatar" feed actions); names are cleaned the same way at hello.
- state.members carry avatarHash; pictures are served once from
  /a/<sid>/<token>/<hash> (PNG/JPEG/WebP/GIF by magic bytes only) and
  cached by hash in the app (AvatarCache).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:23:46 +03:00
foxyzandClaude Opus 5.5 f790470935 Files page: folders, video-only listing grouped by series, lazy mpv thumbnails
Standalone FilesPage (plugged into the shell later): watched folders
(sidebar, chips with remove when narrow), a header saying where you are,
series sections and a 16:9 grid for loose videos, thumbnails requested
only for tiles that stay visible and cancelled when scrolled away,
hidden-files count with examples, rescan, Recent with missing-file
state, play errors in a banner, offline browsing with play disabled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:13:28 +03:00
foxyzandClaude Opus 5.5 2eb6840ec4 Schedule: "on Jellyfin" for shows AnimeFlow doesn't track, AniList episode numbers
From real use: a show on Jellyfin read "not in AnimeFlow". AnimeFlow's
new GET /library lists every Jellyfin series with AniList ids per TVDB
season; matchSchedule uses it for untracked shows (status inLibrary,
Put it up, no Request) and to fill jellyfinSeriesId. A failing or
missing /library never fails the schedule. Episodes may carry
anilist_episode (split cours, absolute numbering), matched first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:01:28 +03:00
foxyzandClaude Opus 5.5 677ece81c4 Schedule page: day columns on AniList + AnimeFlow, real requests and owner inbox
Standalone SchedulePage (plugged into the shell later): cover-first
cards in day columns from last week's Monday, filters (On Jellyfin /
Following / All airing, niche toggle, compact), requests that really
call AnimeFlow (episode with AnimeFlow's own numbering, series via
AniDB search by AniList id), follow, Put it up resolving the Jellyfin
episode, the requests sheet with the owner's Retry/Decline, refresh
every 60 s while visible, stale-AniList and AnimeFlow-error states.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:59:22 +03:00
foxyzandClaude Opus 5.5 caa0765890 Schedule backend: AniList client, AnimeFlow client, schedule matching
lib/src/schedule/ for the 1.0 Schedule tab (UI and docs come with the
schedule round):
- AniListClient: airingSchedules in a window, 50 per page, adult/music
  dropped; memory 10 min + disk 1 h cache, one Retry-After wait on 429,
  stale cache on failure; real User-Agent (AniList 403s some defaults).
- AnimeFlowClient: the full app API (me, schedule, series, follow,
  search, requests incl. approve/reject, users/scopes) with the user's
  Jellyfin token, only for a Jellyfin on AnimeFlow's parent domain;
  ETag/304, typed error codes, backoff on nginx's HTML 429, /me cached
  at most 60 s so scope changes show.
- matchSchedule + ScheduleRepository: AniList airings with AnimeFlow
  status by (episode anilist_id, episode), then absolute number, then
  air day; degrades to AniList-only when AnimeFlow fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:47:33 +03:00
foxyzandClaude Opus 5.5 c76fee70dc Files backend: release-name parser, video-only scanner, folder store, mpv thumbnails
lib/src/files/ for the 1.0 Files source (UI comes with the library
round; ARCHITECTURE gets its section then):
- parseReleaseName: series/season/episode/group/year/extras from release
  names (fansub, scene, batch, NCOP/OVA markers); seriesKey for grouping.
- scanFolder (isolate): videos only, samples/trailers and other files
  counted as hidden, sidecar subtitles paired by name (+ language/flag
  suffixes), 20 000-file cap; groupBySeries -> series + loose videos.
- FolderStore: watched folders and recent files in Hive, as signals.
- thumbnailFor: a 480 px JPEG at 25% via a hidden mpv player
  (screenshot-to-file, vo=null), cached by path+size+mtime, 2 at a time,
  cancellable, never throws. No ffmpeg needed (Windows ships only libmpv).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:46:01 +03:00
foxyzandClaude Opus 5.5 6f136bfc4d Debug-only layout demo of the 1.0 shell (reference for the redesign)
Behind a bug button in Home's top bar, debug builds only: single window
with a party strip, Library (Jellyfin / Link / Files), Schedule on real
AniList data with AnimeFlow statuses (read-only), Settings, profile with
live rename and avatar crop, floating mini player, full and fullscreen
player with a real media_kit player, chat panel and avatar toasts. Not
wired to sessions; the real implementation replaces it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:34:02 +03:00
foxyzandClaude Opus 5.5 e7f33b5dc3 Relay: a member's media through the server's buffer (proto 8)
ci / core (push) Successful in 28s
ci / updater-windows (push) Successful in 49s
ci / server (push) Successful in 49s
Viewers who cannot reach the media get it through the server: a file on
any member's computer, a link passed on by a member ("Through me") or
fetched by the server ("Server fetches it", password-protected servers
only), and Jellyfin items for guests without an account, sourced by
whoever put them up with their own sign-in (or a pasted link's key),
which never leaves their machine. Fixes guests on a dedicated server
without a Jellyfin login being blocked.

The source dials /r/.../source (works behind NAT) and answers pulls with
an adaptive in-flight window (2 x rate x min RTT, 256 KiB..8 MiB). The
server keeps a memory-only buffer of 1 MiB slices sized like a viewer's
mpv buffer (pinned head/tail, 64 MiB back, 256 MiB ahead), fills it with
one sequential pull at a time (blocked viewers first, newest request
only), fetches nothing while nobody watches through it, and drops it on
the next swap or stop. Measured with mpv through shaped links
(tool/relay_e2e.dart): 400 Mbit/s seeks ~0.03 s; 40 Mbit/s no stalls,
3 viewers cost the source ~1x the bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:41:01 +03:00
foxyz 9c5733719b Release v0.2.2
ci / core (push) Successful in 24s
ci / updater-windows (push) Successful in 45s
ci / server (push) Successful in 36s
release / linux (push) Successful in 3m31s
release / windows (push) Successful in 4m49s
2026-10-08 05:30:59 +03:00
foxyzandClaude Opus 5.5 50bcc2e1a2 CI: Windows build + release on yuuka-windows
ci / core (push) Successful in 32s
ci / server (push) Successful in 44s
ci / updater-windows (push) Successful in 1m25s
tool/package_windows.ps1 builds watch-together-<ver>-windows-x64.zip
(same layout as the Linux tarball, libmpv bundled, update.bat and
watch-together-server.exe). The release workflow gets a windows job;
ci runs the updater tests on Windows. Publishing moves to
tool/release_publish.dart (dart: only, no python/bash on Windows), which
tolerates both jobs racing to create the release and replaces an asset
on re-runs. release.sh writes both asset URLs into the manifest.
Windows jobs check out with plain git (the host's cached
actions/checkout clone was corrupt).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 22:27:38 +03:00
foxyzandClaude Opus 5.5 2c21cfde11 Updater: per-platform manifest assets, Windows apply tested on NTFS
The manifest gains `assets` (linux-x64 / windows-x64 → archive URL);
top-level `url` stays the Linux tarball for 0.2.1 installs. A Windows-
only test applies a bsdtar zip over a running exe (PING.EXE copy) and
checks it is parked as .old and swept next start; Linux-layout tests
(exec bits, symlinks) skip on Windows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 22:27:38 +03:00
foxyz e6f243d6a6 Release v0.2.1
ci / core (push) Successful in 24s
ci / server (push) Successful in 36s
release / linux (push) Successful in 3m35s
2026-10-07 03:39:00 +03:00
foxyzandClaude Opus 5.5 8845007cbf CI: create build/ before compiling the server; drop artifact upload
upload-artifact@v4 is unsupported on Gitea (GHES error) and failed the
release job after a successful build; the release asset is the
artifact. dart compile exe doesn't create the output directory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 03:38:59 +03:00
foxyz 8c71ee21c1 Release v0.2.0
ci / core (push) Successful in 24s
ci / server (push) Failing after 38s
release / linux (push) Failing after 3m27s
2026-10-07 03:15:36 +03:00
foxyzandClaude Opus 5.5 75d8651f17 Release CI: libmpv/epoxy deps, stable Flutter, multipart asset upload
The v0.1.0 release job failed: media_kit_video needs libmpv-dev and
libepoxy-dev at build time. Build on stable (what's verified locally),
work on root-only runner images, upload the asset as Gitea's multipart
attachment, and reuse an existing release on re-runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 03:15:35 +03:00
foxyzandClaude Opus 5.5 165cd2a7f9 Experimental mpv-style (uosc) controls behind a Settings toggle
Thick timeline with hatched cache range, chapter diamonds and
chapter/time hover boxes; collapses to a thin progress line when
hidden. Flat icon row with track count badges, shader preset badge,
flat dark menus. Own show/hide state, live switch in window and
fullscreen. New buffered signal from demuxer-cache-time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 03:10:17 +03:00
foxyzandClaude Opus 5.5 bc98c5c665 Chapters on the scrubber, Skip OP/ED, audio/subtitle menus
- PlaybackController exposes chapters, segments (Jellyfin
  MediaSegments for account holders), tracks, position/duration.
- Own seek bar with chapter diamonds and chapter title on hover,
  seeking through the sync user-seek path.
- Skip OP / Skip ED pills from Intro/Outro segments or chapter names
  (ED skips to its end only); Skip 1:30 fallback for >20 min videos
  without OP info; next-episode pill now starts at the ED.
- PgUp/PgDn chapters, Shift+Right +1:30; Audio and Subtitles menus
  with count badges via the existing track path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 02:56:41 +03:00
foxyzandClaude Opus 5.5 ae4edc29e8 Jellyfin end to end: per-viewer playback, progress, episodes, swap browser
- Host/swap send refs only; pasted Jellyfin links become refs; the host
  hands the relay its own app-obtained credential.
- Each viewer resolves with their own account (token in a header) or
  via the relay, else a clear blocked card with Link account.
- Per-account progress reporting (Playing/Progress/Stopped), 401 marks
  the account expired.
- Previous/next episode in the controls (< > / Shift+P/N), floating
  Next episode pill near the end, auto-advance countdown (one
  designated swapper), track carry-over per series.
- Swap panel uses the shared MediaPicker; browser location remembered
  per server and opens on the current series.
- Fix: states are applied one at a time (a stale pause could land
  after a newer play).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 02:38:24 +03:00
foxyzandClaude Opus 5.5 773a04369e Jellyfin in the app: account linking and library browser
Integrations links several Jellyfin servers (Quick Connect or
password, app-obtained tokens only, status/reconnect/sign out). The
Host tab's Jellyfin source is a full browser: continue watching, next
up, latest per library, library grids with sort/filter/infinite
scroll, series pages with smart resume/next-up, details, grouped
search, inspector pane on wide screens, keyboard navigation.
MediaPicker selection is now a sealed PickedMedia. Hosting a Jellyfin
pick is interim (host's stream URL) until per-viewer playback lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 02:05:46 +03:00
foxyzandClaude Opus 5.5 45f4c1bc84 Jellyfin refs on the wire + guest relay (proto 7)
- Media can be a Jellyfin ref {serverId, itemId, mediaSourceId, title,
  subtitle, runtimeSecs}; the server never stores or broadcasts a URL
  or token for it.
- Optional server-side Jellyfin credential (serve map / --jellyfin
  file) enables GET /j/<sid>/<token>/<itemId>: range-faithful proxy of
  the active item only, exact client header fields, upstream aborted on
  disconnect, bytes counted. welcome.relayUrl / state.relay advertise it.
- Members report jf access (own account vs relay).
- /m now sends the real Content-Type (Relic overrode it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 01:57:04 +03:00
foxyzandClaude Opus 5.5 fb2927c094 Shaders: Anime4K presets in-session and in Settings
Vendored Anime4K v4.0.1 GLSL (MIT), extracted to app support once per
version. Presets A/B/C/A+A/B+B/C+A in HQ and Fast per Anime4K's mpv
templates, plus custom files. Shader button in the video controls
(works in fullscreen), Ctrl+1..6 / Ctrl+0, live apply with read-back,
failures fall back to Off with a toast. Render mode detected from
mpv's GL log; warns when on the software path. Docs include the
Jellyfin client section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 01:45:54 +03:00
foxyzandClaude Opus 5.5 4814bfb561 Core: pure-Dart Jellyfin client
Auth (Quick Connect incl. the Initiate 400 fix: Content-Length + full
client header; password), server identity and link parsing, browsing
(views, resume, next up, latest, paged items, search, seasons,
episodes, next episode), images, PlaybackInfo and stream URLs,
progress reporting and user data, and a track matcher for carrying
audio/subtitle choices across episodes. Fixture tests plus env-gated
live tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 01:42:03 +03:00
foxyzandClaude Opus 5.5 d8d487c189 Sync fixes: clean seeks, clock offset, end of file, 2.5 min read-ahead (proto 6)
- User seeks are intercepted at the player (SyncPlayer), previewed
  locally, sent once 300 ms after the drag ends; own echoes are matched
  by seekSeq and never re-applied; the landed check can't reapply a
  stale target. 1 s position poll removed.
- ping/pong clock offset (min-RTT ClockSync); positions use server time.
- Reports carry duration; buffer gate is min(4 s, remaining - 2 s);
  new finished transport instead of a HELD stall at the end.
- cache/readahead 150 s, 256 MiB demuxer cache, pins read back.
- Resumed reports put a stale ready member back in the set; hello.resume
  takes over the old member after an unclean drop (no ghost).
- Docs: roadmap rendering findings (HW path works; shaders verified).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 01:20:18 +03:00
foxyzandClaude Opus 5.5 0669ec0fc2 Home v2: top bar with Join/Host tabs, content-first layout
Top bar merges window chrome, profile, tabs and page icons. Join: one
compact row plus recent-server tiles with live /health status and a
needs-update badge. Host: reusable MediaPicker (Jellyfin placeholder,
Link, File) filling the width, server settings in a narrow column.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 01:04:45 +03:00
foxyzandClaude Opus 5.5 81cac1214d Server on an isolate, dedicated server binary, room -> server (proto 5)
- serve(Map config, events) is the one server entrypoint: the app
  hosts it via Isolate.spawn (crash surfaced on the Host card, logs
  forwarded as [server]); bin/server.dart is the dedicated server,
  compiled into the bundle as watch-together-server. --headless gone.
- Host identity via a per-hosting hostKey in hello, replacing the
  loopback heuristic.
- Rename room -> server across code, files and wire (t:"state",
  /health name); protocol bumped to 5.
- CI compiles the server binary; isolate smoke tests in server and
  desktop suites. Docs updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 00:55:14 +03:00
foxyzandClaude Opus 5.5 d8a0729029 Docs rewrite: current-state docs, HANDOFF retired
README for the repo page; HOW-IT-WORKS overview; ARCHITECTURE,
PROTOCOL, DEVELOPMENT checked against the code; ROADMAP is backlog
only (isolate/dedicated server, sync fixes, rendering+shaders,
Windows build, Jellyfin epic). HANDOFF.md and mpv-http-contract.md
folded in and deleted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 00:42:28 +03:00
foxyzandClaude Opus 5.5 aeff087c7d Desktop: video freeze recovery, session fixes, Home redesign
- Player + VideoController per session; Reload video (Ctrl+R) and a
  frozen-picture watchdog that reloads automatically.
- Shortcuts no longer fire while typing; empty swap disabled;
  hwdec setting/env actually applied.
- Idle unready measured from real inactivity; cancellable reconnects
  and setup; ready restored from server state; kicked/failed host
  stops its server; graceful leave on window close; no URLs with
  keys in display names.
- Welcome + connect screens replaced by one Home screen with Join and
  Host cards, recent servers, Settings, Integrations and About.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 00:41:02 +03:00
foxyzandClaude Opus 5.5 beae87b0cc Updater: real-layout apply with rollback, in-app update + restart
- Strip the tarball's top-level folder; stage inside the install root
  (same filesystem) and swap with full rollback; keep file modes.
- Semver prerelease ordering (1.2.3-rc1 < 1.2.3).
- UpdateController: background check, download with progress, apply,
  restart; required-update wording for newer protocols; Check now.
- update.sh/update.bat in the bundle; Windows apply path (untested).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 00:41:02 +03:00
foxyzandClaude Opus 5.5 e2301aa4ec Server hardening: finite numbers, open servers, unkickable host, no URL leaks
- Reject non-finite numerics at protocol parse and in the engine
  (a 1e400 seek became Infinity and broke every broadcast).
- Reject empty swaps; sanitize URL-looking media names.
- Blank password = open server; defaults no longer 'change-me'.
- Loopback peers (the in-process host) can't be kicked.
- /health no longer exposes media/transport; file URLs use the
  host the client dialled instead of 127.0.0.1.
- Count served bytes as streamed, not as requested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 00:30:12 +03:00
foxyz 32848aaf71 Simplify release DX: local manifest, optional hash, slimmer publish
release.sh writes updates/manifest.json at release time (predictable
asset URL) — no CI commit-back, no push-token problem, no detached
checkout dance. release_publish.sh is create-release + upload only.
Manifest sha256 now optional; updater skips verification with a note
when absent (HTTPS + gzip CRC cover the realistic cases). Fixed the
repo-URL sed keeping a .git suffix (caught by a worktree dry run).
Updater tests: 8 (sha-less manifest flow added).
2026-09-13 03:03:48 +03:00
foxyz afb4a9e051 release.sh: annotated tags so --follow-tags actually sends them
Lightweight tags are silently skipped by --follow-tags; annotated
ones ride along. The v0.1.0 tag predates this and still needs an
explicit push.
2026-09-12 21:36:22 +03:00
298 changed files with 65599 additions and 4578 deletions

No files matched your search

+29
View File
@@ -31,3 +31,32 @@ jobs:
working-directory: packages/server
- run: dart test
working-directory: packages/server
# Compiling the dedicated server proves it stays Flutter-free.
- run: mkdir -p build && dart compile exe bin/server.dart -o build/watch-together-server
working-directory: packages/server
- run: build/watch-together-server --help
working-directory: packages/server
# The updater's Windows apply path (running exe parked as .old) only
# means something on real NTFS. Runner notes: see release.yml.
updater-windows:
runs-on: windows-latest
steps:
- name: Checkout
shell: powershell
run: |
git init -q .
git remote add origin "$env:GITHUB_SERVER_URL/$env:GITHUB_REPOSITORY.git"
git fetch -q origin "$env:GITHUB_SHA"
if ($LASTEXITCODE) { exit 1 }
git checkout -q --force FETCH_HEAD
- name: Setup Flutter
shell: powershell
run: powershell -NoProfile -ExecutionPolicy Bypass -File tool/setup_flutter_windows.ps1
- name: Updater tests
shell: powershell
working-directory: packages/core
run: |
dart pub get
if ($LASTEXITCODE) { exit 1 }
dart test test/updater_test.dart
+48 -14
View File
@@ -14,30 +14,64 @@ jobs:
fetch-depth: 0
- name: Install Linux build deps
# media_kit_video links libmpv + epoxy via pkg-config. Runner images
# differ: some run as root without sudo.
run: |
if command -v apt-get >/dev/null; then
sudo apt-get update
sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev
fi
SUDO=sudo; [ "$(id -u)" = 0 ] && SUDO=
$SUDO apt-get update
$SUDO apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev \
liblzma-dev g++ libmpv-dev libepoxy-dev xz-utils unzip curl python3
- name: Setup Flutter (beta)
- name: Setup Flutter (stable)
run: |
git clone https://github.com/flutter/flutter.git -b beta --depth 1 ~/flutter
git clone https://github.com/flutter/flutter.git -b stable --depth 1 ~/flutter
echo "$HOME/flutter/bin" >> $GITHUB_PATH
- name: Build tarball
run: tool/package_linux.sh
- name: Upload tarball artifact
uses: actions/upload-artifact@v4
with:
name: linux-tarball
path: dist/*.tar.gz
- name: Publish release + manifest
- name: Publish release
if: startsWith(github.ref, 'refs/tags/v')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
run: tool/release_publish.sh "${{ github.ref_name }}"
run: |
VER="${GITHUB_REF_NAME#v}"
dart tool/release_publish.dart "$GITHUB_REF_NAME" "dist/watch-together-$VER-linux-x64.tar.gz"
windows:
# yuuka-windows: host-mode runner (no containers), runs as SYSTEM, has
# git + VS 2022 Build Tools but no bash/python — PowerShell and Dart
# only. Checkout is plain git: that host's cached actions/checkout
# clone was corrupt, and no `uses:` means no action cache to rot.
runs-on: windows-latest
steps:
- name: Checkout
shell: powershell
run: |
git init -q .
git remote add origin "$env:GITHUB_SERVER_URL/$env:GITHUB_REPOSITORY.git"
git fetch -q --tags origin "$env:GITHUB_SHA"
if ($LASTEXITCODE) { exit 1 }
git checkout -q --force FETCH_HEAD
- name: Setup Flutter
shell: powershell
run: powershell -NoProfile -ExecutionPolicy Bypass -File tool/setup_flutter_windows.ps1
- name: Build zip
shell: powershell
run: powershell -NoProfile -ExecutionPolicy Bypass -File tool/package_windows.ps1
- name: Publish release
if: startsWith(github.ref, 'refs/tags/v')
shell: powershell
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
run: |
$Ver = $env:GITHUB_REF_NAME.TrimStart('v')
dart tool/release_publish.dart $env:GITHUB_REF_NAME "dist/watch-together-$Ver-windows-x64.zip"
if ($LASTEXITCODE) { exit 1 }
+31 -26
View File
@@ -1,35 +1,40 @@
# AGENTS.md — sync_relay_project
# AGENTS.md
Project conventions for agents. Global interaction rules live in the
user-level AGENTS.md; this file holds repo-specific facts only.
Source of truth order: codebase > `HANDOFF.md` + `docs/ROADMAP.md` >
chat history.
Repo-specific rules. Global interaction rules live in the user-level
AGENTS.md. Source of truth: the code, then the docs below.
## Docs
- [README.md](README.md): users. [docs/HOW-IT-WORKS.md](docs/HOW-IT-WORKS.md):
plain tour. [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md): packages, sync,
media, updater, decisions. [docs/PROTOCOL.md](docs/PROTOCOL.md): wire format.
[docs/DEVELOPMENT.md](docs/DEVELOPMENT.md): build, test, release, gotchas.
[docs/ROADMAP.md](docs/ROADMAP.md): backlog and epics.
- Docs describe current truth only: no dates, no "fixed" notes, no phase
logs (git has history, plans go in ROADMAP).
- Each fact lives in exactly one file; others link to it.
- Code and its docs change in the same commit. A wire change also bumps
`RelayConstants.protoVersion` and edits PROTOCOL.md.
- Say "server" (there is only one), never "room", in UI, code and docs.
## Wishlist drafts
The user drops raw idea/bug lists as drafts (gitignored, never
committed: `docs/Wishlist.md`, `docs/drafts/`). Agent duty on sight:
translate each entry into its proper place — `docs/ROADMAP.md`
backlog (ordered, one sitting-sized item each) or Epics, real bugs
with repro detail preserved — then confirm placement with the user
and delete the draft. Drafts must never be committed, even partially.
The user drops raw idea/bug lists as drafts (gitignored, never committed:
`docs/Wishlist.md`, `docs/drafts/`). On sight, translate each entry into its
place in `docs/ROADMAP.md` (ordered backlog item or epic; keep repro detail
for real bugs), confirm placement with the user, then delete the draft.
Drafts must never be committed, even partially.
## Commits
Few commits, one per topic/session — never per fix. Rewriting
unpushed history (amend, squash) is expected, not exceptional.
Commit code + its docs together. No `flutter analyze` unless asked
or diagnosing; scoped `dart analyze` on touched files is the
pre-commit check. Tests run only when the code under test changed.
Few commits, one per topic/session, never per fix. Rewriting unpushed
history (amend, squash) is normal. Commit code + its docs together. No
`flutter analyze` unless asked or diagnosing; scoped `dart analyze` on touched
files is the pre-commit check. Run tests only when the code under test changed.
## Releases (user pushes tags, CI does the rest)
## Releases
Release flow: `tool/release.sh [patch|minor|major]` derives the
version (no typing), bumps all pubspecs, commits, tags → review →
`git push --follow-tags` → `.gitea/workflows/release.yml`
builds, publishes the Forgejo release, commits
`updates/manifest.json` back. `tool/release_publish.sh` is the same
flow by hand. Workflows live in `.gitea/workflows` (this forge reads
`.gitea` first, `.github` is ignored when it exists).
Never commit secrets: tokens arrive via env (`DISCORD_WEBHOOK_URL`,
`RELEASE_TOKEN`) or Actions-provided (`GITHUB_TOKEN/GITEA_TOKEN`).
`tool/release.sh [patch|minor|major]` then `git push --follow-tags`; CI
publishes. Details: [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md). Never commit
secrets: tokens come from env (`DISCORD_WEBHOOK_URL`, `RELEASE_TOKEN`) or
Actions (`GITHUB_TOKEN`/`GITEA_TOKEN`).
-321
View File
@@ -1,321 +0,0 @@
# HANDOFF — sync_relay_project (watch-together app)
Date: 2026-09-12 (updated late session). Phases 0–4 + presence hardening
+ UI redesigns done, all green (core 42, server 41, desktop 24).
Phases retired as a tracker 09-12 — `docs/ROADMAP.md` is now a flat
backlog (Built / Backlog / Epics / Deferred); phase text lives in
git history.
Black screen (§8) DIAGNOSED + FIXED (avatar-texture teardown race;
Visibility keep-mounted) + VERIFIED, incl. over internet. Bisect
screens (legacy/minimal/nosheet) REMOVED 09-12 — app.dart routes
session straight to SessionScreen. Internet test PASSED (WAN port
6969, 09-12); user reports 2-client LAN soak + 1.4 GB real-file
test also done 09-12 (outcomes not yet recorded below). Tonight's
fixes: server logging wired into app (`initLogging` + hello logs),
Cancel aborts pending connect, first-join failure no longer
retry-loops. Git repo INITIALIZED (commit 427d851, root
.gitignore). Prior chat may be unreliable; this file plus `docs/`
is the full state (`docs/ROADMAP.md` is now the phase tracker).
## 1. What this is
Private watch-together desktop app (Windows + Linux). Host serves a file
or shared link from their own machine; 4–8 friends watch in sync.
One binary, three modes: Client (Join), Server+Client (Host, opens
ports, self-plays via loopback), `--headless` (server only, no window).
North star: <1.5 s drift, seeks settle ~2 s, cache rides short drops,
kick kills sync+video, no relay memory growth.
## 2. Locked architecture (do not reopen without the user)
- `packages/core`: protocol, sync engine, media abstractions. ZERO Flutter
imports (enforced by test). `packages/server`: Relic-based room + media
server, also zero Flutter imports. `apps/desktop`: the ONE Flutter
binary embedding both. Direct-serve model: host opens ports, clients
open nothing. NO host-upload tunnel (the brief's WSS chunk protocol is
obsolete unless a VPS tier ever needs remote-host upload).
- Server HTTP+WS: **Relic ^1.2.0**. Client WS: **web_socket_channel**.
Server lib also uses **web_socket** for event types.
- PACKAGES ARE NOT LOCKED (user decision 09-11, overrides the old brief
list): pick on merit, no churn where the stack works. Current desktop
additions: media_kit_video + media_kit_libs_video (required),
file_picker (avatar/media picking), hive_ce + hive_ce_flutter
(connect-form persistence), image (avatar shrink). signals + media_kit
+ Relic stay (load-bearing).
- Sync: heavily Syncplay-inspired + original readiness design, NOT 1:1.
Reference clone (scratch, may be gone — re-clone shallow):
`git clone --depth 1 https://github.com/Syncplay/syncplay.git /tmp/opencode/syncplay-ref`
Audit already done; conscious deviations: clock never moves from
reports (theirs follows slowest watcher), no pause-on-leave,
force-for-all instead of operator-only setOthersReadiness, and
QUORUM gate (09-12, user found solo-play meaningless): HELD→PLAYING
needs in-set size ≥ 2 unless ≤1 member is fresh — lone watcher plays
alone, second watcher present ⇒ one Space waits. Late joiners outside
the set never block.
- Single room password (one secret, persisted in Hive on the user's own
box — accepted). No op roles (deferred: op flag + second hello
secret). Kick = transient nudge: revoke + close + `kicked` reason,
instant rejoin with fresh token.
- Deferred, do not build: TLS/Caddy, Jellyfin native, updater
expansion (v1 `--upgrade` SHIPPED 09-12 — see §9.12; in-app
prompt + Windows helper still deferred), playlist (extension shape reserved in PROTOCOL.md), op roles, sounds
(researched, wired, no assets — see §7).
- Standing rules: health reporting (every failure displays, nothing
stalls silently); proto bump + PROTOCOL.md same-commit on ANY
wire change (still v4 — quorum/timeouts are server policy, no bump).
## 3. Repo map (root: Flutter/sync_relay_project/)
- `packages/core/lib/src/`: constants.dart (tuning: seek 300 ms,
drift 5 s, slow 1.5 s @0.95, min_buffer 4 s, heartbeat 1 s, ping 5 s,
avatar 256 KiB (base64 chars; wide headroom over any 128x128 PNG
— raised 09-12 from 64 KiB, which rejected normal PNGs), name 32, chat 500; dropAfter lives as RoomEngine ctor
default, now 30 s), errors.dart, logging.dart, config.dart,
protocol.dart (v4 schemas), media_source.dart, sync_engine.dart
(RoomEngine + quorum in evaluate's HELD branch; PLAYING branch
unchanged).
- `packages/server/lib/src/`: session.dart, media_endpoint.dart,
room_hub.dart (forgetSocket, sweepClosedSockets via `isClosed` poll,
closeAllSockets, onError→forgetSocket), room_server.dart (1 Hz
heartbeat: sweepStale + sweepClosedSockets + dropSilentSockets +
broadcast; stop() closes client sockets first). `bin/server.dart`.
- `apps/desktop/lib/src/`: sync/ (sync_client.dart: hello/intents/
fan-out/match-by-`t`; room_diff.dart: diff + debounce + play gate),
playback/ (media_kit Player, 64 MB cache, pin props, 0.5 s seek
deadband, stop() on leave, SYNC_RELAY_VO/HWDEC env overrides),
session/ (session_coordinator.dart: THE brain — room→player,
reports, scrub/nudge→seek intents incl. HELD, seek-land retry,
idle 8/10 min, reconnect loop, 5 s silence timeout; health.dart),
sfx/ (SfxKind/Cue/Service + NoopSfxService — sounds deferred),
state/ (session_state.dart: route/mode/lastForm/prefs/host/join/
leave; connect_prefs.dart), screens/ (welcome/connect/session),
widgets/ (user_section.dart: ConnectProfileCard; member_avatar.dart:
THE avatar — memoized, no bg on images, dark ring+fallback;
dialogs.dart: kick confirm; media_input.dart: parseMediaInput).
- `apps/desktop/test/`: room_diff_test (9), media_input_test (6),
updater_test (7: version/manifest/layout + stub-server flows),
sync_playback_e2e_test (quorum: solo Space holds, both ready plays;
needs mpv + ~/Videos/chiyo.mp4), coord_presence_test (dead server
clears + reconnects; needs mpv).
- `tool/`: range_logger, shaper (chaos: KB/s cap, latency,
--kill-after), fake_viewer, e2e_check (sim timeline, still ALL PASS
post-quorum), package_linux.sh (release tarball + Discord card,
stamps APP_VERSION).
- `docs/`: PROTOCOL.md (v4 + quorum + 15/30 s timeouts + isClosed note,
normative), ARCHITECTURE.md (+health rule), mpv-http-contract.md.
- `deploy/`: systemd unit. Git repo initialized 09-12 (`git init`,
root `.gitignore`, initial commit 427d851 covering phases 0–4 +
tonight's connect/logging fixes). Bisection is possible again.
## 4. Protocol v4 + policy (see PROTOCOL.md for normative detail)
hello→welcome(token, memberId, mediaUrl?, room-state); intents:
ready{ready,source}/force{target:all|memberId,ready}/seek/seek-swap/
report{position,buffered,eventId}/chat/kick{memberId}. Broadcasts:
room @1 Hz + on change (diff by eventId), feed{actor,action,…}
(toasts/log/sounds), kicked{reason}, err. Media:
GET /m/<sid>/<token>/<name> (200/206×3 shapes/416/401/403/410/503).
Transport PLAYING/HELD; gate = non-empty set AND all in-set ready +
buffered≥4 s + fresh AND quorum (in-set≥2 unless ≤1 fresh).
Stale >15 s ⇒ out of gate; silent >30 s ⇒ closed + removed.
Closed sockets swept every second; socket errors remove immediately.
Client: 5 s room-silence ⇒ dead (clear room, warn, reconnect loop
1.5 s polite infinite + manual cancel/retry, prior-flag restore).
## 5. Phase log (all exits met, all green as stated above)
- P0 scaffold + CI + Flutter-free tests; binary binds/logs/exits.
- P1 Range table byte-exact, real mpv traces, shaper built. 503 tested.
- P2 sessions: secret→128-bit token, 401/403/410 gates, kick kills WS+
media, byte counters, fake_viewer ALL PASS live, proto→3.
- P3 engine + readiness: engine tests (fake clock), e2e 15/15 sim,
Syncplay audit. Sanity: NaN guards, caps, stale/leave feeds,
1 Hz flush, token redaction.
- P4 desktop shell→app: SyncClient framing, RoomDiff (eventId diff,
slowdown latch 0.95×/0.1 reset, seek-land check), media_kit wiring
(pinned buffer props, buffered from demuxer), Space=ready, avatars,
toasts+sheet, health display, reconnect loop. Sounds WIRED ONLY
(NoopSfxService) — see §7.
- P5 presence+sync hardening (user-reported, all reproduced live
first): stop() closes sockets; isClosed sweep (~1 s graceful);
onError forget (~2–4 s kill -9, was 56 s ghost); drop 60→30 s;
client silence-timeout + stuck-reconnect-flag fix + ready-future
leak fix; seek race fixed (2 s same-event debounce both
transports, edge-aware detector 1 s guard, 0.5 s apply deadband);
HELD seeks send intents (paused frame moves for all); arrows
nudge ∓10 s; local play gate (in-set+ready); autoplay off;
leave() stops player (was leaking audio); kick confirm; Cancel
connect; connect-form memory→Hive persistence; file/media
Browse buttons.
- P6 UI redesigns (two subagents, disjoint files — repeat this
pattern): full-bleed video + overlays, controls never autohide
(24 h hover) + Ready/Mark-everyone-ready IN the controls bar
(intents only, zero local transport buttons), center avatars-only
64 px, slide-in toasts, animated right sheet (users/log/chat/swap/
Leave w/ confirm), health pill only-when-unwell, ESC closes/
asks-leave, global focus-proof keys (Space/Tab/arrows/ESC/F,
350 ms throttle, ignored while typing; Tab traversal disabled on
the session screen 09-12 — game-style, Tab toggles the sheet), single media field
(parseMediaInput + tests), avatar pipeline (128 px PNG, keeps
transparency — JPEG did not; self-check measures encoded base64
against RelayConstants, single source of truth), tap to
pick, no path fields), Discord-like corner profile (connect only;
welcome is title+Host/Join; edits apply next join — no rename
intent on the wire), dialogs for terminal states.
- UX decisions locked along the way: Space flips self-ready only;
free seeks any transport (no flag churn); join catch-up never
halts; idle 8-min warn + 10-min auto-unready; reconnect = re-hello
with flag restore; kick → dialog + connect screen; fixed 1.5 s
reconnect + cancel/retry; GMod toasts (social only) + side sheet;
avatar fallback = initial circle; "waiting for X (n/m ready)".
- Open user questions for later: warning-tone timing; bar width at
minimum window; second-ESC on leave dialog (re-asks vs cancel);
cancel-stops-host-server (kept: idle means no bound port).
## 6. Gotchas (learned the hard way)
1. Relic gives Range as TYPED RangeHeader — convert via unit+first.
2. Relic server-side onDone DOES NOT fire for dead peers (proven
live both directions: clean close AND kill -9 both ghosted to
timeout). Presence = isClosed heartbeat sweep + socket-onError
forget + lastSeen timeouts. NEVER depend on onDone. (Docs pattern
confirms CloseReceived is a data event.)
3. Welcome races room broadcasts — match WS messages by `t`, never
by index.
4. web_socket_channel streams are single-subscription — one listener
per socket (test double-listens throw "already been listened to").
5. `sub.asFuture()` can resolve before `onDone` — poll with
deadlines in tests.
6. Comma-form mpv reconnect flags work here; colon-form rejected
(re-verify via set_property if touched).
7. Numeric literals corrupted once by tooling — verify with rg after
writes touching ports/consts.
8. Tool `bash` `&`-backgrounding is unreliable; use nohup + log file
in its OWN call, poll in later calls, kill via `fuser -k
PORT/tcp` (never `pkill -f`). Chain small calls, never long
`&`-chains (they hang the runner past timeout with zero output).
9. Tests must not assume threat model beyond trust-basis.
10. web_socket_channel 3.x: `channel.ready` errors unhandled on
refused connections — eat it (`unawaited(ch.ready.then(...,
onError: ...))`); stream onError is the reporting path.
11. MediaKit has NO public Player.setProperty — runtime props go via
guarded dynamic call on player.platform; cache size via public
PlayerConfiguration(bufferSize:).
12. NEVER `flutter test`-pump the session screen: media_kit Video
hangs the runner (a hung flutter_tester must be kill -9'd by PID;
pgrep carefully). Sync-path coverage lives in room_diff (pure),
lifecycle (server, raw WS), coord_presence + e2e (real mpv,
no widgets). Deleted session_overlays_test.dart for this reason.
13. No `flutter analyze` unless asked (IDE covers it) — except
deliberate per-change checks; kept clean throughout.
14. New `MediaDesktopVideoControlsThemeData` per build is fine
(stock guards subscriptions) but wasteful; leave it.
15. Env overrides for mpv experiments (no rebuild to switch):
SYNC_RELAY_VO (e.g. xv, gpu, gpu-next), SYNC_RELAY_HWDEC (e.g. no).
16. Hidden-but-mounted overlays keep focus: closing the sheet left
focus on an offstage button, so Space fell through (350 ms
throttle hole) into the hidden control. Fix pattern: ExcludeFocus
on the closed subtree + unfocus-on-close effect. Same reason new
overlay buttons reuse the shared no-focus node.
## 7. Sounds (deferred, wiring ready)
Base: Kenney Interface Sounds (CC0, confirmation_* ladder for
ascending-ready). Alt ladder: ObsydianX Pack 1 (CC0, avoid paid
packs). Soft layer: akx/Notifications (CC0 branch). Playback pick:
flutter_soloud (preload WAV, per-play pitch) — NOT added. Swap-in =
replace NoopSfxService; cues already fire with readyIndex. URLs +
mapping in 09-11 chat (unreliable — re-derive from sources above).
## 8. Black window while playing (09-12, FIXED)
Symptom was: room PLAYING ⇒ entire window black (audio + keys
alive, unready restored, zero errors). Cause: the waiting-avatars
center overlay unmounted its GPU textures (avatar Images) exactly
at the HELD→PLAYING flip; teardown racing mpv's frame uploads on
the shared GL context fails silently ⇒ black. Legacy chips never
unmounted and minimal never mounted, which is why they survived.
Fix: center overlay is Visibility(visible + maintainState +
maintainAnimation + maintainSize) — avatars stay mounted, paint
skipped when hidden. Verified by user with full tree, and holds
over internet (WAN 2-client test, port 6969, late 09-12) — CLOSED.
RULE: never unmount GPU-textured widgets on a transport-gated
branch; keep mounted + skip paint. Bisect screens
(legacy/minimal/nosheet + SYNC_RELAY_UI switch in app.dart)
DELETED 09-12 (commit tracks this); no commented bisect lines
remain in session_screen.dart.
## 9. Next (ordered, updated 09-12 late session)
1. §8 black screen — CLOSED (fix verified incl. over internet).
2. Remove bisect flags — DONE 09-12 (files deleted, app.dart
routes session directly; tree analyzes clean).
3. 2-client LAN soak — user reports DONE 09-12 (details pending).
4. 1.4 GB real-file media test — user reports DONE 09-12
(details pending; api_key must never land in files).
5.–8. MIGRATED 09-12 to `docs/ROADMAP.md` backlog (cache-exhaustion,
sounds, file-join wart, /health) — the roadmap is now the single
backlog; this section keeps session history + user-owned pendings
only.
9. FIXED 09-12: auto-unready fired while passively watching —
the 10-min check keyed off `_lastInput`, refreshed only by
explicit actions. Steady playback now counts as activity:
warn + auto-unready are skipped while room transport is
PLAYING (`session_coordinator.dart` idle block). HELD-and-idle
still unreadies after 10 min.
10. FIXED 09-12 (second attempt — first theory was wrong): one chat
send rendered as TWO log rows. Root cause is server dual
delivery, not double-send: `broadcastChat`
(`room_hub.dart:69`) emits both a `ChatMessage` (with text,
converted to a feed entry client-side) and a textless `feed`
entry, and the client logged both. The blank-guard in
`sendChat` was defense against a phantom. Real fix: client
drops textless `chat` feed entries (`sync_client.dart` feed
case) — one send, one row, one toast, one sfx. No wire
change, still v4. Chat field also keeps focus after send
(dedicated FocusNode, re-requested on submit/button).
11. KNOWN (non-breaking, fix later — roadmap backlog #2): seeks feel
jumpy (forward-back-forward) — suspect the seek-land retry
(`_checkSeekLanded`, `session_coordinator.dart:429`)
re-applying SeekAction against the 0.5 s deadband / 2 s
debounce and the 1 Hz room echo. Needs a live repro with a
timeline log before touching.
12. Updater v1 SHIPPED 09-12: `watch-together --upgrade`
(`apps/desktop/lib/src/update/updater.dart`, zero Flutter
imports, 7 tests) — manifest check, download, sha256, apply
over live install (extract-temp + delete-first move-over;
ETXTBSY forbids writing the running exe, delete+rename is
fine). Version stamped via `--dart-define=APP_VERSION`
(wired in `tool/package_linux.sh`; dev runs report
`0.0.0-dev`). Publishing is CI-driven: tag `vX.Y.Z` →
`.gitea/workflows/release.yml` builds, creates the Forgejo
release, uploads the tarball, commits the manifest back
(`tool/release_publish.sh` does the same by hand).
Manual trigger only: bump desktop pubspec in a release commit
first (script enforces tag == pubspec). No silent
proto jumps: updater warns when manifest protocol != local.
Environment: dart 3.14 beta, flutter 3.48 beta, mpv+ffmpeg present,
~/Videos/chiyo.mp4 sample. Never kill the user's Syncplay mpv
instance. Git repo at project root (init 09-12); commit code + docs
together; prefer FEW commits (one per topic/session, not per fix)
over bisect-granularity — user decision 09-12.
## 10. Window chrome — frameless, mpv-style (09-12, VERIFIED by user)
Native title bar gone everywhere (`window_manager ^0.5.2`,
`TitleBarStyle.hidden` in main.dart GUI path only, headless
untouched). New screens MUST bring their own drag + min/max/close
or they ship immovable — reuse `widgets/window_buttons.dart` +
`widgets/window_chrome_bar.dart` (welcome/connect pattern).
Session: drag is `widgets/window_drag_layer.dart`, topmost raw-
pointer observer inside the controls stack (a GestureDetector
below media_kit's overlay LOSES the arena — do not revert to
that); dead zones: bottom 120 px (seek stays exact), open sheet,
leave dialog. `modifyVolumeOnScroll: false` is load-bearing for
drag — side effect: volume is speaker-button-only, no scroll-
volume. Maximize button = media_kit fullscreen toggle in
session (no OS maximize there); NO aspect-ratio lock by user
decision (custom UI lives outside the video frame). Close =
graceful leave + stopServer, then window close. Min size
160x90, no max. Verified on KDE Wayland (Arch): bar-drag,
video-drag, fullscreen, free resize all good.
+375 -19
View File
@@ -1,28 +1,384 @@
# sync_relay_project
<p align="center">
<img src="apps/desktop/assets/icon/icon_256.png" width="128" height="128" alt="Starsync icon">
</p>
Watch-together desktop app (working title). One binary, three modes:
<h1 align="center">Starsync</h1>
- **Client** — GUI Join, sync WS + mpv. No server code runs.
- **Server+Client (Host)** — GUI Host starts `packages/server` in-process,
listens on a port, self-connects via loopback. Host must open ports.
Clients never open ports.
- **Headless** — `watch.exe --headless`, same server package, no window.
Watch a video with friends, in sync, with the quality of a real mpv player.
Pick from your Jellyfin library, paste any direct video link, or pick a file on your own disk;
everyone's play, pause and seek stay together.
v1 order: links support (shared http(s) media URL, Syncplay-style sync)
first, file relay (server-proxied bytes, whitelist case) after.
Jellyfin native + auto-update are deferred as their own undertakings.
Why not Jellyfin SyncPlay: it runs in the browser and its sync is poor.
This is a desktop app that drives mpv.
Layout:
Name: `watch-together` is a placeholder and the final name is undecided
(`sync-relay` is the working name). It appears in file names and the
update manifest until that is settled.
- Desktop only (Linux and Windows), always mpv. No web clients.
- No transcoding. Everyone plays the original file.
- Friends trust model: no accounts, no roles. A blank password means an open server.
How it fits together: [docs/HOW-IT-WORKS.md](docs/HOW-IT-WORKS.md).
## Install and update
From the Releases page of this repo:
- Linux: `watch-together-<version>-linux-x64.tar.gz`. Extract, run
`watch_together`. On start it adds itself to your app launcher (and its
icon), so the window shows its own icon on Wayland. Needs mpv's library installed (`mpv` or `libmpv2`).
- Windows: `watch-together-<version>-windows-x64.zip`. Extract, run
`watch_together.exe`. mpv is bundled. New and not yet tried on a real
Windows desktop; reports welcome.
Update from the app: when a newer version exists, an update indicator
appears in the strip at the top of the window and opens the update card. "Update and restart" downloads,
verifies, applies in place and restarts; the install folder must be writable
by you. Settings has Check now and an auto-check toggle. From a terminal:
`./watch-together-server --upgrade` (no display needed, so it works on a
server box too), `watch_together --upgrade`, or the shipped `update.sh` /
`update.bat` (which run the first). Each updates the whole folder, app and
server; restart whichever is running to use the new version. A
release with a newer protocol is a required update: everyone on a server
must run the same protocol version, and a mismatch is refused with a message
telling you to update.
## Getting started
Three ways in, all at the top of the Library when you are not in a server.
None of them needs Jellyfin or an account.
- **Join a friend** (most people): your friend sends you an address and,
maybe, a password. See [Join](#join).
- **Host**: you have the video and friends watch it with you. See
[Host from the app](#host-from-the-app).
- **Watch solo**: just play something from the Library. It plays for you
alone, with everything a server has (Jellyfin progress, next episode,
skip buttons); nothing is reachable from outside this computer. Invite
friends in the strip turns it into a hosted server (the Host card's port
and password) and the video carries on from where it was; Stop ends it.
## The window
One window. Along the top, the strip: your avatar (click it for your
profile: name, picture with a crop editor, colour, and links to the
settings), the Library / Schedule / Settings tabs, and the party. Offline
the party side has Join a friend and Host; watching solo it says Solo,
with Invite friends and Stop; in a server it shows everyone's avatar
with a ring for their state (green ready, amber buffering, grey not ready,
dimmed reconnecting, dimmed with a moon sitting out; a crown on whoever
hosts, a tower on whoever passes the video on). Click the avatars for the
member list (force ready, Nudge, Sit out, "Copy invite address"; when you host it lists this computer's
network addresses with the port, and friends outside your network need
that port forwarded or a server they can all reach, like a VPS). Beside them the server and "ready n/m", the chat button with unread
count, and Leave. Empty strip space drags the window.
The player fills the window under the strip. Pick a tab and it shrinks to
a mini player in a corner: drag it anywhere and it snaps to the nearest
corner; double-click or ⤢ makes it big again, × sits you out (below) and
× again closes it; the strip's screen button brings it back. F or
double-click on the picture goes fullscreen (the strip hides, the party
shows in a corner while paused, the window buttons sit in the player's top
bar while its controls are up), Esc leaves it. The controls show the
show's cover, title and episode top left (guests get the cover too); before
a video first plays, its banner fills the picture until playback starts.
One pill above the controls says what playback waits for ("Press Space
when you're ready", "Waiting for Bob and Mika · 2/3 ready", "Bob is
loading…") or what went wrong, with Retry or Cancel when it helps. Chat and activity open in a panel on the right (chat button or
Tab), ready to type; it shows messages, and its "Activity" toggle adds
joins, ready, seeks and the like between them. Joins, leaves, chat and
other news also pop up bottom left with the person's avatar; click a message to open the chat. Unread chat
shows as a count on the chat buttons and, in fullscreen with the controls
hidden, as a small chip top right.
Your name and picture change for everyone in the server right away; the
colour is yours only.
**Sitting out.** Step away without leaving: Sit out (your profile, your
row in the member list, or × on the mini player) unloads the video on your
end, and nobody waits for you any more; you stay in chat. Anyone can sit
anyone out, the host too ("Alice sat you out" tells you who). The player
then shows the cover, title and where the others are; Join back (or Space)
loads the video there and readies you. Settings → Party & network → "When idle,
sit out instead of just unready" does it after 10 idle minutes.
**Nudge.** Nudge in the member list pokes someone, sitting out or not:
only they hear it (a sound, a pop-up, and the taskbar entry asks for
attention when the window is in the background).
## Host from the app
Host in the strip: set a port and an optional password (blank = anyone can
join), then Host. The app starts a server inside itself and joins it; you
are the host (the crown). Nothing is up yet: pick what to
watch in the Library tab (below) and put it up. Leaving or
closing the app stops the server. Friends need to reach your machine: open
the TCP port on your router (or use a LAN or VPN address). Friends open
nothing. Ports you hosted on before are one click away in the Host card.
If you put up a local file, the server streams it to everyone from your
disk. Nothing is uploaded or stored anywhere.
## Library
What to watch, from three sources (the switch at the top). Drop a video,
a folder or a link anywhere on the window, or press Ctrl+V outside a
text field, and a small card asks how to open it (put it up, add the
folder to Files, or fill Link).
- **Jellyfin**: your linked servers (see below); with none linked it
points to Files and Link and offers Jellyfin as optional. Library chips beside the
switch, then one **Continue** row (what you were watching and what comes
next, one card per series, the one you watched most recently first;
series not touched for two months drop out) and **Latest in** each
library (one card per series: "S1E1" or "3 new"; Show all opens the
library newest first). Re-uploads (added long after they aired) are
hidden from Latest; the row's ⋮ menu turns that off. Search (/), library
grids (sort, unwatched, favourites) and series pages sit behind them.
Click a card to select it: its
details open on the right with "Put it up · S1E3", mark watched /
unwatched, remove from Continue and the episode list (click an episode
to choose it). Double-click a card or an episode to put it up at once.
- **Link**: paste an http(s) link: a video file, or a web page with a
video (YouTube, X, and whatever else yt-dlp knows). The preview shows
what it is (a page's title, uploader, length and thumbnail, else the
file name); in a server, pick how the
others get it (everyone fetches it, the server fetches it, or through
you). Your last 12 links are listed under it.
- **Files**: video folders you add on this computer, grouped by series
(click a series to list its episodes), or any file through Browse. The others stream it from you.
Put it up and the player opens. Not in a server, the buttons say Play
and it plays for you alone ([Watch solo](#getting-started)).
## Dedicated server
For an always-on box (no display needed): the release tarball also contains
`watch-together-server`, a small standalone server without the app. Run it,
then everyone (you included) uses Join.
```
/packages/core protocol, sync engine, messages (pure Dart, no Flutter)
/packages/server room + media HTTP endpoint (pure Dart, no Flutter)
/apps/desktop Flutter app embedding packages/server (the ONE binary)
/tool spikes + harnesses (range_logger, shaper, fake_viewer, e2e_check)
/deploy systemd unit, notes
/docs PROTOCOL.md, ARCHITECTURE.md, mpv-http-contract.md, ROADMAP.md
./watch-together-server --port 8080 --secret <password>
```
Phases + status: `docs/ROADMAP.md`. Session handoff: `HANDOFF.md` (root).
| Flag | Default | Meaning |
|---|---|---|
| `--port`, `-p` | 8080 | TCP port, all IPv4 interfaces |
| `--secret` | empty | password; empty = open server (a warning is printed) |
| `--name` | `server` | name reported by `/health` |
| `--media` | none | local file to serve as the starting video |
| `--advertise-host` | none | host put in file URLs instead of the one each viewer dialled |
| `--[no-]yt-dlp` | on | read web pages (YouTube, X, …) with yt-dlp when the server fetches a link |
| `--yt-dlp-dir` | next to the binary | writable folder for the server's yt-dlp (systemd: the unit's `StateDirectory`) |
| `--yt-dlp-cookies` | none | cookies.txt or cookie-extension JSON export for that yt-dlp (sites that refuse guests); copied into the yt-dlp folder at start, removed from it when not given. **An account login**: keep it private, use a throwaway account for YouTube |
| `--config` | none | YAML or JSON file with the same settings (`port`, `secret`, `name`, `mediaPath`, `advertiseHost`, `ytDlp`, `ytDlpDir`, `ytDlpCookies`); replaces the flags |
| `--upgrade` | | update the install folder to the latest release ([Install and update](#install-and-update)), then exit |
| `--check-update` | | print whether a newer release exists, then exit |
Enforcement: `core` and `server` must have zero Flutter imports (tested).
It prints `READY port=... name=...` once listening and stops cleanly on
Ctrl+C or SIGTERM. A dedicated server has no host.
To run it as a service, unpack the tarball to `/opt/watch-together` and use
[deploy/watch-together.service](deploy/watch-together.service) (copy it to
`/etc/systemd/system/`, edit the flags, `systemctl enable --now watch-together`).
## Join
Join in the strip. Servers you got into are listed first (up to 6), with
live status (online and how many are watching, or offline, plus "needs
update" when the server runs another protocol): click one to rejoin, x to
forget it. Or enter the host's address, port, and the password if there
is one, then Enter or Join. Your name and picture come from your profile
(your avatar at the top left).
## Settings
The Settings tab (Ctrl+,) has a page per topic, each setting with a line
of help: Profile; Party & network (the Host card's default port and
password, recent servers with remove); Player (below); Jellyfin &
integrations (linked Jellyfin servers, below, the AnimeFlow connection:
"connected as <name>" with what you may do, or why not, and playing the
next episode automatically); Notifications & sounds (what pops up; the sound
volume and sound set, and every sound with a preview, its own set and a switch); Updates; Keyboard shortcuts (read-only); About
(the version).
Player settings live in the player: ⚙ at the right end of its controls
(both designs) opens them, and Settings → Player has the same ones (plus
the custom shader files). Player design: Standard or uosc (below);
progress line when hidden (on by default: a 2 px line at the bottom of
the video while the controls are hidden); upscaling shader (see below);
hardware decoding (auto, off or custom; the env var `SYNC_RELAY_HWDEC`
overrides it); in the player also the subtitle and audio track. Every
change applies right away.
Anywhere in a server (unless you are typing): Space = ready / unready,
Tab = chat & activity (Tab never moves between buttons). Keys while the
player fills the window: Left/Right = seek 10 s, Up/Down = volume, Shift+Right = skip 1:30,
PgUp/PgDn = previous / next chapter (as in mpv), F = fullscreen, Esc =
close the chat, then fullscreen, then shrink the player to mini; from the
mini it asks to leave, Ctrl+R = reload video,
`<` / `>` or Shift+P / Shift+N = previous / next Jellyfin episode,
Ctrl+1..6 = Anime4K mode A, B, C, A+A, B+B, C+A, Ctrl+0 = shaders off.
Every seek, skip and chapter jump moves everyone.
Controls: the seek bar marks chapter starts with a diamond; hovering it
shows the time and the chapter title. The chapters button lists them
(current one checked; click to jump). "1:30" skips ahead 1:30. Audio and
Subtitles buttons (with the track count) list tracks as "Japanese · E-AC3
2.0 · title"; subtitles have Off. Volume (hover the speaker, scroll on
it, or Up/Down) goes to 150 % like mpv: past the tick at 100 % the bar
turns amber and mpv amplifies.
The uosc player design (⚙ → Player design, Standard by default,
switches right away): video controls modeled on mpv's uosc theme. A thick
timeline across the bottom shows the played part, the cached part ahead
(hatched), chapter starts as diamonds and the time; hovering it shows the
chapter title and the time there. With the controls hidden it stays as a
thin line (unless "Progress line when hidden" is off). Above it flat buttons: ≡ menu, Subtitles and Audio (with the
track count), the shader badge (the active Anime4K mode, e.g. "A HQ"),
ready, chapters, 1:30, previous / next episode, change video, volume, ⚙
and fullscreen; the title top left, chat and back-to-library top right. Keys and skip buttons work
the same.
Skipping: during the opening a "Skip OP" button floats in the corner (it
jumps to the end of the opening), during the ending "Skip ED" (to the end
of the ending only; what comes after is yours to skip). Any skip button
(also "Skip 1:30") shows for 5 seconds when it comes up, then only while
the controls are up (move the mouse). The opening and
ending come from Jellyfin's intro / outro segments when your Jellyfin has
them (needs a segment plugin, e.g. Intro Skipper), else from chapter names
(OP, Opening, Intro / ED, Ending, Credits, Outro). Without an opening or
any chapters, on anything longer than 20 minutes, "Skip 1:30" floats there
during the first 6 minutes.
## Ports
| What | Default | Notes |
|---|---|---|
| Sync WebSocket and `/health` | TCP 8080 | plain `ws://`, no TLS |
| Local file streaming | same port | path `/m/...`, needs a session token |
Only the host needs the port reachable.
## Jellyfin
Settings > Jellyfin & integrations > Add server: type the address, then sign in with
Quick Connect (the app shows a code; approve it in Jellyfin under Profile >
Quick Connect) or your username and password. Several servers can be
linked; each shows its status, and a star picks the default. Sign out
revokes the app's sign-in on the server. If a sign-in expires the server
shows "Reconnect" instead of disappearing.
The Library's Jellyfin source is a library browser: Continue watching (with
time left), Next up and the latest per library on the start page; library
grids with sort (name, date added, release date, rating, random) and
Unwatched / Favorites filters; series pages with seasons, episode progress
and a "Next up" button; details with the file's tracks; search. Clicking a
movie or episode picks it. On wide windows a side pane shows details of
whatever is under the mouse.
Keys in the browser: arrows move, Enter picks or opens, I = details,
Esc / Backspace / Alt+Left = back (Esc first clears a search), / or Ctrl+F =
search.
Putting up a movie or episode sends only the item to the server (which
Jellyfin server and which item), never a link or your sign-in. Pasting a Jellyfin link (web page or stream link) does the
same when the app can reach that Jellyfin; otherwise it is shared as a
plain link.
Watching:
- Everyone with their own account on that Jellyfin server plays the file
directly with their own sign-in. Jellyfin tracks progress per account:
your watched state, Continue watching and resume point follow what you
saw (reported on start, every 10 s, on pause, resume, seek and track
change, and when it stops).
- Friends without an account watch through the server: whoever put the
item up passes it on automatically, with their own sign-in, which never
leaves their computer (only the current item, only to people in the
session). They show "via host" in the member list (click the avatars). When the app hosts,
or a dedicated server has its own login (`--jellyfin <file>`, see
[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), Jellyfin relay), the server
fetches it itself instead.
- If nobody can pass it on (the person who put it up left), the app says
"No Jellyfin account here for ..." with a button to link one; it plays as
soon as you are signed in.
Episodes: the ⏮ / ⏭ buttons next to the controls (or `<` / `>`,
Shift+P / Shift+N) put up the previous or next episode for everyone. From
the ending (when it is known, see Skipping above; else in the last two
minutes) a "Next episode" button floats in the corner. When an
episode ends, a card shows what is next with Play next; with "Play the
next episode automatically" (Settings, on by default) it counts down 10 s
first, and Cancel stops it on your screen (seeking back stops it for
everyone). Anyone with an account can put the next one up; friends
watching via the host cannot.
Audio and subtitles: the Audio and Subtitles buttons in the controls pick tracks.
Your choice is remembered per series (and as a default) and the closest
match (language, then title, then codec and channels) is selected on the
next episode. This is per person; nobody else is affected.
"Change video" in the player's controls opens the Library tab (the player
goes mini): the library starts on the series you are watching with the
current season, else where you left it.
Anyone can put up a file from their own computer, also on a dedicated
server: it streams from your computer through the server to everyone (the
server keeps a few minutes in memory, so seeks and skipping the opening
are quick). Keep the app open while it plays. For a link you choose how
the others get it:
- Everyone fetches it (default): each app plays the link itself.
- Server fetches it: the server downloads it once for everyone, for links
that are slow or blocked for some. The server must be able to reach it,
and it must have a password (an open server refuses: anyone could make
it fetch addresses only it can reach).
- Through me: your computer downloads it and passes it on, for links only
you can open.
Web pages work in all three: the app and the dedicated server each
download [yt-dlp](https://github.com/yt-dlp/yt-dlp) by themselves (checked
against the release's checksums, updated once a day; Settings → Player
shows its version and has "Update now"), so there is nothing to install.
Sites that refuse guests (Bilibili answers "HTTP Error 412", YouTube may
ask to sign in) need cookies: Settings → Player → yt-dlp → Cookies reads
them from a browser, or from a cookies.txt or a cookie extension's JSON
export (picked or dropped; every site in it is kept). They stay on this
computer and are used by its yt-dlp only (your own playback, "through
me", and a server you host). Cookies log yt-dlp in as you on those
sites: keep the file private, and for YouTube consider a throwaway
account (Google may flag accounts used with yt-dlp).
YouTube works in all three too, and so do pages and links that only
offer streaming playlists (HLS `.m3u8`, DASH `.mpd`, the HLS of X videos).
The last two never show the link to anyone; they can't pass on live
streams or DRM-protected video (use "Everyone fetches it" for live).
Your upload has to beat the video's bitrate: a 25-minute 2 GB episode is
about 11 Mbit/s.
## Video quality and shaders
Each viewer can run mpv GLSL shaders on their own picture; nobody else is
affected. The app ships [Anime4K](https://github.com/bloc97/Anime4K) v4.0.1
(MIT) with the presets of its mpv guide: Mode A (1080p sources), B (720p),
C (480p), A+A, B+B, C+A, each as HQ (strong GPUs, GTX 1080 and up) or Fast
(the guide's low-end set). "Custom shaders" runs your own `.glsl` files.
Pick one with the ✨ button in the player's control bar (also in
fullscreen) or Ctrl+0..6; the change applies live and is saved, so the next
session starts with it. The player's ⚙ and Settings → Player set it too,
with HQ or Fast (used by Ctrl+1..6); Settings → Player holds the custom
files. Shaders cost GPU time
every frame and need hardware video rendering: on media_kit's software
fallback they do nothing, and the app says so. A shader that is missing or
fails to load shows a toast and switches shaders off.
## Troubleshooting
- "host offline - check address and port": wrong address, or the port is not open.
- "wrong server password": password mismatch.
- "host runs a different version": update the app on both sides.
- Picture frozen but sound fine: Ctrl+R reloads the video; the app also does it automatically.
- Everyone waits at "waiting for X": X has not buffered 4 seconds yet or has not pressed Space.
- Windows: the build is new; if something is off there, say so.
For contributors: [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md),
[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), [docs/PROTOCOL.md](docs/PROTOCOL.md).
+5 -16
View File
@@ -1,17 +1,6 @@
# watch_together
# apps/desktop
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter)
- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
The Flutter desktop app (what users run; hosting runs the server from
`packages/server` on an isolate). Layout and rules:
[../../docs/ARCHITECTURE.md](../../docs/ARCHITECTURE.md). Build and test:
[../../docs/DEVELOPMENT.md](../../docs/DEVELOPMENT.md).
+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256">
<defs><linearGradient id="bg" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#2d2547"/><stop offset="1" stop-color="#14111d"/></linearGradient><linearGradient id="t1" gradientUnits="userSpaceOnUse" x1="50.7" y1="148.7" x2="184.6" y2="71.4"><stop offset="0" stop-color="#f0795a" stop-opacity="0"/><stop offset="0.45" stop-color="#f0795a" stop-opacity="0.85"/><stop offset="1" stop-color="#ffc56b"/></linearGradient><linearGradient id="t2" gradientUnits="userSpaceOnUse" x1="205.3" y1="107.3" x2="71.4" y2="184.6"><stop offset="0" stop-color="#ffc56b" stop-opacity="0"/><stop offset="0.45" stop-color="#ffc56b" stop-opacity="0.85"/><stop offset="1" stop-color="#f0795a"/></linearGradient></defs>
<rect x="8" y="8" width="240" height="240" rx="56" fill="url(#bg)"/>
<circle cx="128" cy="128" r="80" fill="none" stroke="#ffffff" stroke-opacity="0.10" stroke-width="6"/>
<path d="M205.27 107.29 A80 80 0 0 1 71.43 184.57" fill="none" stroke="url(#t2)" stroke-width="7" stroke-linecap="butt"/><path d="M50.73 148.71 A80 80 0 0 1 184.57 71.43" fill="none" stroke="url(#t1)" stroke-width="7" stroke-linecap="butt"/>
<path d="M110 96 L110 160 L162 128 Z" fill="#fff1d6" stroke="#fff1d6" stroke-width="16" stroke-linejoin="round"/>
<path d="M71.4 161.6 Q74.7 181.3 94.4 184.6 Q74.7 187.8 71.4 207.6 Q68.2 187.8 48.4 184.6 Q68.2 181.3 71.4 161.6 Z" fill="#f0795a"/>
<path d="M184.6 40.4 Q188.9 67.1 215.6 71.4 Q188.9 75.8 184.6 102.4 Q180.2 75.8 153.6 71.4 Q180.2 67.1 184.6 40.4 Z" fill="#ffc56b"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 732 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

@@ -0,0 +1,36 @@
// This is free and unencumbered software released into the public domain.
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.
// In jurisdictions that recognize copyright laws, the author or authors
// of this software dedicate any and all copyright interest in the
// software to the public domain. We make this dedication for the benefit
// of the public at large and to the detriment of our heirs and
// successors. We intend this dedication to be an overt act of
// relinquishment in perpetuity of all present and future rights to this
// software under copyright law.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// For more information, please refer to <https://unlicense.org>
//!DESC Anime4K-v4.0-AutoDownscalePre-x2
//!HOOK MAIN
//!BIND HOOKED
//!BIND NATIVE
//!WHEN OUTPUT.w NATIVE.w / 2.0 < OUTPUT.h NATIVE.h / 2.0 < * OUTPUT.w NATIVE.w / 1.2 > OUTPUT.h NATIVE.h / 1.2 > * *
//!WIDTH OUTPUT.w
//!HEIGHT OUTPUT.h
vec4 hook() {
return HOOKED_tex(HOOKED_pos);
}
@@ -0,0 +1,36 @@
// This is free and unencumbered software released into the public domain.
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.
// In jurisdictions that recognize copyright laws, the author or authors
// of this software dedicate any and all copyright interest in the
// software to the public domain. We make this dedication for the benefit
// of the public at large and to the detriment of our heirs and
// successors. We intend this dedication to be an overt act of
// relinquishment in perpetuity of all present and future rights to this
// software under copyright law.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// For more information, please refer to <https://unlicense.org>
//!DESC Anime4K-v3.2-AutoDownscalePre-x4
//!HOOK MAIN
//!BIND HOOKED
//!BIND NATIVE
//!WHEN OUTPUT.w NATIVE.w / 4.0 < OUTPUT.h NATIVE.h / 4.0 < * OUTPUT.w NATIVE.w / 2.4 > OUTPUT.h NATIVE.h / 2.4 > * *
//!WIDTH OUTPUT.w 2 /
//!HEIGHT OUTPUT.h 2 /
vec4 hook() {
return HOOKED_tex(HOOKED_pos);
}
@@ -0,0 +1,90 @@
// MIT License
// Copyright (c) 2019-2021 bloc97
// All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//!DESC Anime4K-v4.0-De-Ring-Compute-Statistics
//!HOOK MAIN
//!BIND HOOKED
//!SAVE STATSMAX
//!COMPONENTS 1
#define KERNELSIZE 5 //Kernel size, must be an positive odd integer.
#define KERNELHALFSIZE 2 //Half of the kernel size without remainder. Must be equal to trunc(KERNELSIZE/2).
float get_luma(vec4 rgba) {
return dot(vec4(0.299, 0.587, 0.114, 0.0), rgba);
}
vec4 hook() {
float gmax = 0.0;
for (int i=0; i<KERNELSIZE; i++) {
float g = get_luma(MAIN_texOff(vec2(i - KERNELHALFSIZE, 0)));
gmax = max(g, gmax);
}
return vec4(gmax, 0.0, 0.0, 0.0);
}
//!DESC Anime4K-v4.0-De-Ring-Compute-Statistics
//!HOOK MAIN
//!BIND HOOKED
//!BIND STATSMAX
//!SAVE STATSMAX
//!COMPONENTS 1
#define KERNELSIZE 5 //Kernel size, must be an positive odd integer.
#define KERNELHALFSIZE 2 //Half of the kernel size without remainder. Must be equal to trunc(KERNELSIZE/2).
vec4 hook() {
float gmax = 0.0;
for (int i=0; i<KERNELSIZE; i++) {
float g = STATSMAX_texOff(vec2(0, i - KERNELHALFSIZE)).x;
gmax = max(g, gmax);
}
return vec4(gmax, 0.0, 0.0, 0.0);
}
//!DESC Anime4K-v4.0-De-Ring-Clamp
//!HOOK PREKERNEL
//!BIND HOOKED
//!BIND STATSMAX
float get_luma(vec4 rgba) {
return dot(vec4(0.299, 0.587, 0.114, 0.0), rgba);
}
vec4 hook() {
float current_luma = get_luma(HOOKED_tex(HOOKED_pos));
float new_luma = min(current_luma, STATSMAX_tex(HOOKED_pos).x);
//This trick is only possible if the inverse Y->RGB matrix has 1 for every row... (which is the case for BT.709)
//Otherwise we would need to convert RGB to YUV, modify Y then convert back to RGB.
return HOOKED_tex(HOOKED_pos) - (current_luma - new_luma);
}
@@ -0,0 +1,275 @@
// MIT License
// Copyright (c) 2019-2021 bloc97
// All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//!DESC Anime4K-v4.0-Restore-CNN-(M)-Conv-4x3x3x3
//!HOOK MAIN
//!BIND MAIN
//!SAVE conv2d_tf
//!WIDTH MAIN.w
//!HEIGHT MAIN.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (MAIN_texOff(vec2(x_off, y_off)))
vec4 hook() {
vec4 result = mat4(-0.09991986, 0.13782342, -0.031251684, -0.06356843, -0.3437488, 0.05450952, 0.34347802, 0.46335372, 0.08607224, 0.044988394, 0.137179, 0.17976908, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, -1.0);
result += mat4(-0.024212424, -0.09278509, -0.00040907756, 0.34552294, -0.13254678, 0.113105185, 0.005667946, -0.00036919137, -0.06375679, 0.009184115, 0.115518734, -0.115506776, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 0.0);
result += mat4(-0.14101827, 0.023523493, 0.044094566, -0.019271746, -0.44348842, -0.08818877, -0.4026149, -0.21995795, -0.15880394, -0.013732858, -0.020751135, 0.012719151, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 1.0);
result += mat4(0.013001821, -0.34503505, 0.39219138, 0.18792126, 0.24760444, -0.016173402, 0.10154511, 0.15453082, -0.058132876, 0.016784398, -0.05808539, -0.11039915, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, -1.0);
result += mat4(0.37024534, 0.041440863, -0.3374568, -0.44994286, 0.19555596, 0.20855539, -0.27974075, -0.5372628, 0.21228147, -0.0295346, -0.56700057, 0.030042822, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 0.0);
result += mat4(-0.12940632, 0.057526, 0.090682045, -0.06985033, -0.13704006, -0.047685407, 0.44615674, -0.48056605, -0.06166251, -0.01883519, 0.2032237, -0.113287605, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 1.0);
result += mat4(0.010856669, -0.35820737, 0.16757219, 0.082619876, -0.03967303, 0.038705572, 0.32652855, -0.012030017, 0.015120559, -0.15314877, 0.23442009, 0.09767922, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, -1.0);
result += mat4(-0.046272673, -0.17752305, 0.082018286, -0.2512824, 0.58619463, -0.060903464, -0.022793597, 0.077803515, -0.17025311, 0.05136993, 0.029383298, -0.15475409, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 0.0);
result += mat4(-0.11212024, 0.13378005, -0.2027488, 0.08056421, -0.11176219, -0.048429377, -0.08396386, 0.10507829, 0.13326839, 0.0430627, 0.051362377, 0.06482755, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 1.0);
result += vec4(-0.061233472, 0.39222646, 0.029704979, 0.02586828);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_tf
//!SAVE conv2d_1_tf
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.16410656, -0.40521824, 0.13121907, -0.02314597, 0.105412476, -0.060401272, -0.043063477, -0.13933973, 0.12558138, -0.020861467, 0.030370515, 0.13178016, -0.14220351, 0.20736893, 0.003321564, -0.29241714) * go_0(-1.0, -1.0);
result += mat4(0.18517321, 0.29162985, -0.26783395, 0.039760686, 0.025527012, -0.067319244, 0.055004176, 0.048916563, 0.12750523, -0.091435954, 0.13818842, 0.36704224, 0.0839921, 0.10186618, -0.17237376, 0.13282418) * go_0(-1.0, 0.0);
result += mat4(-0.1657887, 0.0131325135, -0.17222486, 0.091398895, -0.12756164, -0.08437298, -0.29052997, 0.3269337, 0.15870757, -0.013529402, -0.0581753, 0.11802371, 0.07099966, -0.024063632, 0.31834844, -0.11183859) * go_0(-1.0, 1.0);
result += mat4(0.46036887, -0.07654623, 0.22923063, 0.17463821, 0.10555414, -0.117430426, 0.12406777, -0.011399492, 0.028316498, 0.13684341, 0.009664087, 0.2022659, 0.04953974, -0.31342217, -0.6103131, -0.13605757) * go_0(0.0, -1.0);
result += mat4(0.03406955, -0.39819366, 0.61176, -0.46809456, -0.029321073, 0.46619493, 0.36700186, 0.02288561, 0.11464085, -0.10931452, -0.09154022, 0.07334147, -0.5609916, 0.31826234, -0.011012659, -0.46719545) * go_0(0.0, 0.0);
result += mat4(-0.056855045, 0.27037027, -0.09269696, -0.563572, -0.06816116, -0.22986612, 0.08693167, -0.16246101, 0.09954046, -0.05374176, 0.0071916827, -0.1788692, 0.3825241, -0.1609887, 0.055204768, 0.10213068) * go_0(0.0, 1.0);
result += mat4(0.0646626, 0.102358796, -0.45055822, 0.20557903, -0.23337309, 0.12633002, -0.19299199, -0.15085731, -0.13473304, 0.053790465, -0.10061193, -0.13393497, -0.04264752, -0.029740738, -0.07865285, 0.20883279) * go_0(1.0, -1.0);
result += mat4(0.010471527, -0.033218473, -0.46157447, 0.004866583, 0.23226471, -0.059343327, -0.1439596, 0.13619648, 0.013839963, 0.15930325, 0.043742355, 0.17467323, 0.33772305, 0.40261495, -0.08351293, 0.18129359) * go_0(1.0, 0.0);
result += mat4(-0.12493434, -0.1875134, -0.074943796, -0.0031701606, -0.037142616, 0.1667002, 0.16665547, -0.011248127, 0.0071619414, 0.0034872112, 0.120318964, -0.09625579, 0.14917047, -0.16310586, 0.07231737, 0.30447328) * go_0(1.0, 1.0);
result += mat4(0.093798615, 0.17074613, -0.08780678, -0.012520207, 0.118534856, 0.027508778, -0.2778478, -0.19509242, -0.34137097, 0.32000312, -0.22027159, 0.337515, 0.16220862, 0.108993016, 0.14070526, 0.12784284) * go_1(-1.0, -1.0);
result += mat4(-0.14325632, -0.1467453, -0.27502358, 0.09370837, 0.11821083, -0.012266484, -0.2100548, 0.4707502, -0.06766648, 0.58165014, -0.2512279, -0.33783755, 0.1318925, -0.04346277, 0.15454485, 0.044500057) * go_1(-1.0, 0.0);
result += mat4(-0.05683207, 0.0051946463, -0.108000524, 0.10133204, -0.50763863, 0.007308442, 0.8542404, 0.28387356, 0.022709515, 0.294523, -0.3822472, 0.66166407, 0.01404485, 0.031282708, -0.26756814, -0.123147786) * go_1(-1.0, 1.0);
result += mat4(-0.36455178, 0.3470555, -0.045303088, -0.03170764, -0.15802494, -0.0019141496, -0.25939587, -0.23875342, 0.130428, 0.03954273, -0.17985536, 0.105145946, 0.15804817, 0.12551713, 0.28371975, -0.085748516) * go_1(0.0, -1.0);
result += mat4(0.0060625463, 0.2443924, -0.017692259, -0.20214005, -0.09584515, -0.012805372, -0.13942227, 0.16143198, 0.12942013, 0.41785547, 0.046071563, 0.7030026, 0.10499644, -0.20566013, -0.031321276, 0.27830327) * go_1(0.0, 0.0);
result += mat4(-0.081274964, -0.14562319, 0.27200526, -0.20491314, 0.012910989, 0.024201397, 0.04816258, 0.21297328, -0.22015952, -0.44160756, -0.056035373, 0.33824417, -0.31645304, 0.15469243, 0.053187452, -0.20989445) * go_1(0.0, 1.0);
result += mat4(-0.046550367, 0.033185404, 0.33337244, 0.12853645, 0.23520172, -0.05909214, 0.0861368, 0.10706329, -0.07058717, -0.11759937, -0.18594047, 0.080006264, -0.055425353, -0.12506317, 0.15729053, -0.0915004) * go_1(1.0, -1.0);
result += mat4(0.042516407, 0.14844789, 0.16533111, 0.13502933, -0.0655417, -0.057256397, 0.076713726, -0.23448966, 0.12855926, 0.014219275, 0.051761385, 0.053433083, -0.2446715, -0.4008074, 0.19603717, -0.1796951) * go_1(1.0, 0.0);
result += mat4(0.14777803, 0.15524907, 0.043158617, -0.06996876, 0.19210646, -0.2144364, -0.47020787, -0.4207906, -0.18074386, -0.2163903, 0.0030754965, 0.36799973, -0.3837698, -0.0022661497, -0.37276733, -0.28934997) * go_1(1.0, 1.0);
result += vec4(-0.018297346, -0.080951825, -0.062163066, -0.08050014);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_1_tf
//!SAVE conv2d_2_tf
//!WIDTH conv2d_1_tf.w
//!HEIGHT conv2d_1_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.31543177, 0.23095237, -0.06692611, -0.5867763, 0.003622504, 0.17948842, -0.14627707, 0.1745016, -0.052964583, -0.15551159, 0.05644786, -0.012665164, 0.13107763, 0.11369179, -0.09452995, -0.11973403) * go_0(-1.0, -1.0);
result += mat4(-0.2694661, -0.115382135, 0.3073268, -0.067228466, -0.25511482, -0.13922207, 0.36758214, -0.18821828, -0.022617863, 0.20333402, -0.11125889, 0.3552245, -0.013346653, -0.099095374, -0.25100616, 0.35521755) * go_0(-1.0, 0.0);
result += mat4(0.011012409, -0.13675085, 0.25642, -0.34851208, -0.23184675, 0.18012202, 0.57654136, 0.103173524, -0.16461405, 0.038177088, 0.1234096, 0.013202029, -0.19033363, 0.07469178, -0.017948546, 0.15287702) * go_0(-1.0, 1.0);
result += mat4(-0.05340533, 0.23797482, 0.20351392, -0.05333351, -0.12181174, -0.23363493, -0.20696607, 0.109941036, -0.11519453, 0.13842066, -0.10687832, 0.29040006, 0.022218632, 0.031238724, 0.2685182, 0.15300068) * go_0(0.0, -1.0);
result += mat4(0.22985318, -0.3103802, -0.22916415, 0.25238806, -0.11690287, -0.1947488, 0.118020535, 0.07814263, -0.06335474, -0.007870727, 0.076106325, 0.094677486, -0.16776285, -0.006570437, -0.29589584, 0.41413507) * go_0(0.0, 0.0);
result += mat4(0.43607962, -0.36456433, -0.123776875, -0.16634953, -0.091190875, 0.13035081, 0.28627968, 0.27249968, 0.12356344, -0.008616177, 0.09599816, -0.006144557, -0.23490307, 0.3013123, 0.14153156, 0.21837278) * go_0(0.0, 1.0);
result += mat4(0.060364585, 0.37860224, 0.039182413, -0.22805426, -0.089910224, -0.06817697, -0.2684275, -0.12528503, 0.036934495, -0.07826616, 0.06559976, -0.08253646, 0.13489649, 0.06237663, 0.126376, 0.21194184) * go_0(1.0, -1.0);
result += mat4(-0.12534817, 0.21225189, -0.27818045, -0.3070443, -0.006957577, -0.025105853, 0.12100924, -0.06916452, 0.23081483, 0.1802756, -0.18995638, 0.16603014, -0.2904096, -0.25292823, -0.21834068, 0.13719653) * go_0(1.0, 0.0);
result += mat4(0.017209655, 0.10757137, 0.21414296, -0.30885983, 0.10467716, -0.2184891, 0.100061476, -0.1527528, 0.2100472, -0.25768545, -0.22329919, -0.29153427, -0.06983842, -0.103854865, -0.051384352, 0.14629121) * go_0(1.0, 1.0);
result += mat4(0.0059623295, -0.26060802, 0.32115817, 0.021025505, 0.09783085, -0.15865178, 0.1473021, -0.24977303, -0.033508282, 0.17480391, -0.091310136, 0.09870876, 0.10504043, -0.06105686, 0.013493489, -0.11278855) * go_1(-1.0, -1.0);
result += mat4(0.14875248, -0.14859414, 0.19377062, -0.17456068, 0.101288855, -0.1113682, -0.48944646, 0.1018565, -0.037392337, 0.08539691, 0.1751306, -0.15428723, -0.059375558, 0.027663672, 0.051804014, -0.049813222) * go_1(-1.0, 0.0);
result += mat4(0.118846565, -0.19869871, -0.037388258, 0.08456728, -0.11662527, -0.43818352, -0.093285345, 0.038507205, -0.051991668, 0.21008292, 0.10792365, 0.2020924, 0.057021596, 0.09460527, 0.0016551288, -0.0015957063) * go_1(-1.0, 1.0);
result += mat4(0.11062174, -0.2639232, -0.060295466, -0.3217331, -0.050545212, 0.30989558, 0.30906132, 0.030323273, 0.028986752, 0.037429404, 0.20855664, -0.19848943, 0.034687653, -0.09599135, -0.06250494, -0.13215867) * go_1(0.0, -1.0);
result += mat4(-0.010391146, 0.07657845, 0.44491258, 0.0435906, 0.0075931503, 0.42632654, 0.47022533, 0.34737435, -0.15452717, -0.14613411, -0.45231065, 0.12094409, 0.0067911847, 0.057501152, 0.09876979, 0.044946447) * go_1(0.0, 0.0);
result += mat4(-0.15607435, 0.2293058, -0.09520331, 0.012836732, -0.15282455, 0.26437718, -0.1685477, -0.13211122, -0.055801593, -0.016778728, -0.34478986, -0.23228309, 0.12300962, -0.13235827, -0.13987203, -0.16550972) * go_1(0.0, 1.0);
result += mat4(0.13161735, -0.09039346, -0.033475474, -0.23686698, 0.1514885, 0.20977421, 0.031431954, -0.0049226107, 0.090661936, 0.15288061, -0.03316583, 0.09646573, -0.32651708, 0.18825398, -0.15777239, 0.17572704) * go_1(1.0, -1.0);
result += mat4(0.112157226, -0.08712878, 0.23453182, 0.1043877, -0.14686783, 0.28682423, -0.086443506, 0.059457052, -0.31530112, -0.2700583, -0.06028952, -0.070416875, 0.18053482, 0.16653341, 0.25215197, 0.061915852) * go_1(1.0, 0.0);
result += mat4(-0.20122242, 0.076313145, -0.0988483, 0.094337784, -0.35436687, 0.3762327, -0.07809558, 0.3055848, 0.10425242, -0.17087407, 0.030301496, -0.13911743, 0.01630275, 0.24247427, -0.006474477, 0.03842641) * go_1(1.0, 1.0);
result += vec4(-0.008952847, -0.0058945753, -0.08097229, 0.020968592);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_2_tf
//!SAVE conv2d_3_tf
//!WIDTH conv2d_2_tf.w
//!HEIGHT conv2d_2_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.2237721, -0.0064096362, -0.31808427, 0.73477733, 0.015353088, 0.23983319, 0.14967978, -0.34920225, -0.07456269, 0.093151815, -0.14331086, -0.24586205, -0.14183366, 0.06401045, -0.22044073, 0.29932275) * go_0(-1.0, -1.0);
result += mat4(-0.07968509, -0.3349146, 0.16529128, 0.08443499, 0.4095855, -0.17120704, 0.17425705, 0.15298946, 0.2981273, 0.2212369, 0.10392389, -0.28775454, -0.065247655, -0.15255849, 0.13094437, 0.18685219) * go_0(-1.0, 0.0);
result += mat4(0.015706737, -0.17755036, 0.2622526, 0.112057306, -0.15876788, -0.38466996, -0.33700845, -0.031711742, -0.023320962, -0.3145249, -0.21223734, -0.1314596, -0.1888095, -0.046370104, 0.09000896, -0.0046378844) * go_0(-1.0, 1.0);
result += mat4(-0.31127506, 0.31304324, -0.03965752, 0.03649018, -0.029851055, 0.05801377, 0.00040150844, -0.04422069, 0.18019931, 0.14415511, -0.09845236, 0.21895434, -0.013932474, -0.046454947, -0.3403935, -0.006705289) * go_0(0.0, -1.0);
result += mat4(-0.34878647, -0.5129283, 0.060250953, -0.16354133, 0.20644619, 0.08732273, -0.24118888, 0.24455065, 0.24449423, 0.44103387, 0.22455928, 0.25738943, -0.26914698, -0.21309987, 0.08386486, 0.021484816) * go_0(0.0, 0.0);
result += mat4(-0.057454903, -0.4121922, 0.022661546, 0.37178272, 0.03331408, 0.05044008, 0.04324371, 0.20727943, 0.2432641, 0.076906696, -0.20858039, 0.012439015, -0.19335061, 0.09217451, 0.1968369, -0.19435833) * go_0(0.0, 1.0);
result += mat4(-0.16960496, 0.24616167, 0.37977478, 0.14324574, -0.011531225, -0.11312143, -0.18141079, -0.23843932, 0.0086012175, -0.3564491, -0.12639481, 0.009799298, -0.29120612, 0.23756824, 0.18035695, -0.087133996) * go_0(1.0, -1.0);
result += mat4(-0.10081239, 0.29191494, 0.10434693, 0.08970636, 0.008997759, 0.104756236, 0.039641086, 0.02323888, -0.11627765, 0.023693223, -0.30801758, -0.120208986, 0.05086147, 0.18498175, 0.15595439, -0.09877306) * go_0(1.0, 0.0);
result += mat4(0.101321675, -0.2929976, 0.38810417, 0.5605376, -0.04073937, 0.030110704, -0.18147062, -0.09833952, 0.01927733, 0.15335669, -0.15384074, -0.110595055, -0.054297395, -0.077522054, 0.07918369, -0.068480626) * go_0(1.0, 1.0);
result += mat4(0.23263514, -0.11719232, 0.2903209, -0.007503795, -0.020222448, -0.17790157, -0.15600762, -0.08741775, 0.12529704, 0.25548857, -0.04585447, -0.10255033, 0.18350503, -0.29593533, 0.0868933, 0.027004737) * go_1(-1.0, -1.0);
result += mat4(-0.14958654, -0.006238835, -0.2928948, 0.1988557, -0.17057803, 0.12524141, 0.13978264, -0.019280292, 0.05967142, -0.07790818, -0.5893818, -0.022845713, -0.08596779, 0.07875358, -0.03316667, -0.4369282) * go_1(-1.0, 0.0);
result += mat4(0.19195688, -0.060883682, -0.25897828, 0.07063324, 0.090833396, 0.003422883, 0.109534174, 0.031180874, -0.05017118, 0.022862168, -0.270113, -0.057831235, 0.53920543, -0.10252776, -0.091807485, 0.004294343) * go_1(-1.0, 1.0);
result += mat4(-0.18494242, -0.119284816, 0.3821897, 0.07777979, 0.15568028, -0.2854859, -0.22441281, -0.049155876, -0.15292497, 0.21895619, -0.095677756, 0.15210424, 0.001643022, -0.026176987, 0.048463076, -0.4824009) * go_1(0.0, -1.0);
result += mat4(0.007215129, 0.17074333, 0.053930074, -0.027014816, -0.17180431, -0.15163863, -0.0012122132, -0.18934256, -0.08294297, -0.24580221, -0.46552867, -0.27923223, 0.4092668, 0.06288688, -0.1602188, -0.0030876845) * go_1(0.0, 0.0);
result += mat4(0.111870885, 0.03317145, 0.14155298, 0.20328505, -0.05104131, 0.13979794, 0.018966835, -0.07238511, 0.05493792, -0.14975783, -0.10293237, -0.21985306, 0.49054706, 0.18288186, -0.26925826, 0.35845932) * go_1(0.0, 1.0);
result += mat4(0.3747799, -0.096748486, -0.17139742, 0.25289854, -0.17421168, -0.018461818, 0.09747162, 0.01660535, -0.20580359, 0.56189656, 0.17151354, -0.26347768, 0.28350568, -0.21486014, -0.44330928, -0.008981037) * go_1(1.0, -1.0);
result += mat4(0.10169985, -0.18244018, 0.04760736, 0.41017643, -0.09468786, -0.024218475, 0.103733875, -0.22540338, 0.10630112, 0.3677178, -0.104170956, 0.057317447, 0.21764882, 0.0789158, -0.22041337, 0.15065216) * go_1(1.0, 0.0);
result += mat4(0.11633995, -0.008195114, -0.14501533, 0.07168025, 0.058413275, 0.055995367, 0.09362145, -0.13827963, 0.13760869, 0.040319785, 0.038895044, 0.2675253, -0.087339684, 0.1412073, -0.17166458, -0.2312994) * go_1(1.0, 1.0);
result += vec4(-0.059377354, -0.02055341, 0.07234869, -0.015452986);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_3_tf
//!SAVE conv2d_4_tf
//!WIDTH conv2d_3_tf.w
//!HEIGHT conv2d_3_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.29012984, -0.13150147, 0.31015614, 0.05992291, -0.050289866, 0.14845313, -0.09608898, 0.27913308, 0.060307387, -0.04160452, 0.035932682, -0.08137563, -0.07999419, 0.11818284, -0.27512288, 0.21948813) * go_0(-1.0, -1.0);
result += mat4(0.12916058, -0.21759962, -0.33868533, 0.021636661, 0.053470243, 0.1412425, 0.043395396, -0.26751056, -0.01689101, -0.2623835, 0.010809152, 0.062962815, -0.20692012, -0.1677863, -0.23313859, -0.17402615) * go_0(-1.0, 0.0);
result += mat4(-0.08204112, -0.23672083, -0.0064437394, -0.13200696, -0.056692924, -0.02708657, 0.12536962, 0.004428919, 0.14137582, 0.15404348, -0.105753876, 0.047957454, 0.15734316, 0.16562423, -0.010160829, -0.06602983) * go_0(-1.0, 1.0);
result += mat4(0.025653997, -0.10877775, -0.31258908, 0.18841636, -0.36005193, 0.1816357, -0.34537643, -0.0741087, 0.4663994, 0.0065186517, 0.08109033, 0.2976773, -0.35774228, -0.041366056, -0.37852773, 0.050565656) * go_0(0.0, -1.0);
result += mat4(0.04392313, 0.11316681, -0.14421389, 0.17985669, -0.1651274, -0.5656209, -0.124100484, 0.42774054, -0.1153939, 0.16829851, 0.2025612, 0.054007456, -0.06868256, -0.56935954, -0.12227961, 0.17688861) * go_0(0.0, 0.0);
result += mat4(0.34041, 0.499, 0.15234196, 0.21353458, -0.2732667, -0.049950935, 0.03550811, -0.21051687, 0.2609023, 0.016438454, -0.29874632, 0.37994128, 0.049288407, -0.31126305, 0.029235512, -0.012256015) * go_0(0.0, 1.0);
result += mat4(-0.0046853204, 0.15391374, -0.040689662, 0.20186873, -0.08137621, 0.35905558, 0.23733845, 0.21794793, -0.066420384, 0.029600656, -0.31421044, -0.050773863, -0.06260773, 0.04634221, -0.10948491, -0.045498934) * go_0(1.0, -1.0);
result += mat4(-0.082953, -0.025837064, -0.09928303, -0.14300232, 0.275064, 0.07793617, 0.22240888, 0.06637834, -0.4382666, -0.2932182, -0.27243167, -0.14221182, 0.5695728, 0.20719238, 0.5575927, 0.40816882) * go_0(1.0, 0.0);
result += mat4(-0.18510929, -0.15052167, 0.25277212, 0.06804461, 0.016387, 0.20310035, 0.2903229, -0.0615877, -0.28987274, -0.11942605, 0.013498961, 0.3184152, 0.29543474, -0.042830903, -0.018111207, -0.13263674) * go_0(1.0, 1.0);
result += mat4(0.25749087, 0.0053866603, -0.09391162, -0.06129529, -0.094091184, -0.07419633, 0.0013858611, 0.012000353, -0.062903, -0.0204224, -0.12113313, 0.017942557, -0.073379934, 0.052201986, 0.35864577, 0.023564404) * go_1(-1.0, -1.0);
result += mat4(0.100115694, 0.19451359, 0.23252094, 0.19506809, -0.12470779, 0.0027281935, -0.17488572, -0.018721964, -0.15159339, 0.18457152, 0.057712987, -0.08191495, 0.19735703, 0.07326743, -0.28563106, 0.01642815) * go_1(-1.0, 0.0);
result += mat4(0.068062514, 0.28356665, 0.07377898, 0.42776972, 0.28725025, -0.13045293, -0.17525704, -0.05885591, -0.16676305, -0.2555945, -0.10078422, -0.053032875, 0.084470876, 0.06460686, 0.13824362, -0.05231353) * go_1(-1.0, 1.0);
result += mat4(0.22637829, -0.028969254, 0.1968254, -0.13331996, 0.038017053, -0.008854481, -0.2031639, 0.09237089, -0.3821112, 0.1108527, -0.11029933, -0.24542028, 0.22416145, -0.031492114, -0.19144306, -0.0996271) * go_1(0.0, -1.0);
result += mat4(0.10776744, 0.16363445, 0.14656505, -0.3737814, -0.06642015, 0.5616549, -0.008412252, -0.37266847, 0.12506576, -0.15329036, 0.037538245, -0.10810259, 0.01706349, 0.1813702, 0.035651788, -0.012786579) * go_1(0.0, 0.0);
result += mat4(-0.4023338, -0.2098614, -0.18285121, -0.02727653, 0.26107362, 0.041306913, -0.036515504, -0.045217298, -0.39958602, -0.21229339, -0.021053292, -0.13427502, 0.36178818, 0.20934913, 0.1500852, 0.2634554) * go_1(0.0, 1.0);
result += mat4(0.07794611, -0.25937587, -0.06822529, -0.056336135, 0.094220124, 0.21588847, -0.0455218, -0.10968329, -0.08068449, -0.31366697, 0.07799637, 0.24252681, 0.23963861, 0.13715535, 0.010329345, 0.09094301) * go_1(1.0, -1.0);
result += mat4(-0.20975718, -0.12550138, 0.14453574, -0.0020878632, -0.07153068, 0.3249998, -0.056577377, 0.18166828, 0.37204072, 0.17018336, 0.3752895, 0.32178587, 0.2571982, -0.27258632, -0.25971004, -0.40536007) * go_1(1.0, 0.0);
result += mat4(-0.3243907, -0.06300621, -0.09398436, -0.19549188, 0.14906861, 0.061537784, -0.055284478, 0.11281728, 0.12964857, 0.09979093, -0.1810159, -0.4104283, 0.05807971, -0.056371246, 0.08072554, 0.18479007) * go_1(1.0, 1.0);
result += vec4(-0.048888464, -0.0561434, 0.030690912, -0.030496685);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_4_tf
//!SAVE conv2d_5_tf
//!WIDTH conv2d_4_tf.w
//!HEIGHT conv2d_4_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.15332128, 0.027258258, 0.14900503, -0.15982795, 0.17021236, -0.51046044, -0.15287271, -0.058167327, 0.51826185, -0.34817994, 0.004513167, 0.05395769, 0.1990321, -0.049979225, 0.11391989, -0.16062729) * go_0(-1.0, -1.0);
result += mat4(0.033682905, 0.019728886, 0.19931756, 0.17381927, 0.2585768, -0.2124572, -0.014632459, 0.39779893, -0.1146207, -0.2396625, 0.08960277, 0.38345298, 0.25497693, 0.11692859, -0.14207517, 0.12667973) * go_0(-1.0, 0.0);
result += mat4(-0.14911255, 0.08910706, 0.16136818, 0.03914566, 0.24204038, -0.03607149, -0.4571109, 0.10802461, -0.0021356856, 0.00885878, 0.22297303, 0.2367231, 0.045177583, 0.11120606, -0.009971904, -0.059262395) * go_0(-1.0, 1.0);
result += mat4(0.24565999, -0.2261384, 0.47373205, 0.024613412, -0.10923052, 0.039027315, -0.42707404, -0.3783373, 0.3544573, -0.5468578, -0.27599156, -0.09455918, 0.18760219, -0.19082001, 0.030565469, 0.20589156) * go_0(0.0, -1.0);
result += mat4(0.1973198, -0.03433863, 0.059960485, 0.045642868, 0.1819595, -0.14460869, 0.1286175, 0.2067575, -0.042632047, -0.11842967, -0.11224446, -0.18764776, -0.19563004, 0.027425969, 0.24056377, 0.5949649) * go_0(0.0, 0.0);
result += mat4(0.055027682, 0.16331595, -0.2608588, 0.12545955, 0.4588985, 0.03642909, 0.22187738, 0.45190734, -0.001210133, -0.057651415, -0.061199043, 0.11935476, -0.049561135, 0.27509886, 0.13778673, -0.124914035) * go_0(0.0, 1.0);
result += mat4(-0.02257459, 0.27705106, 0.044165276, -0.26521233, 0.05982374, -0.2824302, 0.3171142, 0.08430561, -0.10155528, 0.16182268, -0.09183147, -0.19447176, 0.3295707, -0.50616395, -0.036964044, 0.23166709) * go_0(1.0, -1.0);
result += mat4(-0.0232342, 0.07299799, -0.18038079, -0.13672702, -0.108305976, 0.15024792, -0.19531927, 0.0870979, -0.26488534, 0.19481428, 0.10737945, -0.14573483, -0.33094683, 0.24155116, -0.09850332, 0.2797003) * go_0(1.0, 0.0);
result += mat4(-0.24089853, 0.19506595, 0.4799156, -0.058313113, 0.36212957, -0.44844806, 0.23864488, 0.15477742, -0.07795971, -0.0033861927, -0.11216164, 0.033454563, -0.25893036, 0.23793478, -0.15769425, -0.00033481256) * go_0(1.0, 1.0);
result += mat4(0.05772507, -0.1640253, -0.13499664, -0.20460358, -0.024399966, 0.14966168, -0.090857334, -0.039677754, 0.00036956606, -0.24236615, -0.053542696, -0.0049544116, 0.026651502, 0.39019194, -0.2742246, -0.061242323) * go_1(-1.0, -1.0);
result += mat4(-0.016323274, -0.036179908, 0.029965919, 0.11151491, -0.00016685206, -0.29573023, 0.17996423, -0.20145437, 0.1324275, -0.18442132, -0.24618152, 0.061780427, -0.02770517, 0.28452995, 0.39804098, -0.1174389) * go_1(-1.0, 0.0);
result += mat4(-0.025068847, -0.053328387, -0.27053785, 0.26866457, -0.09866204, 0.057677213, 0.01850112, -0.18014707, -0.13319959, -0.14411181, -0.26355243, -0.022209354, -0.05062645, -0.036771543, 0.13294417, -0.18458557) * go_1(-1.0, 1.0);
result += mat4(-0.046194963, 0.038230438, -0.08993043, -0.07236354, 0.11031123, -0.16504908, -0.09517036, -0.16459833, -0.5279925, 0.12686682, -0.05726125, 0.055361677, 0.31593755, 0.027328093, 0.001839602, 0.30581662) * go_1(0.0, -1.0);
result += mat4(0.08608678, 0.03168437, 0.007713377, -0.26140293, -0.1268983, 0.13395861, -0.069848835, -0.24080403, 0.018839337, -0.049821075, -0.21461345, -0.14168301, -0.0872339, 0.47096667, 0.022512507, 0.14860632) * go_1(0.0, 0.0);
result += mat4(0.06293673, 0.22462969, 0.045494985, 0.021673543, 0.18227446, -0.2956555, 0.08010543, -0.01919729, -0.012190269, 0.241983, -0.046537094, -0.40094566, -0.3853647, 0.1081711, -0.16926058, 0.16138376) * go_1(0.0, 1.0);
result += mat4(-0.14854589, -0.17625804, -0.10849075, 0.221543, 0.099971965, 0.13901573, 0.29464146, 0.020068526, 0.054358527, -0.10351705, -0.0062914286, 0.24127026, -0.16914125, 0.12729423, -0.18377453, -0.6452375) * go_1(1.0, -1.0);
result += mat4(0.12603393, -0.10986093, 0.2314103, 0.16915044, -0.13619255, -0.09349073, 0.20594226, -0.34507084, 0.19077192, 0.052500796, 0.07185645, 0.029082738, -0.015576321, 0.08254907, -0.5501743, -0.38495848) * go_1(1.0, 0.0);
result += mat4(0.09300796, -0.079218306, 0.46825135, -0.08735625, 0.06321122, 0.16234867, 0.042932414, -0.013057422, 0.09697148, 0.23457524, 0.19417483, -0.16804664, 0.18379296, 0.17770062, -0.050235, -0.059676602) * go_1(1.0, 1.0);
result += vec4(0.011169491, 0.032399546, 0.138099, 0.023857072);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_5_tf
//!SAVE conv2d_6_tf
//!WIDTH conv2d_5_tf.w
//!HEIGHT conv2d_5_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.22753362, -0.08612073, 0.33140692, 0.08699529, -0.18788953, -0.056579117, -0.12905197, -0.06694621, 0.054559365, 0.15031597, -0.13430363, 0.021646025, 0.14884405, -0.0694291, 0.26149413, 0.11270503) * go_0(-1.0, -1.0);
result += mat4(0.17876762, -0.09637848, 0.11285323, 0.2004893, 0.1317187, -0.036162686, 0.17958368, -0.069625, 0.28760737, -0.12505141, 0.12760694, 0.047717955, -0.16811855, -0.16340709, 0.13278298, -0.08403954) * go_0(-1.0, 0.0);
result += mat4(-0.21917523, 0.079711854, -0.28642535, 0.2822416, 0.03001489, -0.014772918, -0.3487396, 0.10597145, -0.013841082, 0.17034237, 0.10810282, -0.08089695, -0.22184245, -0.59067357, 0.44113398, 0.13045649) * go_0(-1.0, 1.0);
result += mat4(-0.29906932, 0.013923749, 0.2031124, -0.11846688, -0.13953634, 0.08003455, -0.10164494, -0.21218559, 0.10563715, 0.31033117, -0.075903505, 0.047310907, -0.37824214, -0.14506383, 0.11866701, -0.21384487) * go_0(0.0, -1.0);
result += mat4(-0.1353849, 0.19258606, 0.063908584, -0.2043788, 0.27244982, 0.1665306, -0.29357895, -0.22441709, 0.18514316, -0.17840464, 0.20986097, 0.14351055, -0.057732623, 0.42166704, -0.23182064, -0.4957248) * go_0(0.0, 0.0);
result += mat4(-0.34830126, 0.109066755, -0.28285867, -0.048280068, -0.12290918, 0.04291651, -0.047484186, -0.03702595, 0.23047262, 0.09398974, 0.022467108, 0.08271034, 0.3066665, -0.54077, 0.057771873, 0.23194093) * go_0(0.0, 1.0);
result += mat4(-0.17731948, -0.3175927, 0.1452728, 0.09396786, -0.16433562, -0.01833653, -0.22345604, -0.04161193, -0.14827462, 0.18544114, -0.15544125, -0.06179007, 0.16989979, -0.20985202, 0.16391534, -0.09447268) * go_0(1.0, -1.0);
result += mat4(-0.053878862, -0.21034616, 0.023831524, 0.19772215, 0.31647214, 0.0126534775, -0.19130844, -0.049282108, -0.21446131, 0.067189045, 0.09117449, -0.25548774, 0.12109098, 0.22009392, -0.3924665, -0.13340388) * go_0(1.0, 0.0);
result += mat4(-0.16096684, -0.18495405, 0.10410178, 0.0015673033, -0.00183498, -0.044303037, -0.062745355, -0.090802394, 0.043269135, 0.06924481, -0.21367405, -0.14619029, 0.11555763, -0.20292862, 0.5799557, 0.14739846) * go_0(1.0, 1.0);
result += mat4(-0.21030277, -0.09578802, 0.013482288, -0.21484336, 0.12995781, 0.40431052, -0.3347856, -0.18183486, 0.15550353, -0.04402301, 0.4603779, 0.14874357, -0.07694621, -0.053523075, -0.19607326, -0.10850742) * go_1(-1.0, -1.0);
result += mat4(-0.2347211, 0.2697403, -0.0634794, -0.17925987, 0.17231455, 0.24999185, -0.5208536, -0.10491828, -0.233575, 0.52950364, 0.0038063182, -0.1380038, 0.022935199, 0.19369157, 0.14586553, 0.1938704) * go_1(-1.0, 0.0);
result += mat4(-0.10245223, 0.34150192, 0.25862157, -0.20165509, 0.5597771, 0.114510864, -0.122526556, -0.04010975, 0.1704679, -0.23335956, -0.16771887, -0.03783455, -0.056995615, 0.24153493, -0.08082429, -0.24210933) * go_1(-1.0, 1.0);
result += mat4(-0.103466526, 0.15278348, -0.30526164, -0.080755696, 0.103505425, 0.15862796, 0.14696524, -0.008358076, -0.09180311, -0.12505089, 0.28052542, -0.13551563, 0.07528779, -0.09636086, -0.10369617, 0.23656134) * go_1(0.0, -1.0);
result += mat4(-0.25752836, 0.099439755, -0.30716348, 0.035077725, 0.023509016, 0.23106368, 0.05277125, 0.34910464, 0.088015385, 0.26995596, 0.1390645, -0.40671825, 0.18096298, -0.100688554, 0.5492049, 0.2482101) * go_1(0.0, 0.0);
result += mat4(0.41411775, -0.107200556, -0.13813478, 0.13768874, 0.27137747, 0.06313619, -0.08522967, 0.03218302, -0.03166121, -0.3415683, -0.52242, -0.1741813, -0.36956537, 0.179129, -0.09742935, -0.11696616) * go_1(0.0, 1.0);
result += mat4(-0.07975504, 0.17964838, 0.37122533, 0.16064765, 0.14309953, 0.29473078, 0.0926391, -0.22333665, 0.34612748, -0.3387473, 0.0077308523, -0.07239449, 0.18522519, -0.21297298, 0.11493978, 0.16117814) * go_1(1.0, -1.0);
result += mat4(-0.17402779, 0.10023144, 0.11712206, 0.031971734, 0.18713303, 0.08736295, 0.013007052, -0.06943139, -0.20102951, -0.010721135, -0.2562522, 0.34877458, -0.13732676, -0.40258047, 0.25824392, 0.15720639) * go_1(1.0, 0.0);
result += mat4(0.044494305, 0.3296108, 0.0017603852, 0.09362289, 0.38839245, 0.40015858, -0.13395199, -0.044521853, -0.56266373, 0.251378, 0.5005789, -0.13106057, -0.18491416, -0.046887, 0.067797676, -0.14694957) * go_1(1.0, 1.0);
result += vec4(0.013687534, -0.08185164, -0.04755438, 0.290178);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(M)-Conv-3x1x1x56
//!HOOK MAIN
//!BIND MAIN
//!BIND conv2d_tf
//!BIND conv2d_1_tf
//!BIND conv2d_2_tf
//!BIND conv2d_3_tf
//!BIND conv2d_4_tf
//!BIND conv2d_5_tf
//!BIND conv2d_6_tf
//!SAVE MAIN
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
#define g_0 (max((conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_1 (max(-(conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_2 (max((conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_3 (max(-(conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_4 (max((conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_5 (max(-(conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_6 (max((conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_7 (max(-(conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_8 (max((conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_9 (max(-(conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_10 (max((conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_11 (max(-(conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_12 (max((conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_13 (max(-(conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
vec4 hook() {
vec4 result = mat4(-0.08837163, -0.065234736, -0.034704313, 0.0, 0.021405501, 0.013663729, 0.019249594, 0.0, 0.05328863, 0.03580334, 0.046457592, 0.0, -0.12216048, 0.022547891, 0.016400825, 0.0) * g_0;
result += mat4(0.061996464, 0.05631466, 0.06808407, 0.0, -0.005013109, -0.0044589997, -0.032367796, 0.0, 0.016481603, 0.13721058, 0.14924648, 0.0, 0.020035887, -0.07250003, -0.08034037, 0.0) * g_1;
result += mat4(0.24078514, 0.081361525, 0.053420708, 0.0, -0.009353794, -0.051077116, -0.058007747, 0.0, -0.14071098, 0.01035966, 0.005308949, 0.0, -0.1489842, -0.06711817, -0.05552926, 0.0) * g_2;
result += mat4(-0.13002375, 0.012733757, 0.017821986, 0.0, 0.17767483, 0.20204604, 0.1751779, 0.0, 0.12804912, 0.07381453, 0.05655911, 0.0, 0.17044514, 0.07301451, 0.06523978, 0.0) * g_3;
result += mat4(-0.1170986, -0.05130371, -0.027939914, 0.0, -0.16645707, -0.121526904, -0.09471366, 0.0, -0.04143118, 0.026693767, 0.034615446, 0.0, -0.084318705, -0.064990036, -0.054324172, 0.0) * g_4;
result += mat4(0.12094524, 0.09518409, 0.07387219, 0.0, 0.062216382, 0.053228356, 0.031372335, 0.0, 0.072797105, 0.026258165, 0.009804673, 0.0, 0.120719045, 0.073281154, 0.056623302, 0.0) * g_5;
result += mat4(-0.11141495, -0.11566289, -0.10398725, 0.0, -0.0651895, -0.06820691, -0.054204144, 0.0, -0.032746475, -0.008849683, -0.007610222, 0.0, -0.024655705, -0.048778858, -0.041144755, 0.0) * g_6;
result += mat4(0.058090195, 0.07538767, 0.059722915, 0.0, 0.044788487, 0.04212742, 0.027502589, 0.0, 0.04892866, 0.015416752, 0.008312418, 0.0, -0.011864114, -0.0074752793, -0.0060824654, 0.0) * g_7;
result += mat4(0.043446552, 0.061971307, 0.05758086, 0.0, -0.06379154, -0.053758245, -0.047204215, 0.0, 0.016307736, 0.03423424, 0.030179083, 0.0, 0.041445345, 0.03843772, 0.033059113, 0.0) * g_8;
result += mat4(-0.003803544, 0.0008906116, -0.00059585314, 0.0, 0.102071285, 0.11485224, 0.10007254, 0.0, -0.074306004, -0.08803551, -0.07972321, 0.0, -0.030704215, -0.021514274, -0.009049376, 0.0) * g_9;
result += mat4(0.0066058086, 0.0011408008, 0.0016199006, 0.0, -0.03916473, -0.042929266, -0.04018418, 0.0, -0.03153446, -0.039413508, -0.034767237, 0.0, 0.113516055, 0.12577052, 0.113335624, 0.0) * g_10;
result += mat4(0.02655948, 0.041905303, 0.03861737, 0.0, 0.048471425, 0.049788587, 0.050447535, 0.0, 0.12092813, 0.13564217, 0.12613249, 0.0, -0.0023508538, 0.0012828974, 0.0028730957, 0.0) * g_11;
result += mat4(0.0084758485, 0.008800083, 0.008206044, 0.0, -0.056123603, -0.06610845, -0.060320783, 0.0, -0.081793964, -0.101638645, -0.096699014, 0.0, -0.04402356, -0.04177539, -0.03829645, 0.0) * g_12;
result += mat4(0.10676299, 0.118409514, 0.10618478, 0.0, -0.05880252, -0.06488367, -0.06432695, 0.0, 0.019221924, 0.017602798, 0.017413978, 0.0, -0.07512528, -0.080483615, -0.066218294, 0.0) * g_13;
result += vec4(-0.010478934, -0.008364784, -0.010246552, 0.0);
return result + MAIN_tex(MAIN_pos);
}
@@ -0,0 +1,137 @@
// MIT License
// Copyright (c) 2019-2021 bloc97
// All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//!DESC Anime4K-v4.0-Restore-CNN-(S)-Conv-4x3x3x3
//!HOOK MAIN
//!BIND MAIN
//!SAVE conv2d_tf
//!WIDTH MAIN.w
//!HEIGHT MAIN.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (MAIN_texOff(vec2(x_off, y_off)))
vec4 hook() {
vec4 result = mat4(-0.19288683, -0.21397883, 0.111997396, -0.04791413, -0.26682988, -0.06144587, -0.03601853, -0.16693151, 0.038494494, -0.16651472, 0.147657, -0.083003886, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, -1.0);
result += mat4(-0.14286195, 0.08746566, -0.40107322, 0.12390977, -0.33392772, -0.18703035, -0.21326795, 0.04780781, -0.15155545, -0.0010025925, -0.1554875, -0.10676251, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 0.0);
result += mat4(0.28095165, 0.022872915, -0.21342312, -0.29982176, 0.025937587, -0.055012174, -0.33779636, 0.0015666655, 0.076416336, 0.06656033, -0.1557806, 0.1078894, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 1.0);
result += mat4(-0.31584853, 0.07527119, 0.30713862, -0.34014285, -0.50103146, -0.07217874, 0.512807, -0.09597398, -0.32097813, -0.051580857, -0.022466356, 0.01148551, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, -1.0);
result += mat4(-0.026032459, -0.04193211, 0.37703893, -0.031916667, -0.27421117, 1.0906446, -0.049654085, -0.19814016, 0.07819544, 0.06003738, 0.1405805, -0.0064135445, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 0.0);
result += mat4(0.041450135, 0.11319654, -0.23237701, 0.08443178, 0.53344345, 0.30857387, -0.057264958, -0.1575803, 0.2325609, -0.027797326, -0.04544767, -0.18720597, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 1.0);
result += mat4(0.2531829, -0.074966915, -0.27800754, -0.3146097, 0.20126024, -0.5380133, -0.15082566, -0.19021043, 0.29951036, 0.17123336, -0.01681872, -0.12574998, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, -1.0);
result += mat4(0.25203633, 0.19882993, 0.14906439, 0.13593598, 0.40712556, 0.084902965, 0.42969635, 0.2961132, -0.057267334, -0.030388135, 8.8084314e-05, 0.0210724, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 0.0);
result += mat4(-0.13459359, -0.12199573, 0.12591946, 0.24736497, 0.2033463, -0.09388599, -0.094370656, 0.1071285, -0.18479438, -0.066625565, 0.08279283, 0.20130983, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 1.0);
result += vec4(-0.011108127, -0.07481861, 0.07640154, 0.4964964);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(S)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_tf
//!SAVE conv2d_1_tf
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.056432575, 0.0028165397, -0.026325442, -0.14802271, 0.16885762, -0.062179096, -0.2332292, 0.17513658, -0.08011296, 0.02947316, 0.014771492, -0.17946689, 0.026012989, -0.09823925, 0.036625937, -0.06924322) * go_0(-1.0, -1.0);
result += mat4(-0.13571467, 0.09831142, 0.12911566, 0.06305893, -0.07188695, -0.20161287, 0.3858435, -0.21069056, -0.12294444, -0.1404628, -0.022659872, 0.23008968, 0.10969853, 0.17640765, 0.39796907, 0.20413099) * go_0(-1.0, 0.0);
result += mat4(-0.0061665224, 0.055102807, -0.0059629944, -0.021429887, 0.061626043, 0.16898955, -0.21215646, 0.16510476, 0.2238265, 0.19429931, 0.09874656, 0.06828208, -0.122404456, -0.00026717107, -0.28203064, -0.29979932) * go_0(-1.0, 1.0);
result += mat4(-0.22735378, 0.14538136, 0.11549746, 0.194148, -0.09841722, -0.0661309, 0.348576, -0.017375294, -0.044078812, 0.1298332, 0.04793373, -0.30687734, 0.08353025, 0.083519086, 0.10766399, 0.31796935) * go_0(0.0, -1.0);
result += mat4(0.048365135, -0.17566709, -0.33212858, -0.052667376, -0.26443407, -0.010216014, 0.1573303, 0.05725314, 0.08140953, -0.09664591, 0.076109104, -0.026773714, 0.07732627, 0.10188082, -0.28266954, -0.16230233) * go_0(0.0, 0.0);
result += mat4(0.29931107, 0.117944, -0.10414009, 0.12795551, 0.12576093, 0.17082554, -0.15803693, 0.13430743, -0.025801308, -0.10797019, 0.0721032, 0.2825884, -0.11025257, 0.12798019, 0.081827976, -0.050441865) * go_0(0.0, 1.0);
result += mat4(-0.11827391, 0.08306765, -0.3430314, 0.07898041, -0.023839617, -0.019507334, 0.23176382, -0.40992323, 0.09411734, 0.38415068, -0.25845516, -0.29984522, 0.1470966, -0.0684779, -0.07071314, -0.026773235) * go_0(1.0, -1.0);
result += mat4(0.19091596, 0.082110435, -0.5266589, -0.1744098, -0.015838385, -0.046316292, 0.023171103, -0.03731331, 0.2642396, 0.31824252, -0.041754793, -0.09525519, -0.14696182, 0.052168854, 0.039857205, -0.027555354) * go_0(1.0, 0.0);
result += mat4(0.15207373, 0.09845733, 0.0142631065, 0.096375965, 0.06089903, 0.17902578, -0.42391995, 0.22475442, 0.016356342, -0.06277531, -0.12173141, -0.18635495, -0.0013459618, 0.15725887, 0.019310836, 0.20293565) * go_0(1.0, 1.0);
result += mat4(-0.18395247, 0.30672902, 0.09034339, 0.1821889, -0.0419004, -0.2169228, -0.14052129, 0.11006559, 0.1709272, 0.51062274, 0.13758625, -0.2242552, -0.030382963, 0.3357568, -0.26491287, 0.02501938) * go_1(-1.0, -1.0);
result += mat4(0.040511727, 0.12523083, -0.27318433, 0.08388512, 0.25354835, 0.3404216, -0.2632471, -0.17784123, 0.2732347, 0.4468553, 0.084667034, -0.1856242, 0.034099877, -0.00954992, -0.32751867, -0.062207516) * go_1(-1.0, 0.0);
result += mat4(0.17564747, 0.11645554, -0.16362113, 0.105654195, -0.2762563, -0.1413764, 0.23264363, -0.14000498, 0.095402054, 0.0715738, -0.19346157, -0.028285999, 0.009799127, 0.04059529, 0.19688335, 0.1282381) * go_1(-1.0, 1.0);
result += mat4(0.23575781, -0.11446148, -0.20504695, 0.035568226, 0.36890212, -0.85968876, -0.18545328, 0.33796397, -0.30916876, -0.10445518, -0.3046253, 0.33271998, -0.06263589, -0.2160114, -0.16383372, -0.31173357) * go_1(0.0, -1.0);
result += mat4(0.20469664, 0.4039374, -0.070057206, 0.030353077, 0.39843914, -0.15490077, -0.24476516, 0.38238233, -0.21809858, 0.23496576, -0.051794037, 0.033664484, -0.14411364, -0.2515329, 0.124655396, -0.05818785) * go_1(0.0, 0.0);
result += mat4(-0.09065731, -0.16787091, 0.013269188, 0.23687351, -0.41504318, -0.048163068, 0.31760025, -0.33648986, 0.29752317, 0.2926866, 0.14408836, -0.33382463, -0.15873958, -0.121961035, 0.11797893, 0.09000567) * go_1(0.0, 1.0);
result += mat4(0.13356976, 0.013763947, 0.012169505, -0.109594524, 0.03417223, 0.7031121, 0.65146804, 0.5250268, -0.50132495, -0.419648, 0.2940041, 0.83051753, -0.17595838, 0.1633008, -0.018587278, 0.079596795) * go_1(1.0, -1.0);
result += mat4(0.07570128, -0.1581438, 0.03904949, 0.14890033, -0.054611947, 0.17469402, -0.44252598, 0.036181703, -0.4981031, -0.37507218, -0.18466389, 0.2645845, 0.25189674, -0.025896115, 0.034307647, -0.020462232) * go_1(1.0, 0.0);
result += mat4(-0.11645865, 0.02296537, 0.040909223, 0.015069485, 0.062284566, -0.22526766, 0.09241534, -0.32623053, 0.18208642, 0.3954284, 0.2884468, -0.25137675, -0.037232924, -0.10185309, -0.17956531, 0.018966453) * go_1(1.0, 1.0);
result += vec4(-0.16371979, -0.024620198, -0.035754893, 0.04176776);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(S)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_1_tf
//!SAVE conv2d_2_tf
//!WIDTH conv2d_1_tf.w
//!HEIGHT conv2d_1_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.01921286, -0.26684764, -0.12663573, 0.31641877, -0.25313398, 0.12264074, 0.58750325, -0.14084283, 0.5837018, -0.042300556, -0.20435576, -0.009954825, 0.060783498, 0.05540401, 0.2205112, -0.06578902) * go_0(-1.0, -1.0);
result += mat4(-0.21930243, -0.03774968, 0.22615197, 0.18338196, 0.011201461, -0.271034, 0.00573116, -0.12248194, 0.47990513, 0.2982416, -0.1087603, -0.050099242, -0.07620939, -0.07148229, 0.03691984, -0.16796488) * go_0(-1.0, 0.0);
result += mat4(-0.14962853, -0.053769328, 0.02387081, 0.22002189, 0.052237745, -0.26160842, -0.08603077, 0.012542448, 0.08119985, 0.075785555, -0.33437458, -0.43373227, -0.13206963, -0.08759176, -0.03288923, -0.09799959) * go_0(-1.0, 1.0);
result += mat4(-0.1305593, -0.5974288, 0.06058367, 0.08406488, 0.013692483, 0.06646377, 0.16469325, 0.08990975, 0.42217395, -0.11289523, -0.06165009, 0.48556912, -0.15702641, -0.19922857, -0.0035429662, -0.0022089656) * go_0(0.0, -1.0);
result += mat4(-0.1964807, 0.038099788, 0.21587034, 0.039734077, -0.07063389, 0.11604167, -0.24558097, -0.08900199, -0.7684516, -0.1037487, -0.09380674, 0.33144563, -0.16653742, 0.0028585843, -0.33774406, -0.0528696) * go_0(0.0, 0.0);
result += mat4(-0.27298656, -0.05665099, 0.09661685, 0.19780266, 0.1025106, -0.22055034, -0.21218458, -0.040628925, 0.0095010325, 0.13118382, -0.42582452, -0.22197723, 0.21006055, -0.06189587, -0.15285942, -0.09526762) * go_0(0.0, 1.0);
result += mat4(-0.14494462, -0.046788953, 0.065877035, 0.09911713, 0.35096622, 0.16682479, 0.028363144, 0.36037162, 0.29413632, 0.28212717, -0.025364442, -0.3406269, 0.047262143, -0.11892685, -0.008032766, 0.29743317) * go_0(1.0, -1.0);
result += mat4(-0.15191558, -0.36980554, 0.14555687, 0.0043930537, -0.012661432, 0.15737776, -0.115250416, 0.10324491, 0.24491951, -0.15575431, -0.27802598, 0.21959937, 0.18063772, 0.4455559, -0.09693302, 0.33382267) * go_0(1.0, 0.0);
result += mat4(0.2717801, 0.13452889, 0.14105384, 0.16324317, -0.40111846, 0.1154301, -0.0076733204, -0.09697362, 0.44306824, -0.02831414, -0.2153124, -0.12075326, 0.060776163, 0.30347148, -0.0036976219, -0.12070682) * go_0(1.0, 1.0);
result += mat4(-0.39780128, -0.29875937, -0.12952097, 0.080333896, 0.07520163, 0.021689568, -0.23121156, -0.038140096, -0.1593877, 0.017156163, -0.06038025, 0.009244022, -0.13917233, 0.30957314, 0.243109, -0.104947075) * go_1(-1.0, -1.0);
result += mat4(-0.07965157, 0.06776501, -0.13288979, 0.005851189, -0.08768168, -0.03689969, 0.12034646, 0.22441491, 0.14453568, -0.17648841, -0.3378289, -0.018329712, 0.11722939, -0.34161824, 0.08424494, -0.01400687) * go_1(-1.0, 0.0);
result += mat4(0.08153887, 0.07222914, -0.14663404, -0.038526025, -0.07385973, 0.18440577, 0.35890242, 0.17084727, 0.26345527, 0.15280858, -0.007446105, -0.024403179, -0.30336383, -0.22978698, 0.11612946, -0.23614909) * go_1(-1.0, 1.0);
result += mat4(-0.07447396, 0.09023449, -0.13798, -0.086943336, -0.30787337, 0.15087669, 0.14418626, -0.03371195, 0.048989657, -0.13075387, -0.13458036, -0.059836224, 0.06495196, 0.269715, 0.3674355, 0.38956037) * go_1(0.0, -1.0);
result += mat4(0.34981915, -0.048779126, 0.31717536, 0.38080826, -0.20149232, -0.82969636, -0.10167862, 0.6382858, 0.25976858, 0.4370118, -0.04724865, -0.10014156, 0.19380626, -0.080370255, 0.09578106, -0.035166856) * go_1(0.0, 0.0);
result += mat4(-0.026443917, 0.4132611, 0.01822534, 0.12742202, -0.26652107, -0.2996705, 0.30905882, 0.07989903, 0.38249823, 0.21486135, 0.025314959, -0.14717339, -0.13344015, -0.32088286, -0.2833883, -0.30973712) * go_1(0.0, 1.0);
result += mat4(0.021517841, 0.006556378, 0.2025686, -0.12044382, -0.38583103, -0.0027515136, -0.06556736, -0.097090125, 0.04676486, -0.11954886, -0.051612873, 0.07831412, -0.18823163, -0.16542958, 0.04245155, 0.6437998) * go_1(1.0, -1.0);
result += mat4(-0.39475346, -0.2936861, 0.26768062, -0.28151843, 0.21935691, 0.2101108, -0.15455097, 0.19548604, 0.09188909, -0.020147726, 0.103328265, -0.12574542, -0.34167948, 0.07523185, -0.17669058, 0.62446547) * go_1(1.0, 0.0);
result += mat4(-0.37661025, -0.29630858, 0.05451026, 0.1611643, 0.14079669, -0.2170294, -0.038716137, 0.13514164, -0.21235192, -0.07860726, -0.005749412, 0.025625167, -0.13297133, 0.33012658, -0.27434957, -0.18416783) * go_1(1.0, 1.0);
result += vec4(-0.0036821906, -0.050239526, -0.01355402, 0.00048220603);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(S)-Conv-3x3x3x8
//!HOOK MAIN
//!BIND MAIN
//!BIND conv2d_2_tf
//!SAVE MAIN
//!WIDTH conv2d_2_tf.w
//!HEIGHT conv2d_2_tf.h
#define go_0(x_off, y_off) (max((conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.15873, 0.17989138, 0.14648493, 0.0, -0.017379675, -0.017363746, -0.019855022, 0.0, 0.009670625, 0.0070157526, 0.0075994316, 0.0, 0.025388412, 0.027231036, 0.024052646, 0.0) * go_0(-1.0, -1.0);
result += mat4(0.048195973, 0.041760173, 0.037366055, 0.0, -0.115950756, -0.12887983, -0.12535639, 0.0, 0.032125086, 0.03397254, 0.032950625, 0.0, 0.01223746, 0.020822672, 0.0161561, 0.0) * go_0(-1.0, 0.0);
result += mat4(0.0890567, 0.094453335, 0.09014035, 0.0, 0.016081346, 0.017434116, 0.020783134, 0.0, -0.011775135, -0.010094134, -0.018522855, 0.0, 0.072103254, 0.07940666, 0.065876864, 0.0) * go_0(-1.0, 1.0);
result += mat4(-0.04841196, -0.06963968, -0.056574684, 0.0, 0.10912542, 0.11813441, 0.10643838, 0.0, -0.013013885, -0.01562045, -0.013802797, 0.0, 0.037505716, 0.04352026, 0.04645123, 0.0) * go_0(0.0, -1.0);
result += mat4(-0.3472869, -0.36243078, -0.33530185, 0.0, 0.23654196, 0.2305048, 0.22150646, 0.0, -0.045226905, -0.041799217, -0.042511635, 0.0, -0.10267792, -0.1123385, -0.10845448, 0.0) * go_0(0.0, 0.0);
result += mat4(0.011987401, 0.012285043, 0.007813165, 0.0, -0.15911353, -0.17523928, -0.1535267, 0.0, 0.15675929, 0.16531634, 0.15948962, 0.0, -0.09240023, -0.09513292, -0.084187366, 0.0) * go_0(0.0, 1.0);
result += mat4(0.069052905, 0.07278333, 0.0756627, 0.0, -0.012180326, -0.018794727, -0.031050753, 0.0, -0.044663202, -0.04362803, -0.038904265, 0.0, -0.008540197, -0.011201734, -0.01556625, 0.0) * go_0(1.0, -1.0);
result += mat4(-0.08261173, -0.09042543, -0.07589266, 0.0, 0.043515377, 0.045066774, 0.04037769, 0.0, -0.06262993, -0.07469342, -0.058593787, 0.0, 0.026696987, 0.028740842, 0.037405368, 0.0) * go_0(1.0, 0.0);
result += mat4(0.07975598, 0.09597654, 0.08997132, 0.0, -0.07844719, -0.07880916, -0.06835411, 0.0, 0.05668995, 0.050163813, 0.053357534, 0.0, -0.020040333, -0.019867316, -0.01907621, 0.0) * go_0(1.0, 1.0);
result += mat4(-0.017078733, -0.017393313, -0.008266595, 0.0, -0.0033478448, -0.0027439648, -0.0042334674, 0.0, -0.06354017, -0.062058125, -0.04652064, 0.0, -0.010787706, -0.0062706997, -0.007573461, 0.0) * go_1(-1.0, -1.0);
result += mat4(-0.019895451, -0.016341688, -0.008712399, 0.0, 0.026231976, 0.023955572, 0.0216376, 0.0, -0.061950512, -0.05481285, -0.05261985, 0.0, -0.018804235, -0.016235247, -0.0131616965, 0.0) * go_1(-1.0, 0.0);
result += mat4(-0.055628926, -0.063315354, -0.057192408, 0.0, -0.0256364, -0.028660972, -0.02937357, 0.0, -0.017604912, -0.020851422, -0.016070362, 0.0, -0.0870202, -0.0832279, -0.07525406, 0.0) * go_1(-1.0, 1.0);
result += mat4(0.062738225, 0.07106593, 0.061644047, 0.0, -0.06068257, -0.06983662, -0.066070385, 0.0, 0.024919355, 0.03227179, 0.028569462, 0.0, -0.07866227, -0.098967604, -0.092128105, 0.0) * go_1(0.0, -1.0);
result += mat4(0.040397774, 0.047241107, 0.03962998, 0.0, -0.09112752, -0.10057507, -0.09301817, 0.0, 0.10833967, 0.101835825, 0.10027467, 0.0, 0.27189335, 0.27433604, 0.26781923, 0.0) * go_1(0.0, 0.0);
result += mat4(-0.044211388, -0.042373534, -0.03658007, 0.0, 0.113148406, 0.12423258, 0.107804194, 0.0, -0.17081551, -0.18562958, -0.17475435, 0.0, 0.09636739, 0.10763415, 0.093332425, 0.0) * go_1(0.0, 1.0);
result += mat4(-0.03798545, -0.047811143, -0.050768293, 0.0, 0.018775463, 0.026812987, 0.03452908, 0.0, 0.0055677597, 0.0039081173, -0.0017878668, 0.0, -0.10728597, -0.12618187, -0.109045394, 0.0) * go_1(1.0, -1.0);
result += mat4(0.06359783, 0.064184755, 0.04934199, 0.0, -0.009819327, -0.006616115, -0.007431496, 0.0, 0.025055679, 0.024787048, 0.017360551, 0.0, -0.047140837, -0.061695747, -0.06440822, 0.0) * go_1(1.0, 0.0);
result += mat4(0.060199022, 0.06482763, 0.059514645, 0.0, 0.026998974, 0.028776823, 0.024897143, 0.0, 0.17968474, 0.19337215, 0.16760105, 0.0, 0.0075838566, 0.010503482, 0.011993149, 0.0) * go_1(1.0, 1.0);
result += vec4(-0.0052927984, -0.0060193934, -0.0048643993, 0.0);
return result + MAIN_tex(MAIN_pos);
}
@@ -0,0 +1,275 @@
// MIT License
// Copyright (c) 2019-2021 bloc97
// All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(M)-Conv-4x3x3x3
//!HOOK MAIN
//!BIND MAIN
//!SAVE conv2d_tf
//!WIDTH MAIN.w
//!HEIGHT MAIN.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (MAIN_texOff(vec2(x_off, y_off)))
vec4 hook() {
vec4 result = mat4(-0.073079124, 0.11507942, 0.028201895, -0.021776304, -0.25251916, -0.08662003, 0.38814726, 0.4146095, 0.06326891, 0.01635252, 0.06423356, 0.13488062, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, -1.0);
result += mat4(-0.059791833, -0.03105604, 0.041643705, 0.35197195, -0.17314838, 0.067622855, -0.032012507, 0.09691628, -0.11094062, 0.007625051, 0.094762206, -0.05824145, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 0.0);
result += mat4(-0.120281175, 0.027440755, -0.026316144, -0.025291128, -0.41698205, -0.05966847, -0.28400028, -0.06946398, -0.10906026, -0.015854035, -0.028724853, -0.06626416, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 1.0);
result += mat4(0.068752654, -0.12652585, 0.38200122, 0.17978846, 0.2749825, 0.015504972, 0.21765926, 0.2246602, -0.062151223, 0.07457783, 0.13588274, -0.037328478, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, -1.0);
result += mat4(0.3718559, 0.025637252, -0.4048626, -0.41484925, 0.24768798, 0.09984098, -0.5663632, -0.6659978, 0.212067, -0.08328392, -0.5277322, -0.016879432, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 0.0);
result += mat4(-0.11072714, -0.010321538, 0.11265787, 0.0055236337, -0.13345073, 0.004847663, 0.3744461, -0.4038564, -0.09893075, 0.031325124, 0.2883957, -0.04268903, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 1.0);
result += mat4(0.10444391, -0.60588187, 0.02543334, 0.10911738, -0.10860678, 0.15701362, 0.29235297, 0.045803998, 0.076250754, -0.10799697, 0.08841044, 0.08145623, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, -1.0);
result += mat4(-0.04246739, -0.1225759, 0.11280305, -0.12079673, 0.5289142, -0.011892906, -0.0800465, 0.05915611, -0.126204, 0.08239301, -0.0092391195, -0.07672885, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 0.0);
result += mat4(-0.11922264, 0.14036109, -0.09491126, 0.05112697, -0.12543046, -0.08662423, -0.041537095, 0.048038274, 0.11672854, -0.006516173, -0.023524825, 0.030505095, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 1.0);
result += vec4(-0.07697861, 0.41154122, 0.042374082, -0.087270625);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_tf
//!SAVE conv2d_1_tf
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.09419103, -0.1178418, 0.09523275, 0.24648252, 0.03595256, -0.05417468, -0.029167585, -0.012279932, 0.08852021, -0.12534834, 0.0604663, 0.050634373, -0.19536541, 0.21548285, 0.040379744, -0.28046605) * go_0(-1.0, -1.0);
result += mat4(-0.13783203, 0.17191975, 0.06956328, 0.005270252, -0.029844455, -0.17657366, 0.03439078, 0.048861686, 0.12017991, -0.087307535, 0.11815637, 0.31309614, 0.08440897, 0.09969244, -0.06220224, 0.2633136) * go_0(-1.0, 0.0);
result += mat4(0.098606475, -0.05856224, -0.01163882, -0.020945825, -0.08988821, 0.18520717, 0.011407763, 0.20973705, 0.21017794, 0.038311377, -0.018910313, 0.053878684, -0.08751144, -0.0081623215, 0.29060364, 0.14363094) * go_0(-1.0, 1.0);
result += mat4(0.13354321, -0.38046083, 0.14157647, 0.10190452, -0.045502663, 0.0053245644, -0.10817685, -0.048371315, 0.16157807, 0.2086147, 0.07632662, 0.24636099, -0.0053555835, -0.19587666, -0.46687222, 0.002362032) * go_0(0.0, -1.0);
result += mat4(0.28275147, -0.1468291, 0.24075283, -0.35119128, 0.18727398, 0.3833064, 0.08667899, 0.15021381, -0.092296466, -0.25686404, -0.116076745, 0.2231862, -0.27637103, 0.12317917, -0.0341737, -0.40077657) * go_0(0.0, 0.0);
result += mat4(-0.007041629, 0.18089123, -0.21195571, -0.12346183, -0.06088577, -0.30784377, 0.0048495876, 0.06013008, 0.07200418, -0.0076884073, 0.02632822, -0.0011575016, 0.21025613, -0.2573419, -0.06994815, 0.32497165) * go_0(0.0, 1.0);
result += mat4(0.0016823286, -0.014366541, -0.5049525, 0.048534572, -0.0057915323, -0.0030526456, -0.028976317, -0.16376147, -0.15560333, -0.053708192, -0.055678204, -0.13087665, 0.0048869387, 0.027514834, 0.017380254, -0.06743363) * go_0(1.0, -1.0);
result += mat4(0.044514824, -0.1754644, -0.26664957, 0.1486667, 0.114894986, 0.061640915, -0.13305616, 0.06450565, 0.03552732, 0.2835473, 0.13800526, 0.005875215, 0.15751484, 0.41759813, -0.19406971, 0.071032055) * go_0(1.0, 0.0);
result += mat4(-0.18419577, -0.05527526, 0.017057603, -0.1146602, 0.15775396, -0.01188916, 0.09368113, 0.05765405, 0.064170234, -0.017833546, 0.12100514, -0.06250493, 0.2421206, 0.15719843, 0.23718071, 0.023142194) * go_0(1.0, 1.0);
result += mat4(0.079226464, 0.07877355, -0.022315226, -0.13507473, 0.14683898, 0.028739132, -0.24479519, -0.280197, -0.13223173, 0.21732429, -0.1546993, 0.045442928, 0.163642, -0.07062695, 0.03805918, 0.060860883) * go_1(-1.0, -1.0);
result += mat4(0.095216066, -0.16650215, -0.34863555, -0.025274571, 0.3064775, -0.034196265, -0.25773287, 0.19570488, -0.005434017, 0.26308087, 0.009404902, -0.24736062, 0.05558232, -0.014217521, 0.03667355, -0.15134114) * go_1(-1.0, 0.0);
result += mat4(-0.074846864, 0.010901994, 0.035149742, 0.12106729, -0.36042807, -0.011231913, 1.4317516, 0.6400351, 0.105860665, -0.11587906, -0.11065066, 0.19126756, 0.14132085, 0.021570992, -0.3618735, -0.081163004) * go_1(-1.0, 1.0);
result += mat4(-0.06937371, 0.3815214, 0.026842717, -0.04051589, -0.09472515, -0.027198657, -0.16502109, 0.114273794, -0.15207845, -0.15054241, -0.25099036, -0.10871029, 0.14311226, 0.07640166, 0.47051275, 0.0447809) * go_1(0.0, -1.0);
result += mat4(-0.25960425, 0.11150338, -0.042022616, -0.006633396, -0.29595324, -0.0149574205, 0.09806478, 0.03635802, 0.26789796, 0.41416678, 0.05145585, 0.61168057, 0.019582301, -0.118703716, 0.13974573, 0.04498941) * go_1(0.0, 0.0);
result += mat4(-0.04119621, -0.15503803, 0.33170196, -0.1158483, -0.06258357, 0.2574262, -0.07890287, -0.6929032, 0.004379942, 0.097908296, 0.009286624, 0.27194506, -0.2476702, 0.13828708, 0.05071857, -0.43693772) * go_1(0.0, 1.0);
result += mat4(-0.010842703, 0.13108006, 0.30126816, 0.20221521, 0.018797455, 0.0614624, 0.11102966, 0.019204421, 0.09975456, 0.04676902, -0.044540443, 0.118877, -0.04871634, -0.089208096, 0.027455999, 0.029557817) * go_1(1.0, -1.0);
result += mat4(-0.10421777, 0.3135469, 0.14557797, 0.0497297, 0.0034963787, -0.20342828, 0.08332032, -0.09004643, 0.06574797, -0.14168271, -0.08754358, 0.30385306, -0.3374016, -0.4360316, 0.15854433, -0.24081887) * go_1(1.0, 0.0);
result += mat4(0.1407836, 0.09678823, -0.02240152, -0.013985894, 0.012281648, -0.24124922, -0.46433777, -0.25915003, 0.042200714, -0.21701157, -0.016618999, 0.13135725, -0.34656256, -0.034729004, -0.29246503, -0.13514486) * go_1(1.0, 1.0);
result += vec4(-0.08458621, -0.023144595, -0.057707336, -0.081382714);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_1_tf
//!SAVE conv2d_2_tf
//!WIDTH conv2d_1_tf.w
//!HEIGHT conv2d_1_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.27743992, 0.04277345, 0.019331178, -0.7335445, 0.006292013, 0.19800001, -0.0025032016, 0.16098699, -0.03186617, -0.060173523, 0.08878855, -0.10669283, 0.130609, -0.068515256, -0.03571823, -0.13751523) * go_0(-1.0, -1.0);
result += mat4(-0.2430821, -0.08233978, 0.082374334, 0.04843392, -0.18989052, -0.041925047, 0.40021122, -0.317836, -0.13517766, 0.032255337, -0.0746507, 0.22114721, -0.045706213, -0.12841983, -0.27830583, 0.05763077) * go_0(-1.0, 0.0);
result += mat4(-0.08436965, -0.04967552, -0.16798134, -0.1539139, -0.17429228, -0.10166739, 0.35864773, 0.12873615, -0.07667423, 0.04985163, 0.13391761, -0.054322604, 0.085659124, -0.078792974, 0.06481059, 0.058667548) * go_0(-1.0, 1.0);
result += mat4(-0.17568155, 0.56705236, 0.056562193, -0.020951264, 0.005879628, -0.2502103, -0.19619654, 0.019490348, -0.14527243, 0.16983634, 0.049245857, 0.18316677, 0.055053137, 0.10699275, 0.0016993808, 0.20105995) * go_0(0.0, -1.0);
result += mat4(0.36284775, -0.05856962, -0.42545465, 0.31931567, -0.15698905, -0.28837132, -0.028697362, -0.024917847, 0.04317283, 0.024557106, -0.052158598, 0.38654143, -0.1782944, 0.43094924, -0.11738149, 0.21554618) * go_0(0.0, 0.0);
result += mat4(0.22645079, -0.20319854, 0.20733371, -0.18697177, -0.05167819, -0.12845007, 0.5543688, 0.2453291, 0.08027872, -0.0628224, -0.06593836, -0.05795855, -0.24527508, 0.23632833, -0.043366548, 0.14135826) * go_0(0.0, 1.0);
result += mat4(0.08384414, 0.20807321, 0.030559694, -0.13640808, -0.07641805, -0.10919174, -0.19799095, -0.12955745, 0.093737304, -0.17856954, 0.035103753, -0.044699315, -0.07255943, -0.02331535, 0.2059249, 0.3058302) * go_0(1.0, -1.0);
result += mat4(0.022345139, 0.16286038, -0.27228013, -0.41105714, -0.0014384583, 0.089546144, -0.08296848, -0.0050463285, 0.07038578, -0.030679917, 0.031246305, 0.36761853, -0.34799108, -0.0405689, -0.19182852, 0.015853593) * go_0(1.0, 0.0);
result += mat4(0.1021783, -0.11396049, -0.08733628, -0.017449526, 0.042015605, -0.14808236, 0.10072531, -0.07403295, 0.15276712, -0.07807765, -0.10013386, -0.26110634, -0.04858846, 0.066066965, 0.13598624, 0.21687816) * go_0(1.0, 1.0);
result += mat4(0.07041569, -0.17775945, 0.15697548, -0.15425202, -0.06569677, -0.033233996, 0.22596005, -0.026170855, -0.20729817, 0.1316505, -0.058410037, 0.22166035, 0.09107114, -0.13078825, -0.05639485, -0.02716142) * go_1(-1.0, -1.0);
result += mat4(0.057966787, -0.15311252, 0.095924966, -0.055951685, 0.082777694, -0.08471956, -0.39918202, 0.10599212, 0.102710955, 0.21808124, 0.12083635, -0.38835892, 0.031709857, 0.13955092, 0.12647778, 0.011549966) * go_1(-1.0, 0.0);
result += mat4(0.09810508, -0.119743295, 0.06166254, 0.13595435, 0.036198203, -0.028710455, -0.40789905, -0.034894038, -0.12622337, 0.14379597, 0.039958883, 0.19636424, 0.047094557, -0.07987105, -0.04905092, -0.07875785) * go_1(-1.0, 1.0);
result += mat4(0.34118712, -0.2833933, -0.045028314, -0.40670308, -0.01961924, 0.37131935, 0.29099533, -0.19843055, 0.18604252, -0.0037280058, 0.1091072, -0.40579233, 0.11422739, -0.16490164, -0.0022396361, -0.21486944) * go_1(0.0, -1.0);
result += mat4(0.0010853866, 0.2223109, 0.2416471, -0.33326814, 0.2549397, 0.6442047, 0.18411863, -0.19081281, -0.43552014, -0.1793875, -0.58699155, -0.01900168, -0.26955804, -0.071371995, 0.07599079, 0.27434483) * go_1(0.0, 0.0);
result += mat4(-0.19644544, 0.14383379, -0.2599538, 0.001666124, -0.16369823, 0.009537702, -0.3690974, -0.048157427, -0.2040159, 0.01522431, -0.11007749, -0.07012568, 0.17536888, -0.012183123, -0.17366478, -0.15090804) * go_1(0.0, 1.0);
result += mat4(0.0855136, 0.06863859, -0.17249937, -0.12850079, 0.15325847, 0.22742507, 0.22535504, 0.24032994, -0.109522276, 0.24135293, -0.17784368, 0.08172238, -0.16143093, 0.1358853, -0.09399085, 0.012180792) * go_1(1.0, -1.0);
result += mat4(-0.04346881, 0.13367178, 0.10387612, 0.04705543, -0.10315795, 0.5816371, -0.090529, -0.017955385, -0.09032907, -0.52505773, 0.10958755, -0.26151448, 0.17246644, 0.011886279, 0.3566306, 0.32170597) * go_1(1.0, 0.0);
result += mat4(-0.27853554, 0.1558035, 0.070289604, 0.17052644, -0.31982365, 0.29085326, -0.09494764, 0.2270542, 0.10514691, -0.24606484, -0.02049181, 0.126686, 0.16719124, 0.013080999, -0.08577963, -0.07057233) * go_1(1.0, 1.0);
result += vec4(0.0061747693, -0.029145364, -0.026801255, 0.027419873);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_2_tf
//!SAVE conv2d_3_tf
//!WIDTH conv2d_2_tf.w
//!HEIGHT conv2d_2_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.07447851, -0.07888509, -0.28236163, 0.2479792, -0.065199964, 0.24733023, 0.099619575, -0.26430824, -0.03523585, -0.03547245, -0.10619345, -0.25326422, -0.116270036, -0.065133184, -0.30401528, 0.01563764) * go_0(-1.0, -1.0);
result += mat4(-0.19106275, -0.26104823, -0.14457102, -0.17298317, 0.24148639, -0.10950928, 0.062851585, 0.042540826, 0.13287601, 0.06975747, 0.15848075, -0.3854902, -0.13132331, -0.16468687, -0.029844414, 0.27754608) * go_0(-1.0, 0.0);
result += mat4(0.015378025, -0.14203559, 0.08058816, 0.32896644, -0.074871175, -0.26611313, -0.18830848, 0.091641426, -0.16522385, -0.23424402, -0.12279703, -0.13343342, -0.2509982, -0.0554576, 0.07286022, -0.028823337) * go_0(-1.0, 1.0);
result += mat4(-0.13543738, 0.049395677, -0.015148539, 0.13301241, -0.12827122, -0.012590744, 0.012936948, 0.008271658, 0.12442749, 0.3497426, -0.16126058, -0.2670464, -0.010479037, 0.07037434, -0.15527055, 0.13205245) * go_0(0.0, -1.0);
result += mat4(-0.09535385, -0.3931354, 0.24716614, -0.21284536, 0.14652656, 0.38149378, -0.09607391, 0.06350967, 0.48615915, 0.32634613, 0.146291, 0.2566475, -0.40927815, -0.05268087, -0.04110691, -0.0068722935) * go_0(0.0, 0.0);
result += mat4(0.089152284, -0.01860622, 0.016856732, 0.31244752, 0.022529159, -0.0071319416, -0.09786801, -0.13005258, 0.1524636, 0.21627748, -0.07395979, -0.087633945, -0.38435504, -0.08842507, -0.0058702417, -0.32936293) * go_0(0.0, 1.0);
result += mat4(0.0816838, 0.0012210817, 0.28217188, 0.36141106, 0.0014665248, -0.0636269, 0.042035818, -0.056671552, -0.032501306, -0.22908778, -0.2067977, -0.004497514, -0.23052917, 0.26728114, 0.15353456, -0.17732324) * go_0(1.0, -1.0);
result += mat4(-0.17229734, 0.0818218, -0.10076918, 0.030027041, -0.14819005, -0.085340135, 0.050100215, 0.05683199, -0.12653661, -0.036583595, -0.32319903, -0.15273796, -0.15346588, 0.20005536, 0.23097478, -0.19834782) * go_0(1.0, 0.0);
result += mat4(0.055430107, -0.2886931, 0.361814, 0.33160287, -0.084407054, 0.06254009, -0.02332793, -0.018134018, -0.014879812, 0.112346604, -0.20686437, -0.23408228, -0.01091196, -0.062669374, 0.085567676, 0.23738655) * go_0(1.0, 1.0);
result += mat4(0.080383554, -0.1172084, 0.19703126, 0.27777427, -0.07559937, -0.25445858, 0.3450109, -0.071967736, 0.2034805, 0.33716002, 0.15314537, -0.22953224, 0.113631405, -0.0058444734, 0.2890972, 0.06557255) * go_1(-1.0, -1.0);
result += mat4(-0.17646056, -0.025448758, -0.14952567, 0.017148364, -0.15238142, 0.1435677, 0.20273875, 0.22255951, -0.011660059, -0.003515217, -0.17305166, -0.13478355, -0.06558679, -0.032662887, -0.20914736, -0.5397283) * go_1(-1.0, 0.0);
result += mat4(0.1679393, -0.109410115, -0.117427185, 0.14982319, -0.06457877, -0.06607065, 0.0018200208, -0.0118605625, 0.046539318, -0.020642165, -0.14413542, -0.09530688, 0.22196163, -0.2187166, -0.10759705, 0.013234591) * go_1(-1.0, 1.0);
result += mat4(-0.13220267, -0.12540027, 0.26163217, 0.12791659, 0.16204996, -0.4023048, -0.13485721, -0.10187536, 0.059764992, 0.048170995, -0.25281772, 0.2090587, -0.06542371, -0.10791867, -0.21286209, -0.309109) * go_1(0.0, -1.0);
result += mat4(0.16233061, 0.120428756, -0.11460241, 0.24531102, -0.2670459, -0.24195078, -0.20964348, -0.12930301, -0.2343609, -0.22543164, -0.28909695, -0.33560297, 0.6009884, 0.39171818, -0.1276308, -0.020736236) * go_1(0.0, 0.0);
result += mat4(0.40162864, 0.045881115, 0.032667033, 0.31454235, -0.17351128, -0.009387306, 0.17341217, 0.30810982, -0.025815086, -0.10390133, 0.012544771, 0.036918722, 0.34386298, 0.23177734, -0.046727546, 0.20098232) * go_1(0.0, 1.0);
result += mat4(0.11541034, -0.11647824, -0.12874861, 0.004921287, -0.13921295, -0.25733355, -0.1112383, -0.033295818, 0.0035326157, 0.3782048, 0.055785846, -0.1547331, 0.17358719, -0.2789715, -0.13400431, 0.1583795) * go_1(1.0, -1.0);
result += mat4(0.4130191, -0.33944547, -0.064674884, 0.39617148, -0.11483455, -0.022601767, 0.1129301, -0.09713594, 0.14681247, 0.34442267, 0.08721343, -0.08309499, 0.088704996, -0.20943391, -0.2891408, 0.1709401) * go_1(1.0, 0.0);
result += mat4(0.19503653, 0.17490312, -0.23491044, -0.028934423, 0.04479765, -0.0334551, 0.0602648, 0.0019939998, 0.23314747, 0.21557438, 0.07273092, 0.15467109, -0.11194509, 0.0736583, -0.17628083, -0.3851578) * go_1(1.0, 1.0);
result += vec4(0.022887055, 0.01521631, 0.17967467, -0.0131908795);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_3_tf
//!SAVE conv2d_4_tf
//!WIDTH conv2d_3_tf.w
//!HEIGHT conv2d_3_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.10233342, -0.30233157, 0.24238978, -0.007108631, 0.14248851, 0.08486557, 0.0028373515, 0.122387215, 0.10996857, -0.17286511, 0.19819227, 0.07023527, 0.07579955, -0.16861476, -0.210025, 0.12760942) * go_0(-1.0, -1.0);
result += mat4(0.091181986, -0.41497424, -0.27567792, -0.09938067, -0.12210428, 0.20617811, -0.017644284, -0.22552875, 0.049019493, -0.18990634, 0.11057753, -0.043193225, -0.15278774, -0.18331046, -0.1837594, 0.029758787) * go_0(-1.0, 0.0);
result += mat4(-0.1757096, -0.199691, -0.034743477, -0.15369363, -0.1701244, -0.0459655, 0.10508695, -0.09795581, 0.13464944, 0.37202564, 0.14706515, 0.23416734, 0.08302458, 0.20696343, -0.13935481, 0.03092827) * go_0(-1.0, 1.0);
result += mat4(-0.49887478, 0.24046332, -0.029459715, 0.17374687, -0.15019977, 0.31086043, -0.28297687, -0.22239804, 0.12314376, 0.11594825, 0.17682782, 0.09753434, -0.26263535, -0.12739435, -0.57744014, 0.087381124) * go_0(0.0, -1.0);
result += mat4(0.08101439, -0.16185337, 0.112901986, 0.24439482, -0.44051018, -0.70680356, -0.23015513, 0.63106006, -0.08817581, -0.057614524, 0.15352182, -0.049620207, 0.17742544, -0.49583626, -0.3844133, 0.18385352) * go_0(0.0, 0.0);
result += mat4(0.17149475, 0.31255633, 0.19286609, 0.21052869, -0.11856372, -0.032373343, 0.06503625, -0.31664965, 0.040755365, -0.027614031, -0.33330554, 0.40148625, 0.056921627, -0.27068445, 0.047014963, 0.103712596) * go_0(0.0, 1.0);
result += mat4(-0.09326643, 0.13677256, 0.06390537, 0.08080093, -0.10685094, 0.124757454, 0.14696303, 0.10871933, -0.10971212, 0.01655797, -0.11052674, -0.17361104, 0.015513338, -0.1917502, -0.26384255, -0.022672707) * go_0(1.0, -1.0);
result += mat4(0.032367155, -0.087523445, -0.06951093, -0.08128242, 0.2627859, 0.14933161, 0.3114999, -0.007791172, -0.4146471, -0.2530298, -0.43175155, -0.06878434, 0.5724947, 0.25498095, 0.4838959, 0.15076154) * go_0(1.0, 0.0);
result += mat4(-0.13427481, -0.10134261, 0.087439895, 0.015921364, 0.15421022, 0.20205952, 0.22928835, -0.07339068, -0.33318612, -0.17467582, -0.04758165, 0.11858059, 0.17408857, -0.099393494, -0.06389948, -0.06494366) * go_0(1.0, 1.0);
result += mat4(0.15349221, 0.08508258, -0.09294437, -0.03204993, -0.22561033, -0.15088828, -0.020105945, 0.10041996, -0.024723593, 0.06610271, -0.24423431, -0.050512858, -0.100530736, 0.16394953, 0.16365045, -0.012055956) * go_1(-1.0, -1.0);
result += mat4(0.16342951, 0.23113559, 0.21289586, 0.28391558, 0.052211206, -0.17983536, -0.008415342, 0.08977486, -0.054481823, 0.17506577, -0.14162593, -0.070448756, 0.093877845, 0.05161232, -0.25006327, 0.007014646) * go_1(-1.0, 0.0);
result += mat4(0.104965575, 0.20048036, 0.024134496, 0.5442797, 0.19958296, -0.05165447, 0.076928124, 0.030868227, -0.0563495, -0.19757621, 0.10801544, -0.24202053, 0.0067657093, -0.17784451, -0.03134409, -0.06741009) * go_1(-1.0, 1.0);
result += mat4(0.33347723, -0.12338564, 0.23495969, -0.23091966, 0.059872203, 0.028045453, -0.06781438, 0.111325614, -0.21861015, -0.030451605, -0.04267672, -0.0039260075, 0.0911101, 0.054191053, -0.08498816, 0.04810343) * go_1(0.0, -1.0);
result += mat4(-0.05028896, 0.21515386, 0.16005337, -0.32279232, 0.19178568, 0.779363, -0.12682606, -0.4378189, 0.37980273, 0.063021325, 0.19370794, -0.05618088, -0.00088428083, 0.29736623, 0.24649377, -0.0021625878) * go_1(0.0, 0.0);
result += mat4(-0.45007992, -0.16040307, -0.1714593, -0.16251564, 0.070867635, 0.21317895, -0.070962, 0.17147541, -0.27786884, -0.33259448, -0.022767346, -0.17967366, 0.21208113, 0.19740848, 0.16877973, 0.09630738) * go_1(0.0, 1.0);
result += mat4(0.09235827, -0.35231477, -0.093315996, -0.035850406, -0.08311695, 0.054329164, 0.17788444, -0.020736141, -0.03739786, -0.1678283, 0.12676615, 0.17182353, 0.17408027, 0.07699043, 0.095501214, 0.0069830767) * go_1(1.0, -1.0);
result += mat4(-0.16631392, -0.16925642, -0.17081848, 0.017719474, -0.20530944, 0.19215193, -0.039511178, -0.08296625, 0.2240653, 0.100644305, 0.2901835, 0.32166973, -0.10026419, -0.14864013, -0.19926691, -0.11607018) * go_1(1.0, 0.0);
result += mat4(-0.13750182, 0.07445518, -0.033964884, -0.085812084, -0.03903257, -0.054933593, 0.06765632, 0.064338475, 0.27182797, 0.07721309, -0.0334218, -0.19344835, -0.14405386, 0.046106674, 0.16596143, 0.0879945) * go_1(1.0, 1.0);
result += vec4(0.049844168, 0.02670437, 0.050967637, -0.10779561);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_4_tf
//!SAVE conv2d_5_tf
//!WIDTH conv2d_4_tf.w
//!HEIGHT conv2d_4_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.034357008, 0.00082413113, -0.13382089, -0.05066409, 0.26684088, -0.31363875, 0.073608615, 0.20824149, 0.21509308, -0.07118628, 0.11287014, -0.09817389, 0.16107765, 0.17146803, -0.13836654, -0.05962866) * go_0(-1.0, -1.0);
result += mat4(0.029981667, 0.08738892, 0.17735903, 0.15817277, 0.041752994, -0.20031185, 0.064203605, 0.48786053, -0.0033609737, -0.42522693, 0.058846988, 0.22180536, 0.17181319, 0.13097888, -0.059532285, 0.062227458) * go_0(-1.0, 0.0);
result += mat4(0.13188283, 0.07971828, 0.28278515, 0.038570832, -0.12815465, 0.29860008, -0.2785862, -0.07612298, -0.14369671, 0.12457525, 0.11982623, -0.018675303, 0.14936846, 0.1284789, -0.0042489986, 0.042810377) * go_0(-1.0, 1.0);
result += mat4(0.2892425, -0.20834558, 0.07358541, -0.11190968, -0.16300741, 0.15674856, -0.04297203, -0.29218298, -0.036296643, -0.052267153, -0.22889943, -0.21203549, -0.03553075, -0.20343691, -0.07413655, -0.092966415) * go_0(0.0, -1.0);
result += mat4(0.2484468, -0.23412868, -0.070199326, 0.2061922, 0.5047224, -0.48216155, -0.5792768, 0.610787, 0.023935676, -0.040435325, -0.1238493, -0.09576053, -0.26183444, 0.14510648, 0.5365255, 0.5499143) * go_0(0.0, 0.0);
result += mat4(-0.058255754, 0.08133753, -0.18663554, 0.26190025, 0.26006857, -0.007619795, 0.14585225, 0.073580734, -0.0396066, 0.2821596, 0.31778112, -0.029853562, -0.19703479, 0.17809318, 0.21089044, -0.106730856) * go_0(0.0, 1.0);
result += mat4(0.20549655, -0.05962688, 0.1432124, 0.013446325, -0.19064854, 0.061387196, 0.1792527, 0.0010619498, -0.1456842, 0.18950678, -0.13990986, -0.37644413, -0.083257, -0.2937246, 0.032096215, 0.14719158) * go_0(1.0, -1.0);
result += mat4(-0.26601696, 0.4242341, -0.073702715, -0.3221337, 0.026037043, -0.0117916465, -0.024286825, 0.23183465, -0.030869482, -0.045915652, -0.040611852, 0.11372697, -0.25404635, 0.21859063, 0.13869432, 0.19651218) * go_0(1.0, 0.0);
result += mat4(-0.028276298, -0.11217159, 0.27144867, -0.010531775, 0.11032058, -0.09957206, 0.12570262, 0.14724332, 0.08758557, -0.11042305, 0.025948172, -0.010910802, -0.029466914, -0.041135952, -0.017091949, 0.05501236) * go_0(1.0, 1.0);
result += mat4(-0.12688768, -0.19051413, 0.052141912, -0.13618521, -0.16320245, -0.1601866, 0.16207355, -0.023218745, 0.2103894, -0.06212745, -0.07042835, 0.0996637, -0.1763831, 0.13890013, -0.12125462, -0.105104685) * go_1(-1.0, -1.0);
result += mat4(0.10485512, -0.49283037, -0.504295, 0.009089699, -0.17389494, -0.12835866, 0.14188384, -0.22946316, 0.006298799, -0.0348454, -0.0852419, 0.17956656, -0.08088888, 0.35675287, 0.16014415, -0.055372503) * go_1(-1.0, 0.0);
result += mat4(-0.17157564, 0.1557075, -0.17681694, 0.14834762, -0.13708206, 0.101721555, 0.17070566, -0.22522852, 0.08100986, -0.23204406, -0.38926315, -0.13165781, 0.1040296, -0.045591615, 0.15745829, -0.10410621) * go_1(-1.0, 1.0);
result += mat4(-0.20517144, 0.35896194, -0.0010962893, -0.18043008, -0.016253468, 0.035292216, 0.06781499, 0.015984116, -0.20261237, -0.28905126, 0.007414641, 0.008870292, 0.52166605, -0.0996688, -0.23151648, 0.2811893) * go_1(0.0, -1.0);
result += mat4(0.013482173, -0.04891998, -0.06094191, -0.14416319, -0.00087873987, 0.11979091, 0.06457245, -0.2305623, -0.1476981, 0.2634587, -0.058895197, -0.07394766, -0.27173743, 0.7530214, 0.037599873, 0.22086331) * go_1(0.0, 0.0);
result += mat4(-0.10357755, 0.23899554, 0.034912035, -0.14336212, -0.019786308, -0.085470654, -0.03096524, 0.108783185, 0.28971127, 0.24527478, -0.19110362, -0.49510127, -0.15574701, 0.10968643, -0.13727877, 0.04502924) * go_1(0.0, 1.0);
result += mat4(-0.10808282, -0.079148844, -0.3244773, -0.2210664, -0.0062175165, 0.1303082, 0.012592612, -0.38039228, 0.26899642, -0.16624425, -0.04438198, 0.42067865, -0.13381268, 0.03408099, -0.2999706, -0.3817641) * go_1(1.0, -1.0);
result += mat4(0.030872926, -0.26852018, -0.14650428, 0.16869825, -0.19185568, -0.06341456, 0.12261606, -0.26597574, 0.44865233, 0.21416639, 0.40359476, 0.12814924, 0.2542566, -0.23348318, -0.43142912, -0.35113996) * go_1(1.0, 0.0);
result += mat4(-0.03364283, 0.11002299, 0.3311268, -0.14580412, -0.10348537, 0.13331696, -0.0793144, -0.04116661, 0.040704627, -0.14875266, -0.09259674, -0.062087066, 0.063962296, 0.18420577, -0.085616685, -0.16555141) * go_1(1.0, 1.0);
result += vec4(-0.037546165, -0.015675364, 0.13989694, 0.027605768);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_5_tf
//!SAVE conv2d_6_tf
//!WIDTH conv2d_5_tf.w
//!HEIGHT conv2d_5_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.35835463, 0.038305778, -0.10198824, -0.021951782, 0.02142098, -0.072417736, -0.2577152, 0.054713376, 0.075116105, -0.21191697, -0.1213158, -0.105036296, 0.12030758, -0.17591658, 0.1726511, 0.17754573) * go_0(-1.0, -1.0);
result += mat4(0.32325825, 0.19869742, 0.333873, 0.39670366, 0.20716824, 0.09557955, 0.120742686, -0.2271023, 0.37509173, -0.031341635, 0.10247365, 0.031520665, -0.092765376, -0.13535516, 0.8333728, 0.05886494) * go_0(-1.0, 0.0);
result += mat4(-0.17573749, 0.16768494, 0.021141645, 0.19668253, 0.21080776, 0.31503728, -0.26834, 0.19103156, 0.21946241, 0.14559007, -0.09761235, -0.23565038, -0.49393657, -0.5332298, 0.09806347, 0.054431103) * go_0(-1.0, 1.0);
result += mat4(-0.042109374, -0.05564321, 0.27586877, 0.010382545, 0.007322007, 0.13193823, -0.18262729, 0.06399193, 0.14174329, 0.3898842, -0.10398105, 0.01846146, -0.24542394, -0.13020967, -0.16491668, -0.03544872) * go_0(0.0, -1.0);
result += mat4(-0.15291597, 0.1566557, 0.14745249, -0.23258151, 0.17843612, 0.15885495, -0.691466, -0.41177312, 0.40330106, -0.07991953, 0.2832403, 0.10656986, -0.19571523, 0.3670614, -0.62296015, -0.5666968) * go_0(0.0, 0.0);
result += mat4(-0.17513512, 0.011393021, -0.44352317, -0.059153114, -0.2227142, -0.033094753, 0.09624524, 0.051315393, 0.2632246, 0.09945105, 0.042561427, -0.1234722, 0.23755905, -0.506999, 0.114180565, 0.27887583) * go_0(0.0, 1.0);
result += mat4(-0.459564, -0.120326266, 0.17507194, 0.06701153, -0.14124362, -0.36653697, -0.2856802, -0.22955593, -0.08515889, 0.18788262, 0.23427077, 0.021544341, 0.31996533, -0.2668834, 0.08469808, -0.01347926) * go_0(1.0, -1.0);
result += mat4(-0.14092083, -0.31244513, -0.044023518, 0.013948701, 0.33119613, -0.011959397, -0.1494438, -0.111066826, -0.11994278, 0.116068155, -0.13032633, -0.037004936, 0.13851176, -0.006655432, -0.39841232, -0.079951204) * go_0(1.0, 0.0);
result += mat4(-0.08959123, 0.18297827, -0.0763483, 0.11364159, -0.04361797, -0.029816678, -0.19314721, -0.03484794, 0.044681285, 0.04669291, -0.30017474, -0.07453036, 0.090825416, -0.27414632, 0.36355078, 0.15742934) * go_0(1.0, 1.0);
result += mat4(0.18470702, 0.113800436, -0.18546791, 0.044184085, 0.12490399, 0.1826781, -0.01313173, -0.19048993, -0.026458051, -0.1693334, 0.21958382, 0.030458853, -0.059242606, 0.039351143, -0.061676584, -0.06904634) * go_1(-1.0, -1.0);
result += mat4(-0.114877924, -0.03781683, -0.19207929, 0.007679428, 0.2409049, 0.2965285, -0.38395065, 0.11604976, -0.22588749, 0.48505852, 0.09866521, -0.2585994, -0.011380872, -0.018334057, -0.047188547, 0.3038583) * go_1(-1.0, 0.0);
result += mat4(-0.2783936, -0.17609318, 0.4904369, -0.31848624, 0.39725313, 0.082951784, -0.15595853, -0.007526218, 0.2355193, -0.30003366, -0.27686292, 0.120900005, -0.1223885, 0.40760317, 0.0013726618, -0.24877374) * go_1(-1.0, 1.0);
result += mat4(0.1580051, -0.044973504, 0.00053594523, -0.057797022, 0.18895927, 0.23527777, -0.18095906, -0.076961614, 0.2544444, -0.05932328, 0.13717431, -0.024487074, 0.33157274, -0.09072586, -0.004386734, -0.05180953) * go_1(0.0, -1.0);
result += mat4(-0.21685815, 0.061656334, -0.066127226, 0.24831405, 0.26001146, 0.046466008, -0.047196623, 0.13538954, -0.06449239, 0.45951647, -0.13132116, -0.7079741, -0.06683439, -0.47628635, 0.42461708, 0.6475073) * go_1(0.0, 0.0);
result += mat4(0.2590011, -0.26020283, 0.0005333198, 0.01555692, 0.37920526, 0.29205114, -0.20281325, -0.1455974, 0.056119893, 0.022032745, -0.30095813, 0.48154855, -0.35761952, 0.07582935, 0.12462687, 0.068093665) * go_1(0.0, 1.0);
result += mat4(0.20434918, 0.26690874, 0.028224666, 0.042565826, 0.037406113, 0.5059272, -0.0047208676, 0.0019095197, 0.16626422, -0.23407575, -0.072687164, 0.00063299487, -0.10172441, -0.11645544, 0.008715937, -0.012423992) * go_1(1.0, -1.0);
result += mat4(0.08269191, 0.116322584, -0.08155921, -0.04790326, 0.09546776, 0.3632936, -0.08139031, -0.10399187, 0.06618616, -0.26862565, 0.25058737, 0.0410593, -0.07191658, -0.20559746, 0.21857823, 0.12776822) * go_1(1.0, 0.0);
result += mat4(0.54989135, 0.38051483, 0.015739547, -0.0068143173, 0.26107135, 0.2585036, -0.12345306, -0.13934542, -0.19018838, 0.2730626, 0.42644337, 0.16693048, -0.15189888, 0.023638237, 0.11272267, 0.039560657) * go_1(1.0, 1.0);
result += vec4(-0.20554838, -0.10647836, -0.02824578, 0.08658529);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(M)-Conv-3x1x1x56
//!HOOK MAIN
//!BIND MAIN
//!BIND conv2d_tf
//!BIND conv2d_1_tf
//!BIND conv2d_2_tf
//!BIND conv2d_3_tf
//!BIND conv2d_4_tf
//!BIND conv2d_5_tf
//!BIND conv2d_6_tf
//!SAVE MAIN
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
#define g_0 (max((conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_1 (max(-(conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_2 (max((conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_3 (max(-(conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_4 (max((conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_5 (max(-(conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_6 (max((conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_7 (max(-(conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_8 (max((conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_9 (max(-(conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_10 (max((conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_11 (max(-(conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_12 (max((conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_13 (max(-(conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
vec4 hook() {
vec4 result = mat4(-0.030150581, -0.002168429, 0.014918388, 0.0, 0.020940892, 0.04591048, 0.049137186, 0.0, 0.111167125, 0.05311203, 0.0625381, 0.0, 0.020043287, 0.04785493, 0.040921766, 0.0) * g_0;
result += mat4(0.04158565, -0.008488135, 0.0020472286, 0.0, 0.049123142, -0.055042226, -0.06489915, 0.0, 0.09238876, 0.10387972, 0.09576964, 0.0, -0.054776173, -0.098954335, -0.09018853, 0.0) * g_1;
result += mat4(0.2081418, 0.08273068, 0.040325668, 0.0, -0.09937802, -0.13162258, -0.13989717, 0.0, -0.13983749, 0.01309777, 0.0023888077, 0.0, -0.18937743, -0.07021057, -0.047152344, 0.0) * g_2;
result += mat4(-0.09646629, 0.080605574, 0.10463501, 0.0, 0.22579835, 0.24077554, 0.22600271, 0.0, 0.049726978, 0.015292378, -0.0047161994, 0.0, 0.16281025, 0.048491795, 0.038338162, 0.0) * g_3;
result += mat4(-0.09772107, -0.043998875, -0.054745924, 0.0, -0.1257736, -0.13175423, -0.10889618, 0.0, -0.015900036, 0.07074481, 0.08210496, 0.0, -0.11321135, -0.12526917, -0.105605066, 0.0) * g_4;
result += mat4(0.14187162, 0.14032297, 0.13016908, 0.0, 0.018954534, 0.016011704, 0.010169183, 0.0, 0.04762765, -0.044460997, -0.06499567, 0.0, 0.11133751, 0.09464176, 0.08865274, 0.0) * g_5;
result += mat4(-0.16567162, -0.1744712, -0.1637222, 0.0, -0.02412003, 0.0074480795, 0.007903436, 0.0, -0.06161098, -0.046788957, -0.03971239, 0.0, 0.030736001, 0.036460854, 0.03660504, 0.0) * g_6;
result += mat4(0.084027, 0.10024112, 0.08152756, 0.0, 0.005087354, -0.026047802, -0.027264625, 0.0, 0.10519243, 0.08977278, 0.077558964, 0.0, -0.052826345, -0.06602686, -0.055083472, 0.0) * g_7;
result += mat4(0.007862721, 0.009936555, 0.012004831, 0.0, -0.042322706, -0.061728776, -0.05359773, 0.0, 0.030532641, 0.045623366, 0.04214089, 0.0, 0.030569768, 0.011892851, 0.0074041556, 0.0) * g_8;
result += mat4(0.03948997, 0.043119986, 0.039943404, 0.0, 0.0526772, 0.06820589, 0.058139592, 0.0, -0.062081397, -0.06755701, -0.054816127, 0.0, -0.004076369, 0.0061744447, 0.016273081, 0.0) * g_9;
result += mat4(0.0071622543, 0.004829105, -0.002032197, 0.0, -0.048541367, -0.059043564, -0.05662218, 0.0, 0.0015553127, 0.009178359, 0.009577062, 0.0, 0.114169896, 0.1349016, 0.11432262, 0.0) * g_10;
result += mat4(0.019324556, 0.028323999, 0.027396113, 0.0, 0.016746879, 0.01608199, 0.026891617, 0.0, 0.12068619, 0.13617857, 0.113496214, 0.0, -0.013930715, -0.014250072, -0.00824306, 0.0) * g_11;
result += mat4(-0.0024534757, -0.0064973077, -0.007905654, 0.0, -0.019158727, -0.024820521, -0.020509848, 0.0, -0.09608131, -0.11177871, -0.10503465, 0.0, -0.011210447, -0.010875943, -0.015295865, 0.0) * g_12;
result += mat4(0.09681486, 0.113604136, 0.10416855, 0.0, -0.08199983, -0.09013433, -0.08562243, 0.0, 0.041304465, 0.048315883, 0.042945288, 0.0, -0.09863276, -0.117853515, -0.09870226, 0.0) * g_13;
result += vec4(-0.0039074384, -0.0085585555, -0.0132283475, 0.0);
return result + MAIN_tex(MAIN_pos);
}
@@ -0,0 +1,137 @@
// MIT License
// Copyright (c) 2019-2021 bloc97
// All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(S)-Conv-4x3x3x3
//!HOOK MAIN
//!BIND MAIN
//!SAVE conv2d_tf
//!WIDTH MAIN.w
//!HEIGHT MAIN.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (MAIN_texOff(vec2(x_off, y_off)))
vec4 hook() {
vec4 result = mat4(0.10922428, -0.16249932, 0.15452726, -0.15669551, 0.053448875, -0.16528402, 0.01697721, -0.049275912, 0.20947173, -0.10576949, 0.19738325, -0.025417482, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, -1.0);
result += mat4(-0.3285196, 0.15909512, -0.5273671, 0.23778777, -0.40508887, -0.0609677, -0.4188177, 0.11137456, -0.24131267, 0.10453423, -0.36216277, 0.053446792, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 0.0);
result += mat4(0.23072472, -0.082083695, -0.0041477727, -0.09136237, 0.11958912, -0.312698, -0.15842685, -0.013882424, 0.10933724, 0.017880991, -0.022167003, 0.014662608, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 1.0);
result += mat4(-0.2789985, 0.054727737, 0.22577816, -0.49625716, -0.48472273, -0.011525487, 0.5354349, -0.08814955, -0.27021924, -0.044563178, 0.008232271, -0.13480483, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, -1.0);
result += mat4(-0.18203105, 0.09277001, 0.27071548, -0.17773713, -0.4335171, 1.2275106, -0.07663438, -0.29020032, 0.011992759, 0.060106967, 0.11002492, -0.046098012, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 0.0);
result += mat4(0.08363418, 0.063420765, -0.10278259, 0.09357691, 0.38670546, 0.13577081, 0.048631024, -0.024960777, 0.0846784, -0.057097007, 0.06049236, 0.042082917, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 1.0);
result += mat4(0.12315548, -0.056513585, -0.09826642, -0.17079762, 0.06479095, -0.36984903, -0.12512982, 0.042867575, 0.08360464, 0.12835538, -0.005067881, 0.02542005, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, -1.0);
result += mat4(0.18997705, 0.086363226, -0.0007131526, 0.19858918, 0.39745626, -0.0090341605, 0.27864447, 0.20052041, 0.010576528, -0.089242525, -0.025109483, -0.030768145, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 0.0);
result += mat4(0.05427315, -0.060894873, 0.06548642, 0.095537595, 0.29116166, -0.16159569, -0.13293959, -0.112566955, 0.0059667625, 0.016041303, 0.03831561, 0.09869594, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 1.0);
result += vec4(0.0113532655, -0.06449327, 0.035503868, 0.5683031);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(S)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_tf
//!SAVE conv2d_1_tf
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.027102098, 0.2640691, 0.1169015, 0.030902913, 0.15404584, 0.1361459, -0.38066056, 0.096569136, -0.111836195, -0.0051853824, -0.0996669, -0.23538585, -0.07060754, -0.18889332, -0.10793357, -0.15154232) * go_0(-1.0, -1.0);
result += mat4(0.1378689, 0.21024452, 0.010976513, 0.0179521, -0.05993059, -0.28364083, 0.24486947, 0.21347582, -0.12522404, -0.16091396, 0.15499291, 0.08353191, -0.03342411, -0.08964405, 0.25111282, -0.07550899) * go_0(-1.0, 0.0);
result += mat4(-0.06398718, 0.05763278, 0.021394925, 0.14780094, -0.033050716, 0.03346528, -0.0846797, 0.0125302235, 0.18018652, 0.24586707, 0.050538495, 0.09879243, -0.100035876, 0.043505374, 0.042692907, -0.08768257) * go_0(-1.0, 1.0);
result += mat4(-0.11572878, 0.0545887, 0.16437739, 0.2775331, 0.10323911, -0.18938646, -0.17097469, -0.188723, 0.085762165, 0.14605838, -0.15568069, -0.16947642, 0.09042493, -0.087587915, -0.041969277, 0.27252352) * go_0(0.0, -1.0);
result += mat4(0.21475963, -0.018211678, -0.5711054, -0.09235345, -0.20367791, -0.23041399, 0.16346097, 0.007901888, 0.12542121, 0.16807431, 0.09862575, 0.16968751, 0.28490388, 0.40945014, -0.22364445, 0.14460565) * go_0(0.0, 0.0);
result += mat4(0.27512726, 0.14046481, -0.17684339, 0.102218024, -0.10503195, 0.3080809, 0.03681373, 0.2668656, -0.093752496, -0.07476867, 0.19900662, 0.06028286, -0.19815882, -0.0584525, 0.027984729, -0.02143819) * go_0(0.0, 1.0);
result += mat4(-0.16829525, -0.06818115, 0.0006509334, 0.01163159, 0.18918815, -0.10731989, -0.008126929, -0.47991323, -0.11022971, 0.19150843, 0.05272113, -0.34417602, 0.022882428, 0.1774031, 0.062597334, -0.09915319) * go_0(1.0, -1.0);
result += mat4(0.32131585, 0.05668815, -0.34203658, 0.05542482, -0.008077225, 0.009148517, 0.10953332, -0.050969962, 0.09904077, 0.46938205, -0.5148919, -0.22275375, -0.10536104, -0.23662373, 0.002147416, -0.14256701) * go_0(1.0, 0.0);
result += mat4(-0.19335353, -0.103732094, 0.17156832, 0.0059756916, -0.118641876, 0.14529023, -0.18662338, 0.0447326, 0.026719248, 0.042491894, 0.026437795, 0.05601309, 0.08645617, 0.08365193, -0.039582565, 0.16612953) * go_0(1.0, 1.0);
result += mat4(-0.014315469, 0.012588422, 0.037587024, 0.08707526, -0.08064868, -0.28149533, 0.27326405, 0.21468583, -0.04278333, 0.29369017, 0.18653142, 0.035729136, 0.079363555, 0.30725953, 0.0147137, 0.08527481) * go_1(-1.0, -1.0);
result += mat4(0.06659263, 0.03452449, -0.33752796, 0.0066543026, 0.48697233, 0.019602561, -0.32033685, -0.20538871, 0.3089118, 0.4315903, -0.13524854, -0.10791581, 0.3315688, 0.13135147, -0.26904663, 0.142365) * go_1(-1.0, 0.0);
result += mat4(0.13619833, 0.045271892, -0.029841429, 0.010704955, -0.29257727, -0.10563375, 0.35345638, -0.06734038, -0.043791633, -0.0056891907, -0.078411415, 0.075443126, -0.05746597, -0.19959894, -0.12797245, 0.18837726) * go_1(-1.0, 1.0);
result += mat4(0.25673476, 0.120482095, -0.23827696, -0.13557845, 0.300447, -0.3008584, -0.13834439, 0.5459493, -0.26155484, 0.06905137, 0.16247983, 0.039960653, -0.023218757, 0.07977591, -0.11354706, -0.25831422) * go_1(0.0, -1.0);
result += mat4(0.0842605, 0.282916, 0.14062001, 0.06356874, 0.55912817, 0.1743876, -0.30324093, 0.052068707, -0.20756413, 0.27321506, -0.26560605, -0.27695876, -0.3927334, -0.5439608, 0.39293098, -0.001130203) * go_1(0.0, 0.0);
result += mat4(-0.021890296, -0.12703396, 0.06660714, -0.03164527, 0.0018722567, -0.26552317, 0.06978973, -0.24030049, 0.46008193, 0.5595346, 0.081981994, -0.038414747, -0.010446991, -0.56102365, -0.079274766, -0.01851302) * go_1(0.0, 1.0);
result += mat4(0.052988984, 0.030581746, -0.06868741, 0.21545182, -0.5706256, -0.0034910638, 0.48361364, 0.9020033, -0.02242781, -0.13256042, 0.08997955, 0.21001706, -0.059571438, -0.040119104, -0.05029196, -0.127414) * go_1(1.0, -1.0);
result += mat4(-0.08275339, -0.05999088, 0.11068767, 0.014646892, -0.041986465, 0.1028236, -0.17218924, 0.026559748, -0.17412743, -0.38364175, 0.17410514, 0.13038695, 0.23155633, 0.2655843, 0.045085523, 0.13005458) * go_1(1.0, 0.0);
result += mat4(-0.013383197, -0.064526096, 0.049046878, 0.015992291, 0.123987064, 0.0104690585, 0.07065378, -0.009824511, -0.036109775, 0.13384768, 0.29676288, -0.39475223, -0.009368096, -0.05666906, -0.09132696, -0.082638375) * go_1(1.0, 1.0);
result += vec4(-0.106538564, -0.065693766, -0.03790106, 0.04776706);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(S)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_1_tf
//!SAVE conv2d_2_tf
//!WIDTH conv2d_1_tf.w
//!HEIGHT conv2d_1_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.024004154, -0.26474997, -0.5256586, 0.051624652, -0.16621786, 0.2964122, 0.6044247, -0.14335106, 0.17002718, -0.2679876, -0.30162668, 0.1273794, -0.17601459, -0.1782376, 0.104725115, -0.16351137) * go_0(-1.0, -1.0);
result += mat4(-0.121676154, 0.047741555, -0.06738679, -0.056402843, 0.004424971, -0.35099635, -0.073440626, 0.039784692, 0.15204315, -0.1165704, 0.11231046, -0.27369732, 0.33737272, -0.11880767, 0.09637475, -0.14709689) * go_0(-1.0, 0.0);
result += mat4(-0.017987821, -0.08798823, -0.062515825, -0.046803873, -0.05871703, 0.27013004, 0.19397618, -0.052147817, 0.003271283, -0.0029015478, -0.07390092, -0.09348337, -0.1574738, 0.06750957, -0.07661155, 0.054327156) * go_0(-1.0, 1.0);
result += mat4(-0.15215784, -0.72508365, -0.3202069, 0.20295432, -0.19125701, 0.021401431, -0.051837035, -0.025939213, 0.25565025, -0.12872623, 0.13169816, 0.27377388, -0.008718429, -0.05864064, 0.028844763, 0.1144993) * go_0(0.0, -1.0);
result += mat4(-0.30012092, -0.1322455, -0.11868545, 0.09857058, 0.082795605, -0.075334676, -0.3752773, -0.02918163, -0.67764, -0.38598236, -0.21023573, 0.38274166, -0.07398165, -0.07213789, -0.28427607, 0.1266569) * go_0(0.0, 0.0);
result += mat4(-0.37507388, 0.18809201, -0.21982779, 0.27208912, 0.022066567, -0.27627763, 0.12345216, -0.30041683, 0.017002959, -0.091398515, -0.25207692, -0.29253414, -0.08231422, -0.14665812, -0.07868529, -0.24562219) * go_0(0.0, 1.0);
result += mat4(0.08686712, 0.080837384, 0.20736577, 0.008233064, 0.14957365, 0.21801959, -0.04870689, 0.42149112, 0.27255878, 0.33320278, -0.08467146, 0.10381615, 0.055278245, 0.085710146, 0.009097151, 0.29092705) * go_0(1.0, -1.0);
result += mat4(0.0012207404, -0.023874281, -0.027035477, 0.005157451, 0.19330226, 0.33711615, -0.16495204, 0.549021, 0.44879642, 0.1978837, -0.20492741, 0.28099406, 0.2631811, 0.40786585, -0.055340275, 0.2575511) * go_0(1.0, 0.0);
result += mat4(0.29127392, -0.06287165, 0.12715077, 0.14784902, -0.3183704, 0.42057636, -0.11483724, -0.3019506, 0.010730576, 0.29091576, -0.046116166, -0.23528357, -0.0037143505, 0.1191774, -0.06084074, 0.011641706) * go_0(1.0, 1.0);
result += mat4(-0.2579205, 0.036545023, 0.11691888, 0.04996418, 0.21318026, 0.21370813, -0.14114271, 0.031217605, -0.06979331, -0.0690704, 0.04618086, 0.025164584, -0.10994228, 0.109930746, 0.103678934, 0.12193115) * go_1(-1.0, -1.0);
result += mat4(-0.19843774, -0.11237926, 0.007291354, 0.16480611, -0.15669724, 0.46283355, 0.077065215, 0.112273656, 0.17143534, -0.19934891, -0.25481275, 0.034591813, -0.27032652, -0.2702769, 0.04816228, -0.031614583) * go_1(-1.0, 0.0);
result += mat4(-0.16307239, -0.11295217, 0.05861256, 0.14225823, -0.015648091, 0.11741865, 0.113366075, 0.023935538, 0.19560932, -0.10553561, -0.042583376, -0.048160724, -0.3116519, 0.13957061, -0.0044852323, -0.015472912) * go_1(-1.0, 1.0);
result += mat4(-0.15629178, 0.06463271, -0.13176678, 0.025518289, -0.021733627, 0.22236359, 0.019508492, -0.11629477, 0.10801276, -0.021957984, -0.11272639, -0.03615053, -0.121420704, 0.2520835, 0.043395765, 0.1699031) * go_1(0.0, -1.0);
result += mat4(0.2886654, 0.21755892, 0.21757497, 0.08442575, -0.109903164, -0.67295986, 0.22886126, -0.027185453, 0.3761606, 0.23199768, 0.05908783, -0.1496158, 0.10832971, -0.3530352, 0.20234483, -0.07615918) * go_1(0.0, 0.0);
result += mat4(0.11043024, 0.18943349, 0.42394367, 0.029350199, -0.15085667, 0.020204183, -0.081609115, 0.07907012, 0.33805525, 0.0066280114, 0.0018284445, 0.022983696, 0.004984607, 0.0429299, -0.14568979, -0.29143327) * go_1(0.0, 1.0);
result += mat4(-0.16376027, -0.20387048, 0.06522074, 0.17484841, -0.13885716, -0.04380927, -0.03535832, -0.16978237, -0.004799155, -0.25407305, -0.039976966, -0.011992087, -0.22535577, -0.09583549, 0.0334331, 0.016292758) * go_1(1.0, -1.0);
result += mat4(-0.38688713, -0.20232083, 0.23887886, -0.10438324, -0.24170811, -0.074868314, 0.03977399, -0.22810821, -0.08257971, -0.11902456, 0.106009185, -0.078289054, -0.11932821, 0.024207884, 0.10070917, 0.79348284) * go_1(1.0, 0.0);
result += mat4(-0.4018743, 0.050456528, 0.035341598, -0.03788609, 0.12964934, -0.44461823, 0.029031694, 0.29604837, -0.102386944, -0.13805065, 0.0055692918, 0.14659804, -0.22499937, 0.14680648, -0.3443954, -0.06994176) * go_1(1.0, 1.0);
result += vec4(-0.0063428865, 0.0057986965, -0.12526293, -0.059240736);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(S)-Conv-3x3x3x8
//!HOOK MAIN
//!BIND MAIN
//!BIND conv2d_2_tf
//!SAVE MAIN
//!WIDTH conv2d_2_tf.w
//!HEIGHT conv2d_2_tf.h
#define go_0(x_off, y_off) (max((conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.08631539, 0.09499331, 0.065609254, 0.0, -0.023760278, -0.027293118, -0.022839671, 0.0, -0.012447854, -0.008565141, -0.012041815, 0.0, -0.033292875, -0.031266093, -0.02874347, 0.0) * go_0(-1.0, -1.0);
result += mat4(0.08709062, 0.09760889, 0.08988583, 0.0, -0.09099671, -0.102120616, -0.098076016, 0.0, 0.057814583, 0.06999608, 0.05961344, 0.0, 0.12246188, 0.1319784, 0.12254915, 0.0) * go_0(-1.0, 0.0);
result += mat4(0.07694916, 0.0822054, 0.07549296, 0.0, -0.046808865, -0.051509347, -0.035890795, 0.0, 0.01599848, 0.014677793, 0.0086143715, 0.0, 0.033142705, 0.0426565, 0.035911378, 0.0) * go_0(-1.0, 1.0);
result += mat4(-0.0008269902, 0.0009082343, 0.014101725, 0.0, 0.0006387551, 0.005079344, -0.013034868, 0.0, 0.013909732, 0.011026747, 0.012485332, 0.0, 0.027028518, 0.022164145, 0.03183532, 0.0) * go_0(0.0, -1.0);
result += mat4(-0.33575395, -0.36700967, -0.34140685, 0.0, 0.35850254, 0.37535715, 0.34613726, 0.0, -0.12680013, -0.1256115, -0.112494245, 0.0, -0.061541136, -0.059120018, -0.06552594, 0.0) * go_0(0.0, 0.0);
result += mat4(-0.047570463, -0.050335366, -0.04665491, 0.0, -0.110970475, -0.12363716, -0.11072252, 0.0, 0.041563414, 0.059771337, 0.045290247, 0.0, -0.17999935, -0.19700716, -0.17459513, 0.0) * go_0(0.0, 1.0);
result += mat4(0.078488424, 0.07483357, 0.08347933, 0.0, -0.0063715233, 0.00035415235, -0.010886946, 0.0, 0.031237155, 0.02512343, 0.034399323, 0.0, -0.023146842, -0.026732154, -0.027644241, 0.0) * go_0(1.0, -1.0);
result += mat4(-0.05906883, -0.06784104, -0.04506148, 0.0, -0.003939601, -0.0011749315, -0.006256036, 0.0, -0.1662408, -0.16871658, -0.16598499, 0.0, 0.051277652, 0.04837499, 0.05120855, 0.0) * go_0(1.0, 0.0);
result += mat4(0.08158806, 0.08674548, 0.07437206, 0.0, -0.05765347, -0.06196418, -0.057311118, 0.0, 0.26747537, 0.2668808, 0.2389857, 0.0, -0.010376844, -0.01690028, -0.008414153, 0.0) * go_0(1.0, 1.0);
result += mat4(0.030539425, 0.02415435, 0.039969034, 0.0, 0.006491679, 0.014436586, 0.005435709, 0.0, -0.0058292216, -0.013982021, -0.011243379, 0.0, 0.025942149, 0.015361476, 0.019134998, 0.0) * go_1(-1.0, -1.0);
result += mat4(-0.06322247, -0.07146787, -0.06673042, 0.0, 0.028702464, 0.039047733, 0.039646607, 0.0, -0.072553575, -0.08046175, -0.07027197, 0.0, -0.1447189, -0.1539398, -0.1466465, 0.0) * go_1(-1.0, 0.0);
result += mat4(-0.046430312, -0.054549117, -0.048076343, 0.0, 0.032971155, 0.02980819, 0.029172963, 0.0, -0.017612953, -0.015100736, -0.01202649, 0.0, -0.026717246, -0.028401854, -0.034548033, 0.0) * go_1(-1.0, 1.0);
result += mat4(-0.0020459262, -0.0008748501, -0.012601956, 0.0, 0.0054226154, 0.008867029, 0.018921215, 0.0, -0.0021330053, -0.0036601655, -0.0022091097, 0.0, -0.08636891, -0.10203159, -0.09741449, 0.0) * go_1(0.0, -1.0);
result += mat4(0.07306159, 0.08245483, 0.06548199, 0.0, -0.1933229, -0.20326294, -0.19189309, 0.0, 0.107496604, 0.11584994, 0.10907522, 0.0, 0.30877885, 0.31297725, 0.30890995, 0.0) * go_1(0.0, 0.0);
result += mat4(0.03192904, 0.035112645, 0.033732817, 0.0, 0.074100636, 0.08349646, 0.06659352, 0.0, -0.1136165, -0.12470947, -0.11192198, 0.0, 0.14465587, 0.16328491, 0.13984151, 0.0) * go_1(0.0, 1.0);
result += mat4(-0.05098033, -0.053096622, -0.05533725, 0.0, 0.0045651463, -0.007682458, 0.0026934785, 0.0, -0.021199327, -0.016210148, -0.030939564, 0.0, -0.031621892, -0.046702545, -0.02647333, 0.0) * go_1(1.0, -1.0);
result += mat4(0.055801813, 0.06430485, 0.05052402, 0.0, 0.0241233, 0.013879883, 0.017344628, 0.0, 0.08707151, 0.10031039, 0.095042154, 0.0, -0.109053336, -0.11414017, -0.111838564, 0.0) * go_1(1.0, 0.0);
result += mat4(0.030582374, 0.03604719, 0.040417343, 0.0, 0.038665913, 0.036998056, 0.030004544, 0.0, 0.09209076, 0.10010001, 0.08389406, 0.0, -0.014655714, -0.0074866647, -0.012227013, 0.0) * go_1(1.0, 1.0);
result += vec4(-0.008303841, -0.008251826, -0.0069884053, 0.0);
return result + MAIN_tex(MAIN_pos);
}
@@ -0,0 +1,873 @@
// MIT License
// Copyright (c) 2019-2021 bloc97
// All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(VL)-Conv-4x3x3x3
//!HOOK MAIN
//!BIND MAIN
//!SAVE conv2d_tf
//!WIDTH MAIN.w
//!HEIGHT MAIN.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (MAIN_texOff(vec2(x_off, y_off)))
vec4 hook() {
vec4 result = mat4(0.14361712, -0.16690509, 0.37253398, -0.45202538, -0.21331833, -0.32675815, -0.33971128, 0.20261937, -0.20606318, -0.215143, -0.079716705, 0.15640882, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, -1.0);
result += mat4(-0.17360486, -0.3435545, 0.08199117, 0.56259036, -0.120246716, 0.24312893, -0.021436244, -0.11864853, 0.19452724, 0.106943935, -0.077393375, -0.3503661, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 0.0);
result += mat4(-0.072465785, 0.2772823, 0.25493625, 0.3098145, -0.115831695, 0.072458096, -0.014782132, -0.15310249, 0.12178311, -0.015555423, -0.2229811, 0.16469522, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 1.0);
result += mat4(-0.18652022, -0.30702665, -0.59921896, 0.079824045, 0.4426619, 0.049343713, 0.44902903, -0.2711445, 0.20470268, -0.029203767, 0.29092675, 0.15562426, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, -1.0);
result += mat4(-0.21041247, 0.48450592, -0.110547826, 0.3842122, 0.5303875, -0.26512837, 0.19846216, 0.045673862, 0.12773214, -0.05117536, -0.03510946, -0.30123934, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 0.0);
result += mat4(0.3186735, 0.052702922, -0.12499774, 0.055628903, -0.16476867, 0.12642322, -0.18314636, 0.018323101, -0.3609603, 0.25649396, 0.3185421, -0.0057759956, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 1.0);
result += mat4(0.16603558, -0.09259665, -0.28760567, -0.14319661, 0.12511417, -0.12551902, -0.00070228375, 0.20914114, -0.22466865, 0.1064727, 0.32598525, -0.08596318, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, -1.0);
result += mat4(-0.03163653, 0.026722813, -0.4361858, -0.21164834, 0.4176763, 0.08203146, 0.35289326, -0.06128859, 0.20506798, -0.07098943, 0.1807802, 0.2658414, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 0.0);
result += mat4(-0.09821681, 0.058886815, 0.39192092, -0.06791861, -0.15682612, 0.09503328, -0.23400265, 0.026475023, -0.08800713, -0.043749645, -0.18024494, -0.08045564, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 1.0);
result += vec4(-0.040999945, 0.075765304, -0.0911532, -0.10705836);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(VL)-Conv-4x3x3x3
//!HOOK MAIN
//!BIND MAIN
//!SAVE conv2d_tf1
//!WIDTH MAIN.w
//!HEIGHT MAIN.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (MAIN_texOff(vec2(x_off, y_off)))
vec4 hook() {
vec4 result = mat4(-0.16406488, -0.2506693, -0.15592022, -0.05529256, -0.3997277, -0.229681, -0.07762124, 0.1843808, 0.07895815, 0.14437248, 0.219114, -0.048090722, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, -1.0);
result += mat4(-0.2150676, 0.09080163, 0.19598733, -0.40578827, -0.33846557, -0.02518622, 0.037079208, 0.20188439, -0.013777575, -0.2369007, -0.30985412, 0.0411912, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 0.0);
result += mat4(0.119948365, 0.23014452, -0.14962277, -0.096262485, 0.09625151, 0.2025487, 0.03933539, 0.12268028, -0.24373281, 0.19730613, 0.11634144, 0.12293635, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 1.0);
result += mat4(0.08030697, -0.40114692, 0.21532272, 0.20222071, 0.073098, -0.004463858, 0.02820587, -0.18861918, -0.20994501, -0.12444653, -0.23178193, -0.13965288, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, -1.0);
result += mat4(0.14150894, 0.14563078, 0.697704, 0.20918849, 0.26776335, -0.34291518, 0.06394055, 0.17925078, 0.4165139, -0.042595536, 0.105312675, 0.231854, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 0.0);
result += mat4(0.024318576, 0.16668217, 0.0729521, -0.7071404, 0.3121693, 0.37295797, -0.015632952, 0.33763757, 0.00706697, 0.10836652, -0.11132417, 0.292844, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 1.0);
result += mat4(-0.14489831, 0.0027769986, -0.24509215, 0.5557927, -0.1104541, 0.005070684, -0.020032275, -0.5642205, 0.16048644, 0.07248175, 0.20387374, -0.38145426, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, -1.0);
result += mat4(0.33140838, -0.007438425, 0.26074782, 0.15947102, 0.219755, -0.14690271, -0.07412696, -0.24176367, -0.2230114, 0.027256912, -0.11255796, -0.05882673, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 0.0);
result += mat4(-0.19712369, 0.003842208, -0.10893768, 0.09047115, -0.10260409, 0.18662766, 0.009733428, 0.0039940844, -0.006444674, -0.15196493, 0.06641555, -0.06169452, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 1.0);
result += vec4(-0.029148052, -0.03215124, -0.6175828, 0.057135154);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_tf
//!BIND conv2d_tf1
//!SAVE conv2d_1_tf
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.05195995, 0.15220909, -0.24354807, -0.109075695, 0.020483498, -0.14830725, 0.0018816335, -0.0072673927, 0.0649385, 0.046050787, -0.10789607, -0.046609525, -0.11455093, -0.009358115, 0.11280759, 0.18053898) * go_0(-1.0, -1.0);
result += mat4(-0.08619698, 0.091353096, -0.16379662, 0.07822936, 0.072919995, 0.1482446, 0.17846228, 0.04639898, -0.18998149, 0.1653338, -0.44187957, -0.010017503, -0.069953404, 0.08784785, -0.16391355, 0.35095468) * go_0(-1.0, 0.0);
result += mat4(0.088297926, 0.27259287, 0.013088447, 0.023461785, 0.10037149, -0.017414214, 0.08559885, -0.10822335, 0.10591637, 0.17240539, 0.15749931, 0.026641782, 0.11889612, -0.018095117, -0.08736018, 0.09934933) * go_0(-1.0, 1.0);
result += mat4(0.21426749, 0.0800268, -0.19816414, 0.07693414, 0.026270509, -0.11724047, 0.026078718, 0.13080709, 0.12207936, 0.056103867, -0.323923, -0.111454345, 0.059245165, 0.07257926, 0.032195322, 0.27225617) * go_0(0.0, -1.0);
result += mat4(-0.20130268, 0.026809234, -0.0020803472, -0.04394057, -0.1982125, -0.033678252, -0.12881789, 0.0025656687, 0.14193355, -0.2541802, -0.13239717, -0.05983356, -0.029376393, 0.33187667, 0.14438996, -0.21993925) * go_0(0.0, 0.0);
result += mat4(-0.12772562, 0.022498213, 0.24753313, 0.07440761, -0.17564529, 0.09971503, -0.013372, 0.09459552, -0.21597451, -0.40116546, 0.23446435, 0.1515452, 0.050813515, 0.19662157, -0.10604596, -0.24638489) * go_0(0.0, 1.0);
result += mat4(0.23866327, -0.2706382, -0.07480157, 0.03789501, 0.117716484, -0.095995456, -0.0435066, 0.013025061, -0.029759895, -0.036287807, 0.08570493, 0.030151363, 0.18863682, -0.27228612, 0.020479294, -0.07058746) * go_0(1.0, -1.0);
result += mat4(0.0026758043, -0.20750894, 0.2802277, -0.07761428, -0.012089615, -0.112726666, -0.03867965, -0.085082226, 0.034227375, 0.19662802, 0.26272395, 0.036822405, -0.23040786, -0.20173554, 0.07110236, -0.26939383) * go_0(1.0, 0.0);
result += mat4(-0.14012688, -0.067249745, 0.14726773, -0.0070919944, 0.19275497, 0.04460783, 0.18776374, -0.019941995, -0.076159865, 0.002261728, 0.238768, 0.039375026, 0.13200568, -0.023286859, 0.034387972, -0.01827453) * go_0(1.0, 1.0);
result += mat4(-0.0107542025, -0.13001555, 0.06596806, -0.03370635, -0.024291076, 0.10367739, -0.03396605, 0.041960735, 0.16230568, 0.024845246, -0.016806586, 0.22547007, -0.025378102, 0.064547986, -0.2113137, 0.042272836) * go_1(-1.0, -1.0);
result += mat4(-0.2219356, -0.049535394, 0.10289468, 0.14175911, 0.013058568, -0.15909089, -0.02546921, 0.11721571, 0.13020545, 0.39660174, -0.07601573, -0.16366366, -0.023935124, 0.06681424, -0.26143414, -0.07485668) * go_1(-1.0, 0.0);
result += mat4(0.1405031, -0.0645044, -0.15865614, 0.1829069, -0.22526503, 0.08991175, 0.041972812, -0.012462953, 0.3022753, 0.19457603, 0.022607598, -0.25460255, 0.028327515, 0.14420614, -0.077984214, 0.09278112) * go_1(-1.0, 1.0);
result += mat4(0.13224132, 0.13115089, -0.188987, -0.19428022, -0.080641985, 0.20909777, 0.067079, -0.19832124, 0.13150498, 0.04450851, -0.2770351, -0.010381239, 0.32295567, 0.04445836, 0.030657565, 0.020271502) * go_1(0.0, -1.0);
result += mat4(-0.08188993, 0.039709873, 0.16059989, -0.13279189, 0.11389818, -0.071865685, 0.09312801, -0.08816363, -0.65844774, -0.6854379, 0.21431407, 0.597198, -0.3734657, -0.116027676, 0.015932929, -0.0653176) * go_1(0.0, 0.0);
result += mat4(0.24136105, 0.21444799, -0.14235207, 0.08445492, 0.017335927, -0.49877876, -0.06224622, 0.1571534, 0.035594277, 0.059829034, 0.087631516, -0.17090686, -0.005452869, 0.13786094, 0.27586326, 0.046760406) * go_1(0.0, 1.0);
result += mat4(0.095078, 0.30310658, 0.010268592, 0.18540539, -0.20722823, -0.0005848572, -0.06464327, -0.111019135, -0.07837157, -0.12183798, -0.09187498, -0.3368629, -0.08216629, -0.20095807, 0.009563313, 0.024838416) * go_1(1.0, -1.0);
result += mat4(0.28712475, 0.0641969, -0.034764312, 0.13600683, -0.09211094, 0.009699817, -0.001104855, -0.026146285, 0.33425868, -0.16132632, 0.18051304, -0.104004376, 0.20768233, 0.0888418, 0.050057285, -0.020228952) * go_1(1.0, 0.0);
result += mat4(0.11642946, -0.021900529, -0.08910504, 0.15492517, -0.19726521, 0.1434987, -0.24708387, 0.006737377, 0.11353539, -0.15897587, -0.029491093, 0.06002862, -0.09640613, -0.11342702, 0.21375169, 0.0062719737) * go_1(1.0, 1.0);
result += mat4(-0.15513068, -0.3151456, 0.20799752, -0.07449935, -0.09226967, 0.112302735, -0.16211908, -0.37986508, -0.27418482, -0.10445544, 0.21112369, -0.06780466, -0.062341, 0.07758948, -0.012719117, -0.16481343) * go_2(-1.0, -1.0);
result += mat4(0.16382848, 0.14490448, -0.012869055, 0.1804095, -0.05304844, -0.14624795, -0.14816979, -0.17435774, 0.25356865, 0.11435022, 0.19412366, 0.19499794, -0.10189348, 0.023880519, 0.16822465, -0.17454338) * go_2(-1.0, 0.0);
result += mat4(0.04854064, 0.11944563, 0.022984248, -0.0852543, -0.0077684796, -0.044182744, -0.02888099, 0.27452356, -0.07887827, -0.15155658, -0.12841311, -0.21202831, -0.18533322, -0.05852455, 0.0761054, -0.22115342) * go_2(-1.0, 1.0);
result += mat4(-0.21520375, 0.11415518, 0.18909843, -0.16420493, -0.20909967, -0.3257246, 0.29332343, -0.029541709, -0.1679851, 0.14073059, 0.32720464, 0.13311239, -0.0021121972, -0.08773544, -0.045532625, 0.36960867) * go_2(0.0, -1.0);
result += mat4(0.58407414, -0.23632582, -0.16739567, 0.264173, 0.09584864, 0.18455075, 0.20051196, -0.04616608, 0.13441175, -0.0055764276, -0.08625195, 0.097847305, 0.19565724, -0.12183587, -0.11488796, 0.2520169) * go_2(0.0, 0.0);
result += mat4(0.01584208, -0.31471413, 0.017104283, 0.0682452, 0.18728764, 0.042960413, 0.06437809, -0.14483811, 0.13882554, 0.016576322, -0.029599546, 0.034904055, -0.20939542, -0.10213055, 0.08821727, 0.0030552552) * go_2(0.0, 1.0);
result += mat4(-0.2973797, 0.15791039, 0.10811437, -0.07947077, -0.26328024, -0.061920475, 0.12498813, 0.100570425, -0.018922925, 0.002256239, -0.094379805, -0.032315314, 0.48677605, -0.04879864, 0.028028104, -0.14557233) * go_2(1.0, -1.0);
result += mat4(0.016148027, 0.13884154, -0.19554809, -0.006344376, -0.013450252, 0.2581758, 0.10643088, 0.23465036, -0.078438915, -0.099644944, -0.1442203, -0.2285087, 0.33528957, -0.17052084, -0.26595074, 0.14794162) * go_2(1.0, 0.0);
result += mat4(0.041404825, -0.0813985, -0.19863169, -0.008302881, 0.023570588, -0.043578386, -0.20971186, 0.14654282, 0.048436746, 0.11266723, -0.25812748, -0.03340969, -0.18430679, -0.046258014, -0.007674466, -0.037139155) * go_2(1.0, 1.0);
result += mat4(-0.060693484, -0.08285047, 0.06638212, 0.18479855, 0.11099276, -0.14470962, 0.16915078, 0.32247669, -0.10845523, 0.0027510398, -0.014941873, -0.15779859, 0.051481526, -0.14748912, 0.12125527, -0.059839584) * go_3(-1.0, -1.0);
result += mat4(0.27571446, 0.01663349, -0.057985745, -0.089736536, -0.09541078, 0.18101417, 0.084854685, 0.11060913, 0.05631825, 0.066835634, -0.02837782, -0.049748126, -0.050051138, -0.05126488, 0.27121767, 0.06331115) * go_3(-1.0, 0.0);
result += mat4(-0.13630085, -0.03787764, 0.13351586, -0.024081819, 0.10403757, -0.0034796793, -0.04838045, -0.064052396, -0.34672704, -0.06271465, -0.024577484, -0.13450806, -0.013759927, 0.11706738, 0.07913658, -0.016639082) * go_3(-1.0, 1.0);
result += mat4(-0.023730129, 0.020174952, 0.048988737, -0.013395666, 0.0073305597, 0.059409764, -0.27721968, 0.13349204, -0.022947624, 0.112007216, -0.008175606, -0.14903043, -0.35755506, -0.02145208, -0.021762518, -0.17889674) * go_3(0.0, -1.0);
result += mat4(0.19315337, 0.16287236, -0.07667863, -0.020898499, -0.021058874, -0.20849414, -0.3571716, -0.13001479, 0.44977963, 0.016706442, -0.03471178, 0.35189477, 0.3050666, -0.019236205, 0.16278796, 0.3093703) * go_3(0.0, 0.0);
result += mat4(-0.1507458, -0.13747548, -0.05822537, 0.16035356, -0.08386089, -0.03476887, -0.0022021863, -0.032772254, 0.17572841, 0.004200287, 0.045312192, 0.27265742, -0.037853006, -0.056344658, -0.3095155, 0.15215549) * go_3(0.0, 1.0);
result += mat4(0.11428048, -0.19523771, 0.016499955, -0.03625986, 0.15670861, -0.077859454, -0.059640404, 0.023970904, -0.009806148, 0.0904747, -0.006978744, 0.15938658, 0.030886533, 0.13507655, -0.002613293, -0.1335748) * go_3(1.0, -1.0);
result += mat4(-0.20070468, 0.06281564, -0.026250493, -0.042895693, -0.06574456, 0.10412931, 0.12061968, -0.0750467, -0.10865931, -0.05715226, -0.022071969, 0.02608941, -0.21416737, -0.18582128, -0.091236554, -0.044943426) * go_3(1.0, 0.0);
result += mat4(-0.057988428, 0.21430638, -0.17991407, -0.051662743, 0.060244065, -0.021494022, -0.018070806, -0.09278776, -0.011404125, 0.064091586, 0.12852973, -0.16610947, 0.08740408, 0.045517463, -0.27932477, 0.11050971) * go_3(1.0, 1.0);
result += vec4(0.012687187, -0.11876551, -0.041985378, -0.10110911);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_tf
//!BIND conv2d_tf1
//!SAVE conv2d_1_tf1
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.07579397, 0.008718031, 0.03874428, -0.022123579, 0.064964466, -0.27502275, -0.0053009577, 0.11669645, 0.007708085, 0.009340055, -0.13001843, -0.03758108, -0.07045759, -0.08749642, -0.21329811, 0.13205966) * go_0(-1.0, -1.0);
result += mat4(-0.14087188, -0.12068241, 0.046639618, 0.05115712, 0.108357444, -0.05040456, 0.03280633, 0.09336891, -0.055509757, -0.036777936, 0.043575723, -0.041975956, -0.17782387, -0.12977566, -0.0736514, 0.17304243) * go_0(-1.0, 0.0);
result += mat4(-0.2638534, 0.0385968, 0.14743716, 0.18057759, -0.036564615, 0.107838504, 0.08324167, 0.13403444, -0.41366392, 0.072824344, -0.013165103, 0.06114856, -0.040475495, -0.14222278, 0.10455181, 0.0021660402) * go_0(-1.0, 1.0);
result += mat4(0.30221993, -0.06315301, 0.057081617, -0.020285107, 0.053984016, 0.13086873, -0.30863532, 0.028010197, 0.0070908144, 0.19940577, -0.013766302, -0.039389495, 0.28064504, 0.05970737, 0.074613005, -0.10217121) * go_0(0.0, -1.0);
result += mat4(0.042094592, -0.1725651, 0.3514404, 0.008126955, 0.08739713, 0.081543595, -0.12912413, 0.0854203, 0.28885832, 0.107783586, 0.22996111, 0.13907135, 0.071920335, -0.15172984, 0.07151959, 0.1406894) * go_0(0.0, 0.0);
result += mat4(-0.1072496, 0.03934067, 0.20014063, 0.051399443, -0.29610988, 0.18659018, -0.17761967, 0.08701774, -0.17493258, -0.08035252, 0.03155133, -0.13986085, 0.023490375, 0.083998375, 0.014006612, 0.03860323) * go_0(0.0, 1.0);
result += mat4(0.09324427, 0.10990628, -0.18758917, 0.0054821614, -0.09425237, 0.1192338, -0.063183226, -0.15047066, 0.15664004, 0.037881903, -0.06762073, 0.09622682, 0.028449943, -0.25338468, -0.18897526, -0.18360007) * go_0(1.0, -1.0);
result += mat4(0.030310342, 0.2083269, -0.04938559, -0.009608655, 0.019751158, 0.12257741, 0.090964966, -0.09864261, 0.058817703, -0.053385522, 0.15931179, -0.10585003, 0.06986225, 0.3435001, -0.33307528, -0.14035752) * go_0(1.0, 0.0);
result += mat4(0.13506691, -0.00015406386, -0.15279713, -0.2290177, 0.019568326, 0.41041428, 0.10566904, -0.08350839, 0.19839814, -0.31052053, -0.04471875, 0.07629561, -0.117245845, 0.19819061, 0.1683647, 0.11896638) * go_0(1.0, 1.0);
result += mat4(0.06920538, 0.2656798, -0.06529862, -0.1695985, -0.21614018, 0.17208195, 0.123307765, -0.061470803, 0.07827313, -0.18543327, 0.0937214, 0.098630935, -0.17667519, -0.01978596, -0.09126346, -0.034487445) * go_1(-1.0, -1.0);
result += mat4(0.030779282, -0.24423946, -0.08623178, 0.1490136, 0.029337894, 0.17548573, -0.05990294, -0.29123273, -0.10020608, -0.3527181, -0.105286725, 0.27502912, -0.25686985, 0.18521136, -0.110095225, -0.07999611) * go_1(-1.0, 0.0);
result += mat4(-0.03266192, 0.045139533, -0.03275437, -0.13748369, 0.15633966, 0.089048125, -0.07592367, -0.09013536, -0.18907873, 0.08265683, -0.069233745, 0.27151683, -0.0647864, -0.15308899, 0.021954, 0.05528693) * go_1(-1.0, 1.0);
result += mat4(0.10284642, -0.14667438, 0.18669777, 0.053000864, -0.12383836, -0.037600834, 0.29438737, 0.04739594, 0.07846367, -0.11676573, -0.048153553, -0.34298027, 0.028358897, 0.119508564, 0.08012271, -0.019992562) * go_1(0.0, -1.0);
result += mat4(-0.22123314, -0.2223458, 0.002969434, -0.07143056, 0.027859585, 0.010600199, 0.056626067, 0.15160584, -0.16350581, -0.044484995, -0.1805076, 0.33351076, 0.073631234, 0.0167081, 0.15704727, 0.107799366) * go_1(0.0, 0.0);
result += mat4(-0.006882137, 0.19744347, 0.041128602, 0.17459555, 0.10376277, -0.12519689, 0.0993647, -0.13044195, 0.10485074, 0.1712284, 0.13369127, 0.24649777, -0.038975652, -0.24550107, 0.19567624, -0.09961197) * go_1(0.0, 1.0);
result += mat4(0.24763626, -0.0902329, 0.21201743, 0.078442305, 0.013261817, -0.019013328, -0.07576136, 0.14993069, -0.24216306, -0.05666454, -0.064632, -0.38150248, 0.14649945, -0.020437164, -0.13821694, -0.026110074) * go_1(1.0, -1.0);
result += mat4(0.21790951, -0.08288076, 0.011415891, -0.1446542, -0.15910968, -0.21221179, -0.06154624, -0.028623452, 0.10872824, 0.17089185, 0.26339474, -0.42544034, 0.095593184, 0.20962211, 0.0034138034, 0.024243662) * go_1(1.0, 0.0);
result += mat4(-0.050784085, 0.06333505, 0.041011192, 0.17474842, 0.14517011, -0.4340653, -0.10313813, 0.12524489, 0.18353751, 0.4589042, -0.037463415, 0.07841999, -0.114173576, -0.10669665, 0.029463472, -0.14393249) * go_1(1.0, 1.0);
result += mat4(0.12771326, -0.06622126, 0.08327681, -0.15113758, -0.114005744, 0.059280578, 0.04071302, -0.11074485, -0.23312584, -0.032968838, 0.13736604, -0.15776984, 0.067029156, 0.0580463, 0.20655325, -0.2112593) * go_2(-1.0, -1.0);
result += mat4(0.16148107, 0.02879793, -0.24918973, 0.009605728, -0.102177374, 0.050518002, -0.00015101423, -0.046602443, 0.5081422, -0.044740383, -0.06243097, 0.076031074, 0.1157983, 0.03965003, 0.109161526, -0.36589798) * go_2(-1.0, 0.0);
result += mat4(-0.018941574, 0.000912917, -0.2585099, 0.13668273, 0.062664494, -0.09246434, -0.14594543, -0.11160076, 0.015663203, -0.02447256, -0.070794076, 0.11807077, 0.12931514, 0.14109722, -0.07506544, -0.012781477) * go_2(-1.0, 1.0);
result += mat4(-0.48816162, 0.16294348, 0.011336221, 0.107038386, -0.01978858, 0.039453425, 0.112853855, 0.007536018, -0.005471479, -0.11315905, 0.032013394, 0.11523904, -0.2504089, 0.04803124, -0.09689627, 0.24372064) * go_2(0.0, -1.0);
result += mat4(0.61343086, 0.09531598, -0.24803302, 0.23788263, 0.13495958, 0.24733612, 0.1575427, -0.06863399, 0.2341275, -0.15821049, -0.165848, 0.0290172, -0.010136783, 0.04415787, -0.2619951, 0.09987892) * go_2(0.0, 0.0);
result += mat4(0.19411229, 0.24528526, -0.250216, -0.33602244, 0.17639299, -0.052413136, 0.122578874, 0.028618507, 0.25713214, 0.22033587, -0.19680484, 0.028938502, -0.083384775, -0.06476429, 0.036840588, -0.14297847) * go_2(0.0, 1.0);
result += mat4(-0.2897587, -0.12176407, 0.19259763, -0.106649496, -0.026704982, -0.036201328, -0.06753124, 0.37967134, -0.20092241, 0.006229027, 0.12085137, -0.09810282, -0.1501556, -0.0099991355, 0.25044358, 0.08538966) * go_2(1.0, -1.0);
result += mat4(-0.11304407, -0.24147832, 0.21644448, -0.035938095, -0.036439262, -0.042730987, -0.04384442, 0.10325233, -0.32405272, -0.11873838, -0.15075137, -0.036929503, -0.10808143, 0.25799102, 0.13749036, 0.5451476) * go_2(1.0, 0.0);
result += mat4(-0.24142508, -0.04895773, 0.09022442, 0.2821465, -0.06298706, -0.1807906, 0.02960867, 0.22310257, -0.1915311, 0.2900501, 0.1670845, -0.080343634, 0.25779882, -0.27144584, -0.23575482, -0.14724477) * go_2(1.0, 1.0);
result += mat4(0.020742219, -0.10571064, -0.0010137435, 0.14439318, 0.32805952, -0.027505733, -0.07111945, 0.07043296, -0.09525604, 0.03175366, -0.14633068, -0.15810682, 0.18050082, 0.08191363, 0.07047039, 0.0018573351) * go_3(-1.0, -1.0);
result += mat4(-0.023874652, 0.14996628, 0.11298528, -0.1508891, -0.052415725, -0.02570088, 0.0055150646, 0.16365297, -0.046594325, 0.18095094, 0.09934885, -0.066233225, 0.2404304, -0.112728044, 0.14004207, 0.11369578) * go_3(-1.0, 0.0);
result += mat4(0.14799033, 0.025304591, 0.031008242, 0.03795376, -0.15800071, -0.043169834, 0.10797239, 0.17129694, 0.09674189, -0.11010672, 0.07283912, -0.11063907, 0.108249694, 0.025199141, 0.09162024, -0.1827302) * go_3(-1.0, 1.0);
result += mat4(-0.08983324, 0.07823903, -0.137839, 0.11909572, 0.11996334, -0.05947995, -0.25459376, -0.18159851, 0.044489045, 0.052461334, 0.13674203, 0.12579007, -0.33665392, -0.07313439, -0.013640538, -0.010538632) * go_3(0.0, -1.0);
result += mat4(0.0884388, -0.10034604, 0.047238693, 0.12025125, -0.16648497, -0.20305477, 0.08240087, -0.17453992, 0.19033237, 0.28438845, -0.32885036, 0.14011146, -0.13389368, -0.012868356, -0.15273216, -0.19119217) * go_3(0.0, 0.0);
result += mat4(0.09196779, -0.13800567, 0.08842335, -0.18658079, 0.17512907, 0.021311145, -0.06347847, -0.13827331, -0.10689703, -0.1707886, -0.15724367, -0.167876, 0.22493233, 0.3070637, -0.035266686, -0.0068385694) * go_3(0.0, 1.0);
result += mat4(-0.2739973, 0.07336105, -0.196827, 0.060224827, 0.05752693, -0.014346674, 0.025412507, -0.27530053, 0.27755278, -0.07631679, -0.053861864, 0.113329165, -0.31025892, -0.012681806, 0.06228483, -0.054306302) * go_3(1.0, -1.0);
result += mat4(-0.16827694, 0.16333361, 0.068389125, 0.24560109, 0.11659498, 0.052896734, -0.020310031, -0.17830387, -0.07551057, -0.01822214, -0.037451357, 0.24607496, -0.2033962, -0.11107965, 0.05005381, 0.13685274) * go_3(1.0, 0.0);
result += mat4(0.13665263, -0.24541081, 0.0012457973, -0.012630116, -0.09559698, 0.17756529, -0.039300505, -0.044217475, -0.22984356, -0.2294885, 0.104534455, -0.04131095, 0.084843494, 0.038027752, -0.106351435, 0.18853655) * go_3(1.0, 1.0);
result += vec4(0.010324113, -0.01262194, 0.0762259, -0.014071781);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_1_tf
//!BIND conv2d_1_tf1
//!SAVE conv2d_2_tf
//!WIDTH conv2d_1_tf.w
//!HEIGHT conv2d_1_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_1_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_1_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.06961854, 0.06914646, 0.120440066, -0.04889646, -0.012870159, 0.01994181, 0.052958567, -0.14740478, -0.0027199117, -0.004924673, 0.10131955, -0.11496505, -0.06742836, 0.08287776, 0.11206167, -0.021625644) * go_0(-1.0, -1.0);
result += mat4(-0.025003597, 0.05389498, 0.14938618, 0.12255602, 0.050963886, 0.16300994, 0.17633909, 0.03229484, 0.2092038, 0.13367431, -0.09538967, 0.1636076, -0.022082182, 0.10898033, 0.0422286, -0.062253885) * go_0(-1.0, 0.0);
result += mat4(-0.0018258828, 0.08333001, 0.002765037, -0.022241322, 0.1628206, 0.14671557, 0.3001151, 0.030986495, 0.05225914, -0.04880372, 0.15963705, 0.17972782, 0.055128947, 0.114626616, 0.03460699, -0.07679627) * go_0(-1.0, 1.0);
result += mat4(-0.08866054, 0.0882386, 0.13833097, -0.079257324, -0.03060485, 0.049487974, 0.092268504, -0.17009564, 0.021603461, 0.20750603, 0.18884364, -0.10977116, 0.31758478, 0.053426504, 0.093257, 0.14912026) * go_0(0.0, -1.0);
result += mat4(0.13069148, 0.21368778, -0.4405162, -0.009193694, 0.090230525, -0.15897161, -0.005089127, -0.06011075, -0.27336648, -0.021869129, -0.2084852, -0.0850094, -0.10896211, 0.27229342, -0.044210993, -0.03346366) * go_0(0.0, 0.0);
result += mat4(0.05807779, 0.08506817, 0.23984064, 0.12547795, 0.036945213, 0.039088245, -0.10716132, -0.15966031, 0.13548918, 0.07746645, -0.248966, -0.15717135, -0.059498273, 0.0088413125, -0.02828682, -0.021795277) * go_0(0.0, 1.0);
result += mat4(0.013289853, 0.007272393, 0.06875863, -0.053158432, -0.03578172, 0.20148727, 0.1961931, -0.16910668, 0.03259818, 0.054221123, -0.0326064, 0.06493197, 0.053533003, -0.11878436, 0.14398894, -0.17543368) * go_0(1.0, -1.0);
result += mat4(-0.17906332, 0.1111989, 0.047910325, 0.11560207, 0.09790123, -0.2023765, 0.04265116, 0.0075303926, 0.012974969, -0.0853146, -0.04037416, 0.14489946, -0.0716403, -0.055603035, -0.30376709, -0.011667526) * go_0(1.0, 0.0);
result += mat4(-0.053314358, -0.012657763, 0.0077033425, 0.12168191, 0.016371705, 0.11979062, -0.08494259, -0.009617431, 0.1303907, 0.043279216, -0.17285421, 0.15823162, -0.030746695, 0.121796146, 0.13097613, 0.0024783302) * go_0(1.0, 1.0);
result += mat4(-0.11677548, -0.06592395, -0.022185773, 0.0031006308, -0.00906918, -0.0006412884, -0.00083743286, 0.083697535, -0.060518038, 0.14058606, 0.122444086, 0.17866874, 0.02376487, -0.06369968, -0.026537767, 0.21466877) * go_1(-1.0, -1.0);
result += mat4(0.12340551, -0.015656117, 0.051990572, 0.04361656, -0.05291406, 0.10119005, 0.17603071, 0.10464767, 0.03288951, 0.091776796, -0.17373918, -0.12871055, 0.10205503, -0.17783496, -0.17020486, -0.09781929) * go_1(-1.0, 0.0);
result += mat4(-0.01845568, -0.008877597, 0.14279746, 0.031775143, 0.041680444, 0.08784194, 0.044564564, -0.0011678484, -0.010219994, 0.10472676, 0.046920944, -0.110975444, -0.1197329, -0.11303071, -0.14893234, -0.091113724) * go_1(-1.0, 1.0);
result += mat4(-0.03856561, -0.12173735, 0.040876064, 0.13847597, -0.14995924, -0.13332345, 0.18687452, -0.22562599, 0.08920785, -0.0017916666, 0.019448435, 0.2306492, -0.054546747, -0.1465318, -0.10628867, -0.0073827514) * go_1(0.0, -1.0);
result += mat4(0.12689775, 0.11765595, 0.13039489, 0.06940679, 0.2672624, -0.03880143, -0.11693099, -0.05516293, -0.09665274, -0.2583138, 0.22954193, -0.19324702, -0.39629623, -0.35457405, 0.10052407, -0.19756024) * go_1(0.0, 0.0);
result += mat4(-0.06307673, -0.096393906, -0.0075868783, -0.25133502, 0.03436604, -0.008201423, 0.06386583, 0.106548436, 0.014626536, 0.03485315, -0.043418273, -0.1141408, 0.005102567, -0.11701804, -0.01645601, -0.057083) * go_1(0.0, 1.0);
result += mat4(-0.019062268, 0.020416953, -0.08854219, -0.037497565, 0.09449262, -0.09127615, -0.063330196, 0.08736769, -0.12394077, -0.17950213, -0.11101161, 0.16013645, -0.09370585, 0.0047447495, -0.04288296, 0.00314098) * go_1(1.0, -1.0);
result += mat4(-0.08263743, -0.14441489, -0.14886282, -0.05694989, 0.4254853, 0.10864832, 0.26322174, -0.042006254, 0.24269578, -0.053833783, -0.11558995, -0.066605136, -0.064816564, -0.25914803, -0.017624624, 0.0402331) * go_1(1.0, 0.0);
result += mat4(-0.100058846, -0.030422715, -0.19600148, -0.13322774, 0.1796998, 0.087852575, 0.07324559, -0.0047889417, 0.007248384, 0.08930289, 0.09643387, -0.0060126656, 0.16357517, -0.06628222, 0.030618697, 0.097391844) * go_1(1.0, 1.0);
result += mat4(0.09539377, -0.10802722, -0.014952347, 0.1683223, -0.03919409, 0.041155327, -0.012186347, -0.030456683, -0.015024977, 0.061710294, 0.00049987395, 0.27338788, 0.04845922, -0.014114694, -0.06371904, 0.008664) * go_2(-1.0, -1.0);
result += mat4(0.063082814, -0.02755945, -0.15663072, -0.053271208, 0.070173115, 0.038125586, -0.11840675, -0.016337764, -0.07963128, -0.06404943, 0.23033784, -0.007848355, -0.04434174, -0.092422634, -0.013985954, -0.038096108) * go_2(-1.0, 0.0);
result += mat4(0.037121523, -0.020622304, 0.086708754, 0.045878958, -0.13188364, -0.022858748, -0.22411314, -0.08116162, 0.048863005, 0.039260563, -0.04934298, 0.11015131, 0.028177079, 0.025245499, 0.1067935, 0.15324049) * go_2(-1.0, 1.0);
result += mat4(0.068235874, -0.14401375, -0.032677606, 0.02996807, -0.11290208, 0.114133574, -0.09627152, 0.053930115, 0.14560424, -0.15935057, -0.13495773, 0.29710987, -0.23231608, 0.14334352, 0.070753984, -0.08189047) * go_2(0.0, -1.0);
result += mat4(-0.22378983, -0.09858718, 0.30114698, -0.0048736916, 0.02198528, 0.21444769, -0.11228022, -0.14812283, 0.092372194, 0.1598949, 0.2534843, 0.4932573, -0.16642319, 0.12972073, -0.04147445, -0.09365905) * go_2(0.0, 0.0);
result += mat4(-0.132199, -0.0798279, -0.18289213, -0.15133642, -0.033057958, 0.007495456, 0.070398286, 0.049111973, -0.03361502, 0.032059964, 0.003850814, 0.22922683, 0.20279214, -0.07350396, 0.27681342, 0.11891455) * go_2(0.0, 1.0);
result += mat4(-0.095355205, -0.08533997, -0.043466177, 0.03183743, 0.0048090555, -0.07969942, -0.044769235, 0.15350139, 0.06485437, -0.027922742, 0.0850892, 0.00069019396, 0.035737295, 0.20380683, 0.03413393, 0.025630401) * go_2(1.0, -1.0);
result += mat4(0.26616514, -0.024066277, 0.09220501, 0.09643391, -0.014585791, 0.22894275, -0.053128377, -0.08719867, -0.08819027, 0.01932318, -0.113633566, -0.15435793, 0.10542983, 0.029819246, 0.33675614, -0.059085276) * go_2(1.0, 0.0);
result += mat4(-0.031325538, 0.040770013, -0.049561024, -0.2095101, -0.09537227, -0.075998954, -0.04323478, -0.05470401, -0.110066876, 0.059249427, -0.042351052, -0.047700178, 0.21932366, -0.12850443, 0.035361454, 0.013699006) * go_2(1.0, 1.0);
result += mat4(-0.08417607, 0.113477044, 0.03574209, 0.007835156, 0.2021717, 0.030678429, 0.19313626, -0.03506592, 0.04233059, -0.08540689, -0.07128929, -0.13245375, -0.08918939, -0.042622462, 0.19011301, -0.18228586) * go_3(-1.0, -1.0);
result += mat4(-0.19981891, -0.16255717, 0.042949058, -0.06921157, 0.279451, -0.11536949, -0.13747527, -0.10020231, -0.013784027, -0.06727259, 0.3556115, 0.08460814, -0.15348805, -0.07692103, -0.018658075, 0.0037634284) * go_3(-1.0, 0.0);
result += mat4(-0.09063814, -0.036312047, 0.13528036, 0.0070792423, 0.11834377, 0.02331524, 0.09386154, 0.07144935, 0.033078104, -0.1397121, 0.09283168, 0.2118868, -0.06313442, 0.032146804, 0.0060367053, 0.005822348) * go_3(-1.0, 1.0);
result += mat4(0.035949346, 0.06469895, -0.0051385965, -0.078584194, 0.43195483, 0.0045206803, -0.24012396, 0.21436183, -0.013394304, -0.04198491, 0.06645506, -0.23869638, -0.02311661, 0.06589808, 0.16800866, -0.21120183) * go_3(0.0, -1.0);
result += mat4(-0.24937367, -0.042277586, 0.08117994, 0.3105402, -0.26087892, -0.10325264, -0.08689298, 0.0064907144, 0.031937066, 0.09783758, -0.9514562, -0.104631096, 0.27990052, 0.36389935, 0.057687905, 0.14072314) * go_3(0.0, 0.0);
result += mat4(-0.19865227, 0.09398578, 0.06911146, 0.13077813, 0.024283953, -0.0036808057, -0.036725305, -0.024085987, 0.061556816, 0.0029027078, 0.24621862, 0.112107046, 0.068239614, 0.052718107, 0.20803368, 0.065064415) * go_3(0.0, 1.0);
result += mat4(-0.055511028, -0.08662344, -0.074801624, -0.021917107, 0.18730342, 0.047116343, 0.14872652, 0.10580926, 0.16962165, 0.16628978, 0.17343876, -0.1697205, 0.047853447, -0.22705628, 0.031780355, -0.09273609) * go_3(1.0, -1.0);
result += mat4(-0.17306295, -0.067308225, -0.17174196, -0.13221754, -0.24622467, 0.029901514, -0.12799668, -0.04145667, -0.14546, 0.013308366, 0.028113116, 0.1678875, 0.07922657, -0.015584258, 0.17059629, 0.07330948) * go_3(1.0, 0.0);
result += mat4(-0.09916512, 0.0623665, -0.022458963, 0.061962493, 0.18569344, -0.06590287, 0.111395456, 0.08477448, -0.03609452, 0.024279302, -0.083497405, 0.06459743, -0.22963138, -0.12262581, 0.006980887, -0.06653474) * go_3(1.0, 1.0);
result += vec4(-0.023354841, 0.0019475977, -0.0705355, -0.08216019);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_1_tf
//!BIND conv2d_1_tf1
//!SAVE conv2d_2_tf1
//!WIDTH conv2d_1_tf.w
//!HEIGHT conv2d_1_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_1_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_1_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.13703531, 0.06135254, -0.05032855, 0.0039429665, -0.05997914, 0.03737832, -0.09703001, -0.08112204, -0.096779875, 0.086732335, 0.03021232, -0.14636067, 0.079296306, 0.006656948, 0.08904937, 0.06196539) * go_0(-1.0, -1.0);
result += mat4(-0.26374274, 0.16698441, -0.08554561, 0.03734819, -0.08525629, 0.12257442, 0.015473835, 0.13266069, 0.008439022, -0.05002345, 0.03232084, 0.17349075, 0.014541135, -0.10353582, 0.13339484, -0.13474584) * go_0(-1.0, 0.0);
result += mat4(0.05637785, -0.049726896, 0.06597188, 0.0058668824, -0.10623723, 0.13441847, 0.015975956, -0.07811197, 0.05975957, -0.062021587, -0.06533749, 0.083735935, 0.02666556, 0.029904561, -0.0102926055, -0.10931666) * go_0(-1.0, 1.0);
result += mat4(-0.22616413, 0.042830274, -0.116208926, -0.053796053, -0.1112898, 0.20703097, -0.34109348, -0.065111674, -0.17255561, 0.16784647, 0.00193431, -0.043237597, -0.02353095, -0.1302526, 0.05119598, 0.01403269) * go_0(0.0, -1.0);
result += mat4(0.086109385, -0.053006437, -0.24992542, 0.007938272, -0.0027849772, 0.09198081, -0.17596659, 0.030577915, -0.31807357, -0.29618275, 0.0056317504, 0.3662508, 0.16753437, -0.12481447, -0.057597708, -0.14973637) * go_0(0.0, 0.0);
result += mat4(-0.14585754, 0.027715279, -0.039035518, 0.11505972, 0.0038059987, -0.20368981, -0.014822689, 0.094012834, -0.20693347, -0.37216228, -0.12690443, 0.2727411, -0.15475404, -0.01948714, -0.12414737, 0.10378582) * go_0(0.0, 1.0);
result += mat4(-0.11750072, 0.051394574, -0.011073509, -0.1100907, -0.1389209, -0.10706716, 0.0017484069, -0.059556484, -0.20038931, 0.24976069, -0.011129469, -0.080446415, 0.19259459, -0.14515446, -0.07275811, 0.039244935) * go_0(1.0, -1.0);
result += mat4(-0.101780266, 0.003889027, 0.010705813, 0.011088775, -0.20406786, -0.009807119, 0.23070864, -0.030722639, -0.012015954, 0.025211284, -0.29246482, 0.04907962, -0.10485314, 0.21213223, 0.15788344, -0.014188987) * go_0(1.0, 0.0);
result += mat4(0.1546438, -0.15895118, 0.010730076, 0.034053337, -0.018741185, -0.008467293, 0.13143812, 0.022905342, -0.27543658, 0.3054419, 0.07025048, 0.29454592, -0.0032350307, 0.01671764, 0.081928045, -0.10051137) * go_0(1.0, 1.0);
result += mat4(-0.014834404, 0.07487839, -0.16554666, -0.04127725, 0.15239598, -0.017607473, 0.09927426, 0.15027349, -0.2073968, 0.041613225, -0.10290223, -0.12565911, 0.022021815, -0.07609557, -0.16338238, 0.04468512) * go_1(-1.0, -1.0);
result += mat4(0.01768976, 0.0637369, 0.006542782, -0.0022799321, -0.14728844, -0.058199093, -0.029928437, 0.079634584, 0.095769696, -0.13526416, 0.20718366, -0.10116214, 0.1688786, -0.08906526, 0.020397741, 0.06541649) * go_1(-1.0, 0.0);
result += mat4(-0.033067044, 0.10095467, -0.13792777, 0.022673525, -0.012797848, -0.11222105, 0.11443862, 0.04893716, 0.11389547, -0.07337629, 0.21447009, -0.032212257, 0.23070163, -0.18156143, 0.14542435, -0.10207653) * go_1(-1.0, 1.0);
result += mat4(-0.22985588, 0.012290226, 0.018557416, -0.064000085, 0.012936774, -0.104329854, -0.0719669, 0.24160251, 0.03716294, -0.093069404, -0.12110873, 0.013251573, -0.12731232, -0.1995954, -0.07679729, 0.06823493) * go_1(0.0, -1.0);
result += mat4(-0.23359679, -0.052702624, -0.08710696, 0.19826421, 0.12880315, 0.19875911, -0.20581602, 0.32980308, -0.14479029, 0.099422045, 0.44737315, 0.13044962, 0.12935589, -0.13621494, 0.14902137, 0.09162335) * go_1(0.0, 0.0);
result += mat4(0.10801082, -0.22644557, 0.035719793, -0.12396268, 0.2906566, 0.119107775, -0.15470679, 0.17997102, -0.12866725, -0.12695445, -0.06832712, 0.017622665, 0.08215481, 0.065239124, -0.1256659, -0.06811625) * go_1(0.0, 1.0);
result += mat4(-0.097956754, 0.09383762, -0.19813508, 0.0035260199, -0.14278924, 0.0660843, 0.19110036, 0.11025648, 0.15489757, 0.011157471, -0.16014035, -0.050144047, 0.0032884583, 0.061513808, -0.03385016, -0.08534137) * go_1(1.0, -1.0);
result += mat4(0.09499595, 0.04162155, -0.26091605, -0.18066265, -0.21523187, -0.036668014, 0.09586408, 0.059850723, -0.10890033, 0.28857672, -0.32993382, 0.05107536, 0.012024929, -0.27968574, 0.15081042, -0.07215633) * go_1(1.0, 0.0);
result += mat4(0.15673614, -0.064684846, -0.13838115, 0.1264376, -0.23772664, 0.11594999, 0.0898036, -0.092647165, 0.26081505, 0.05110054, -0.017965768, 0.06740709, -0.24977967, 0.05645255, -0.08204664, 0.0435078) * go_1(1.0, 1.0);
result += mat4(0.02560865, -0.1613835, 0.05876215, 0.101586774, -0.00058163394, 0.0013674656, 0.039857507, -0.002919488, 0.05573127, -0.04311352, 0.05305971, 0.10097247, 0.036392104, -0.025071293, 0.029137935, -0.08593101) * go_2(-1.0, -1.0);
result += mat4(0.12406646, -0.21399136, 0.05611706, 0.021867402, -0.037916705, 0.05941278, 0.11277805, -0.12387807, 0.008577062, -0.045022104, 0.16465645, -0.07607619, 0.035939474, 0.07221297, -0.13557361, 0.07806311) * go_2(-1.0, 0.0);
result += mat4(-0.19589397, 0.011909766, -0.01258029, -0.065313555, 0.07366803, -0.0812486, 0.115863465, 0.019752543, -0.15854625, 0.11246406, 0.007201303, 0.0008530298, -0.0287012, -0.036224626, 0.059641607, 0.09416462) * go_2(-1.0, 1.0);
result += mat4(0.20361906, -0.20671111, -0.1126041, 0.049152024, 0.17586707, 0.10047246, 0.13149028, -0.16302691, -0.08559989, -0.17756243, -0.0061752857, 0.124775924, 0.020011704, 0.17147969, -0.0003063916, -0.015890911) * go_2(0.0, -1.0);
result += mat4(0.11051906, 0.13774526, 0.29333818, -0.029932505, -0.07021508, 0.046212852, 0.11793092, -0.081830084, -0.18609521, -0.108229816, -0.044969153, -0.041069634, -0.13936938, 0.11356429, 0.19260931, 0.093210496) * go_2(0.0, 0.0);
result += mat4(0.010555152, -0.15726428, -0.13187453, -0.12396212, 0.17309372, 0.100884624, 0.11547714, -0.030650318, -0.21877939, -0.0015167049, -0.090150684, 0.029793834, 0.1465573, -0.038805004, -0.033211514, -0.04926991) * go_2(0.0, 1.0);
result += mat4(0.10250675, -0.030922988, -0.008545946, 0.024706079, 0.105154864, -0.06838902, -0.12627976, 0.032457255, 0.21747419, -0.12865087, -0.056018118, 0.07152061, -0.11214344, -0.029831404, 0.044855718, -0.04316971) * go_2(1.0, -1.0);
result += mat4(0.12806997, 0.12385188, -0.06831653, -0.015933594, 0.08645126, 0.013043054, -0.19599608, -0.060719345, -0.23076192, 0.19181651, 0.1292978, 0.036317572, -0.061692618, -0.25434494, -0.10012762, 0.06366783) * go_2(1.0, 0.0);
result += mat4(-0.11098094, 0.034632366, -0.053560194, 0.08499573, 0.20842391, -0.020262053, -0.023394845, 0.048971336, 0.10436084, 0.12614205, 0.035942093, -0.07592917, -0.07455495, -0.012119416, -0.011834865, 0.21032205) * go_2(1.0, 1.0);
result += mat4(-0.00055114913, -0.06662242, -0.009248925, -0.0024843027, -0.22993802, -0.04828541, -0.08667693, -0.093717255, 0.14400347, 0.030130679, -0.01590651, 0.10399553, 0.14478837, -0.11228224, -0.039653912, -0.042144097) * go_3(-1.0, -1.0);
result += mat4(-0.011044514, -0.09870122, -0.24879128, 0.111903004, 0.092567004, 0.06100228, 0.0053522107, 0.065252475, -0.18228072, 0.25602147, -0.2863954, 0.103064165, 0.052214783, -0.017557586, -0.07434391, 0.021111684) * go_3(-1.0, 0.0);
result += mat4(0.04537496, -0.024985183, -0.15247425, -0.0009907635, -0.09677889, 0.09858206, -0.030702371, 0.03539458, -0.029408665, 0.24335481, -0.1918429, 0.08056781, 0.1548214, 0.2850923, -0.15131058, -0.052048493) * go_3(-1.0, 1.0);
result += mat4(0.055409238, -0.13090813, -0.016612396, -0.019183576, -0.18499215, -0.013184845, 0.038750056, 0.10953814, -0.18437819, 0.19183092, -0.09780726, -0.046532292, -0.10841146, -0.17717329, -0.1731886, -0.06741823) * go_3(0.0, -1.0);
result += mat4(0.27919188, -0.14904179, 0.22850563, -0.17785722, -0.32835802, -0.19134615, 0.32093298, 0.24667856, 0.51687604, -0.59745705, 0.23057328, -0.41411245, -0.4234339, -0.03083826, -0.13972719, 0.1729651) * go_3(0.0, 0.0);
result += mat4(0.042352367, -0.109207705, -0.31047532, 0.08896513, -0.2187999, -0.117951825, 0.060705405, -0.10287316, 0.013815159, -0.023699438, -0.053614594, 0.09065406, -0.15286967, -0.101803675, 0.019537682, 0.12476822) * go_3(0.0, 1.0);
result += mat4(0.0016159728, 0.04094818, 0.012745902, -0.051958837, 0.014557628, 0.00061195926, -0.11669799, 0.08763203, -0.27820277, 0.17871988, 0.10634548, 0.05234229, 0.03827577, -0.3117398, 0.027675012, 0.0655132) * go_3(1.0, -1.0);
result += mat4(-0.0025006514, -0.1457415, 0.053443488, -0.0050932285, 0.01582735, 0.18783967, -0.066718, -0.15485887, -0.039741408, -0.21280284, 0.1502977, 0.09507925, 0.17178543, -0.014238171, -0.35757875, 0.026410697) * go_3(1.0, 0.0);
result += mat4(-0.19434428, -0.079038315, -0.017264817, -0.04004242, 0.0063378955, 0.027904915, 0.02571677, 0.09895997, -0.036605608, -0.19889063, 0.015920812, -0.014095519, 0.4363826, -0.14143194, 0.015463533, -0.1656284) * go_3(1.0, 1.0);
result += vec4(0.08523788, 0.052322272, 0.08955637, -0.06945023);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_2_tf
//!BIND conv2d_2_tf1
//!SAVE conv2d_3_tf
//!WIDTH conv2d_2_tf.w
//!HEIGHT conv2d_2_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_2_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_2_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.048841953, -0.010713874, 0.09238948, -0.0789676, -0.093295254, 0.063662216, -0.023454266, -0.06739832, 0.027439933, 0.007399632, -0.03550259, -0.013834889, 0.17168441, 0.06177229, 0.023950668, 0.14574073) * go_0(-1.0, -1.0);
result += mat4(0.117296845, -0.07858486, -0.02099164, -0.024150673, -0.11662526, -0.26440877, -0.05449493, -0.13366842, -0.06870016, 0.12457937, 0.25052628, 0.013982828, 0.15127566, -0.031653196, -0.13851896, 0.04148151) * go_0(-1.0, 0.0);
result += mat4(0.024360385, -0.31051615, 0.012448293, -0.11265428, 0.06123606, -0.0701936, 0.033618104, -0.064061284, -0.06969811, -0.108838804, 0.014163671, 0.02596177, 0.20071186, -0.0028744373, 0.13663651, -0.05592813) * go_0(-1.0, 1.0);
result += mat4(0.13492568, -0.0726796, 0.13431883, -0.085713945, 0.056370113, 0.115660414, -0.14475793, 0.0044200714, 0.027387753, 0.045452334, 0.28178552, 0.017371183, 0.17304336, 0.0582999, 0.14465337, 0.046005037) * go_0(0.0, -1.0);
result += mat4(0.064034574, 0.041531377, 0.08218889, -0.44529077, -0.010563538, -0.14926371, 0.051012456, 0.08209141, 0.24089444, -0.225398, -0.22259372, -0.26353076, -0.1687418, -0.11501685, -0.016655196, -0.09882357) * go_0(0.0, 0.0);
result += mat4(-0.019985389, -0.19189276, -0.104917, -0.11139956, -0.08406414, 0.031484302, -0.082132496, 0.025829919, 0.07512055, 0.31116992, 0.061163265, -0.074850895, -0.091695994, -0.26492774, -0.06617365, 0.06590624) * go_0(0.0, 1.0);
result += mat4(0.1326703, 0.13008863, -0.1659525, -0.058325157, -0.047528613, 0.06777741, 0.06953616, 0.010587038, 0.031675722, -0.08119788, -0.11269768, -0.06225964, -0.26593694, 0.03627298, 0.12866129, 0.17876588) * go_0(1.0, -1.0);
result += mat4(-0.29016155, -0.12549841, -0.050858997, -0.088932805, 0.002237332, 0.01287246, 0.30138868, -0.071756564, -0.061206467, -0.11114371, -0.25731218, -0.11551616, -0.069513, -0.004583348, -0.10647163, 0.01981785) * go_0(1.0, 0.0);
result += mat4(0.16387528, 0.03450354, 0.03422023, -0.014030813, 0.13418834, -0.010909722, -0.00447121, -0.03082622, -0.23983373, -0.020655053, -0.054034587, -0.07133469, 0.21171515, 0.06268651, -0.1738516, -0.15001713) * go_0(1.0, 1.0);
result += mat4(0.040721033, -0.037582736, -0.13819644, -0.123978324, 0.1650318, 0.033942625, 0.17534302, 0.06452234, 0.18384823, 0.0048657497, 0.20220642, -0.0025760103, 0.011163899, 0.027265374, -0.051284578, 0.19202651) * go_1(-1.0, -1.0);
result += mat4(-0.057493486, -0.031516504, 0.10835143, -0.040618125, -0.07762303, -0.06787725, 0.025559613, -0.0055560498, -0.0017830619, 0.020185964, -0.06656476, -0.008523214, 0.32331157, -0.21633361, 0.15338033, -0.104042485) * go_1(-1.0, 0.0);
result += mat4(-0.18544987, -0.090446, -0.26797467, -0.082941435, -0.15003708, -0.11446041, -0.0394892, 1.1379096e-05, 0.04978554, 0.3350256, 0.032780237, 0.034625802, 0.0596261, 0.045886245, 0.009002243, 0.04746998) * go_1(-1.0, 1.0);
result += mat4(-0.17104147, 0.0054165213, 0.09161088, -0.0673989, -0.119282715, -0.09094731, 0.47243354, 0.09914267, -0.13958418, -0.0050379517, 0.14352496, 0.18380567, -0.16128838, 0.08766813, 0.013876981, -0.09808636) * go_1(0.0, -1.0);
result += mat4(0.09617889, 0.045525175, -0.2550057, -0.02874332, 0.2743444, -0.20102581, 0.008461914, 0.16626629, -0.13309516, -0.19307104, 0.15780488, 0.15518525, -0.2790243, 0.056782067, 0.16836968, 0.17771688) * go_1(0.0, 0.0);
result += mat4(-0.10694667, 0.14490083, -0.037976455, 0.013456577, -0.1166783, 0.060722847, 0.07323464, -0.013812333, 0.03234213, 0.50859296, -0.20670377, -0.019631205, -0.022543924, 0.21776745, -0.093769215, 0.12193299) * go_1(0.0, 1.0);
result += mat4(-0.15260598, -0.04798592, -0.02370747, -0.005714705, 0.030857049, -0.16643822, 0.23971851, 0.08117996, -0.069645695, -0.06674784, 0.033509918, 0.06333286, 0.14010383, 0.02218942, -0.036704093, 0.043163314) * go_1(1.0, -1.0);
result += mat4(0.14653306, 0.002759894, 0.10548246, 0.24976018, 0.3212893, -0.07108953, 0.14068738, 0.29437128, -0.020556152, -0.17813908, 0.1989112, 0.12182122, -0.19231579, 0.06547012, -0.032785345, 0.089717634) * go_1(1.0, 0.0);
result += mat4(-0.23632105, -0.027022298, 0.00586518, 0.01836479, -0.2854795, -0.035417695, -0.07586866, 0.0715673, 0.17984483, 0.11210451, 0.032767817, 0.097993985, -0.010899036, 0.15933803, 0.05454052, 0.06768528) * go_1(1.0, 1.0);
result += mat4(-0.017289463, -0.058823984, 0.0807603, 0.32464716, 0.2756627, 0.036061637, -0.034578573, -0.08811335, 0.031841308, 0.11359879, 0.07553143, -0.028648997, 0.057192322, 0.07769366, -0.1998847, -0.06258051) * go_2(-1.0, -1.0);
result += mat4(0.0422091, 0.046305113, 0.028377453, -0.031071126, 0.06866086, 0.1538135, -0.009288249, -0.25543538, 0.07067607, -0.114061736, -0.024740022, -0.11824987, -0.17426041, 0.0028396242, 0.12849464, 0.057790644) * go_2(-1.0, 0.0);
result += mat4(0.057328146, 0.030677445, 0.07496485, 0.07847613, -0.22358766, -0.15659446, -0.18270054, -0.21316889, 0.084770195, 0.013863274, -0.001335942, -0.04027535, -0.15230416, -0.048156176, -0.04614562, 0.089494966) * go_2(-1.0, 1.0);
result += mat4(-0.117369525, 0.026577681, -0.1941765, 0.14904885, -0.16210394, -0.19549404, 0.19999947, 0.37138188, 0.14809363, -0.05078633, -0.092692114, -0.08533522, 0.12769112, 0.017061725, 0.104464866, -0.026744602) * go_2(0.0, -1.0);
result += mat4(0.0880251, -0.005333869, -0.10327546, 0.30419552, 0.107773595, 0.02335926, -0.19014318, 0.19670166, -0.09443473, 0.10621109, 0.36843884, 0.13197622, 0.24537645, 0.4032842, 0.21791221, 0.08400414) * go_2(0.0, 0.0);
result += mat4(0.06408587, 0.15366535, 0.042582024, 0.15629277, 0.028716238, -0.013479061, -0.23052843, -0.2992272, -0.050045617, -0.27255702, -0.038093377, 0.0031149297, -0.05625518, 0.52598304, -0.0845234, -0.09116851) * go_2(0.0, 1.0);
result += mat4(0.02294159, -0.011902539, 0.00079296535, 0.030631313, 0.02114366, 0.082455896, 0.09450867, -0.08027284, 0.042443607, 0.15427661, 0.11882799, -0.040319934, 0.23706424, -0.107808165, -0.1730313, -0.06340064) * go_2(1.0, -1.0);
result += mat4(0.2645207, 0.002157867, -0.095794424, 0.1141035, 0.08255855, -0.06977906, -0.04348005, 0.27864936, -0.1197219, 0.015997604, 0.09500464, -0.0010631803, 0.07198933, -0.053128377, 0.02176274, -0.001298847) * go_2(1.0, 0.0);
result += mat4(-0.045475803, 0.03626341, -0.00891833, 0.17907676, -0.2810277, 0.13725498, -0.02413441, -0.08605496, 0.08306595, -0.012227401, -0.0070282067, -0.019027572, -0.13443586, -0.041331865, 0.029120144, -0.00490357) * go_2(1.0, 1.0);
result += mat4(-0.13398282, 0.06475972, 0.2528711, 0.02553969, -0.13428321, -0.03931247, 0.11360386, -0.18912545, -0.3725821, -0.018747944, -0.20893294, -0.012743096, 0.07444533, -0.15381604, 0.29776138, 0.10601149) * go_3(-1.0, -1.0);
result += mat4(-0.21793252, 0.07817356, -0.109576665, 0.19185133, -0.072846025, 0.04960289, -0.07506936, 0.12839878, -0.0061091883, 0.093669325, 0.009295678, 0.03780657, -0.10901407, 0.1375137, -0.0745914, 0.1468883) * go_3(-1.0, 0.0);
result += mat4(0.10739044, 0.30611086, 0.1585515, 0.07903283, 0.05612715, -0.0061900485, 0.13646163, 0.15230569, 0.036846787, -0.15846778, -0.18765065, 0.06611226, -0.07209187, 0.056037188, 0.04302953, -0.03887873) * go_3(-1.0, 1.0);
result += mat4(0.05618538, -0.072312586, -0.018046018, 0.049542785, -0.033638306, -0.035169322, -0.25882784, -0.036425237, 0.43763217, -0.07049093, 0.08085481, 0.013634128, -0.2701461, -0.13007875, 0.09603447, 0.2479431) * go_3(0.0, -1.0);
result += mat4(-0.02283992, -0.24593964, 0.04616348, 0.023422526, -0.20994014, 0.064769074, -0.07680045, -0.30547765, 0.1518723, 0.31953967, -0.12841515, -0.19525428, -0.0076093865, -0.112106465, -0.04573789, -0.04834478) * go_3(0.0, 0.0);
result += mat4(-0.008045419, -0.20285496, 0.15290824, 0.036240693, 0.11959966, -0.15712506, 0.096806675, 0.008780234, -0.19716795, -0.3824029, 0.1376541, 0.13325086, -0.103316806, -0.31788048, -0.071698256, -0.25901568) * go_3(0.0, 1.0);
result += mat4(0.13714787, 0.020738773, 0.13716534, 0.12359137, -0.038154524, 0.053202964, -0.12023912, 0.09011213, -0.012448548, -0.026505312, -0.11293235, 0.10613704, -0.39916727, 0.041521315, 0.10659441, 0.027749784) * go_3(1.0, -1.0);
result += mat4(-0.26475835, 0.044597875, -0.31229413, -0.17121075, -0.21795374, -0.009583571, -0.13428004, -0.30734754, -0.017038794, 0.113667324, -0.1516075, 0.06525228, -0.13789397, -0.05770066, -0.016166758, -0.29457557) * go_3(1.0, 0.0);
result += mat4(0.054183286, 0.022085225, 0.086794585, 0.10968018, 0.1276148, 0.05739452, 0.08860957, -0.08131373, -0.081570424, -0.107991874, -0.03724999, 0.000843539, 0.20231429, -0.123543546, -0.19073018, -0.28328305) * go_3(1.0, 1.0);
result += vec4(0.013646388, -0.021442367, 0.0045393505, -0.037433166);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_2_tf
//!BIND conv2d_2_tf1
//!SAVE conv2d_3_tf1
//!WIDTH conv2d_2_tf.w
//!HEIGHT conv2d_2_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_2_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_2_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.13948695, 0.016643738, 0.08168136, 0.02315663, 0.017184775, 0.11487715, 0.05770107, 0.010102888, 0.04955321, -0.045132335, -0.05731744, -0.05798246, 0.2245112, 0.17406365, 0.08979801, -0.10607952) * go_0(-1.0, -1.0);
result += mat4(0.2812785, 0.022830509, 0.15164222, 0.13460225, 0.22263442, 0.2558749, -0.122489706, 0.10409658, 0.023308244, -0.19583783, -0.007824269, 0.06256542, 0.11161938, 0.14878923, 0.30865005, 0.08962341) * go_0(-1.0, 0.0);
result += mat4(-0.20843887, 0.012371968, -0.008279775, -0.042467568, -0.13022369, 0.056743186, -0.018389069, 0.13964763, -0.03361555, -0.053087234, 0.012521351, 0.0209293, 0.015771557, 0.11718523, 0.010176676, 0.021708367) * go_0(-1.0, 1.0);
result += mat4(-0.14373007, -0.114338934, -0.09077395, -0.11040866, 0.055298284, 0.022516333, 0.18901019, -0.05640152, -0.1413198, -0.08748339, -0.029985962, 0.00712751, -0.071436934, -0.18909407, 0.173448, 0.053675048) * go_0(0.0, -1.0);
result += mat4(-0.023129769, 0.42883545, -0.18110612, 0.24296297, -0.02441117, 0.18108079, -0.12298153, -0.19192219, -0.14139178, -0.069563635, 0.1524624, -0.17755614, -0.248875, 0.015161957, -0.16541803, -0.17773613) * go_0(0.0, 0.0);
result += mat4(-0.065477535, -0.113195814, -0.08284894, 0.11679537, 0.028445985, -0.026559185, -0.007267581, 0.14052133, 0.14847197, -0.040276285, -0.038166475, -0.030452784, -0.15184602, -0.22223297, 0.113732725, 0.11163395) * go_0(0.0, 1.0);
result += mat4(0.04990171, 0.08493333, 0.08668171, 0.14610586, -0.010766879, -0.05690133, 0.10706113, 0.13667485, 0.044783257, 0.029695645, -0.101674624, -0.02023205, 0.031889528, 0.14293797, 0.08712652, 0.08716896) * go_0(1.0, -1.0);
result += mat4(-0.21387868, -0.21650635, 0.2743992, -0.048781313, -0.027735803, -0.1543507, 0.11343657, -0.18251626, 0.15225998, 0.13158897, -0.41056108, 0.102582805, -0.09181491, -0.0042975787, 0.056065407, -0.16961528) * go_0(1.0, 0.0);
result += mat4(0.08966051, 0.09331515, -0.085415326, -0.022695992, 0.009771476, -0.07143986, 0.0590329, 0.07347928, -0.09033658, -0.06805735, -0.20129825, 0.017873045, 0.16908158, 0.014213783, 0.112663984, 0.10048714) * go_0(1.0, 1.0);
result += mat4(0.115590535, 0.08364541, 0.00864431, -0.094349444, -0.11073411, 0.05337711, 0.055587426, 0.12131219, -0.04710173, -0.046455074, 0.110379905, 0.25445566, 0.15154606, 0.04483541, 0.08708686, 0.113456205) * go_1(-1.0, -1.0);
result += mat4(-0.014296297, 0.24858733, 0.05035193, -0.09225393, 0.034625243, 0.06219943, 0.19825043, 0.04673499, -0.4083363, -0.39954248, -0.08299408, 0.048756655, 0.09862206, 0.01588621, 0.0070629907, 0.04173666) * go_1(-1.0, 0.0);
result += mat4(0.17356622, 0.1484559, -0.10054033, 0.013332302, 0.15200937, 0.08985606, -0.031668343, -0.026007611, -0.16339104, 0.054744486, 0.07386605, -0.033910174, -0.0018002358, -0.02968911, 0.054931052, 0.09970459) * go_1(-1.0, 1.0);
result += mat4(-0.07330346, 0.05938635, 0.01911963, -0.09856661, -0.081916444, -0.046957035, -0.043849826, 0.09572135, -0.13621825, 0.034347896, -0.21189907, 0.10592239, -0.060592845, 0.09957844, 0.050621815, -0.07447668) * go_1(0.0, -1.0);
result += mat4(0.044731334, -0.13406886, -0.04138754, -0.06764551, -0.018899845, 0.35320804, -0.10959127, 0.17435175, -0.17941645, -0.30889434, 0.10573405, 0.0319751, -0.15677677, 0.08164649, 0.16559398, -0.08152387) * go_1(0.0, 0.0);
result += mat4(0.057760764, -0.12145107, 0.06889264, -0.30627275, 0.011501002, -0.080296256, -0.18067095, 0.10592384, 0.12884894, -0.18973115, 0.18740658, 0.28362688, 0.12934786, -0.010292026, 0.0559999, 0.079962276) * go_1(0.0, 1.0);
result += mat4(0.048659086, -0.006250348, -0.041242067, -0.12078197, -0.07152629, 0.05699244, 0.0011704164, -0.023007339, 0.07814492, 0.02546712, -0.08957218, -0.036925297, -0.03383498, 0.12583385, 0.12207602, 0.03910942) * go_1(1.0, -1.0);
result += mat4(0.26151723, 0.23277281, -0.021892069, 0.052827276, 0.18268764, 0.28595275, -0.20529993, 0.19892794, 0.0038986763, 0.114547804, -0.020574905, 0.02405073, 0.11713121, 0.04491106, -0.07557327, 0.014374293) * go_1(1.0, 0.0);
result += mat4(-0.14276731, -0.06600894, -0.029757235, -0.099975966, 0.023050314, -0.07662015, -0.11542214, 0.087981045, 0.070319094, 0.12462511, 0.008152087, 0.12613884, -0.07071591, 0.0063393894, 0.08699723, -0.0242523) * go_1(1.0, 1.0);
result += mat4(0.035586607, -0.26826563, -0.10145326, -0.002177148, 0.022144236, -0.117452875, 0.021346297, 0.051908135, -0.022425706, 0.067299, 0.09406446, 0.078294896, 0.014900606, -0.05468236, 0.07241715, 0.061000507) * go_2(-1.0, -1.0);
result += mat4(-0.184133, 0.06229474, -0.13819578, -0.025011744, -0.01868356, -0.18940887, 0.092631504, -0.092806384, 0.0035951615, 0.11777577, 0.028149817, 0.0049419673, 0.22230826, 0.06337655, -0.20004818, -0.20937593) * go_2(-1.0, 0.0);
result += mat4(0.13852163, -0.094492316, -0.040309057, 0.10771662, 0.18963522, 0.08687606, -0.20030232, -0.082126215, 0.012181411, 0.044306785, -0.036970526, 0.04403363, 0.07911973, 0.0021176056, 0.26944208, -0.06657045) * go_2(-1.0, 1.0);
result += mat4(0.027229607, 0.12410596, 0.04348171, 0.0019921176, 0.088246435, -0.02828269, -0.26499373, -0.12566662, 0.025947344, -0.0078000715, 0.058063716, -0.0032702687, 0.0059978673, -0.04860002, 0.027650384, -0.23394564) * go_2(0.0, -1.0);
result += mat4(0.07892762, -0.13300626, 0.46678603, -0.033239357, -0.12306804, -0.079602, 0.20534003, 0.23873802, -0.035643574, 0.059950788, -0.26559883, 0.12206408, 0.25408483, 0.029933078, 0.32081822, 0.033947676) * go_2(0.0, 0.0);
result += mat4(-0.06847802, -0.017930118, -0.12299636, -0.12987946, 0.09267518, -0.0009083275, -0.035390552, -0.15379669, -0.1132433, -0.036670692, -0.08342377, 0.015636675, 0.022590527, 0.10533322, 0.0389949, -0.059033744) * go_2(0.0, 1.0);
result += mat4(-0.041753534, -0.014428097, 0.06999257, -3.546234e-05, -0.033465035, -0.040709455, 0.13118082, -0.21016484, -0.07846085, -0.030885663, 0.06934681, 0.12725256, -0.023784902, -0.13373604, -0.015261479, 0.05234782) * go_2(1.0, -1.0);
result += mat4(0.13798563, 0.12757827, -0.26978776, 0.102494285, 0.13285922, 0.35432795, -0.11997128, 0.17108068, -0.12235328, -0.24582328, 0.26962712, -0.086760186, 0.010127441, 0.08048835, 0.047505867, 0.19991067) * go_2(1.0, 0.0);
result += mat4(0.03584222, -0.13433793, -0.044629525, -0.0010440781, -0.0033084434, -0.026725832, -0.05386642, -0.13612603, 0.10066015, 0.10499841, 0.031767137, -0.04550841, -0.09391546, 0.1454157, -0.26962402, 0.21015608) * go_2(1.0, 1.0);
result += mat4(-0.21956864, -0.13502425, -0.02126954, 0.059263993, -0.13461533, -0.04001395, -0.0924258, -0.069165014, 0.22019973, 0.003270619, 0.022072528, -0.14173602, 0.0028843523, -0.13784003, -0.061057515, -0.0049253837) * go_3(-1.0, -1.0);
result += mat4(-0.0011410525, -0.16098002, -0.12883134, 0.018262507, 0.001481578, 0.19514659, -0.13703239, 0.096059754, 0.34194204, 0.13983466, 0.14021507, 0.011405113, -0.11303146, -0.17050214, -0.06992079, -0.05566986) * go_3(-1.0, 0.0);
result += mat4(-0.12307941, -0.02192472, 0.13193923, -0.061640862, -0.16841564, -0.0822524, 0.10141759, 0.02139286, 0.1599039, -0.050632223, 0.16702358, 0.111514546, 0.02397393, 0.037606515, 0.017971672, -0.048641708) * go_3(-1.0, 1.0);
result += mat4(-0.02697617, -0.08579184, -0.28045088, 0.05262136, -0.059576314, 0.107535526, -0.06188862, 0.0010509328, -0.18178311, -0.17288832, 0.20703638, 0.083048366, 0.03859681, -0.07548898, 0.011605782, -0.021842534) * go_3(0.0, -1.0);
result += mat4(0.13198483, 0.37200937, -0.0896539, 0.12450637, 0.037202634, 0.035985112, 0.16579124, -0.08967905, -0.24341385, 0.32482424, -0.3037812, -0.007154969, -0.007152382, -0.017435173, 0.12662841, -0.090513505) * go_3(0.0, 0.0);
result += mat4(-0.014726027, 0.08394915, -0.02100581, 0.24882795, -0.023793869, -0.006450114, 0.17093314, -0.06994153, -0.08689907, 0.113542505, -0.053211495, -0.1780173, 0.030043352, 0.2500714, -0.026940798, -0.0069258413) * go_3(0.0, 1.0);
result += mat4(0.037078895, -0.03033529, -0.066851325, 0.14718252, 0.066372745, 0.028897487, -0.036055963, 0.035399746, 0.06733992, 0.21021596, -0.18314466, -0.027192699, 0.020213274, -0.17751546, -0.050674338, -0.09382659) * go_3(1.0, -1.0);
result += mat4(-0.14761917, -0.22166072, 0.033172436, -0.21982265, -0.09172891, -0.20794454, 0.1738752, -0.13685037, 0.10981111, -0.23169234, 0.053787973, 0.12001196, -0.038242023, -0.047124114, 0.22503005, 0.1015142) * go_3(1.0, 0.0);
result += mat4(0.021231879, -0.015423476, 0.058986407, 0.032002006, -0.029305007, 0.008933183, 0.10777483, -0.112574644, -0.023935415, -0.06604598, 0.053859934, -0.08354717, 0.13703763, -0.078382134, 0.12914242, -0.022056468) * go_3(1.0, 1.0);
result += vec4(-0.002022359, -0.007333954, -0.038140967, -0.03819673);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_3_tf
//!BIND conv2d_3_tf1
//!SAVE conv2d_4_tf
//!WIDTH conv2d_3_tf.w
//!HEIGHT conv2d_3_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_3_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_3_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.050738923, 0.15003614, -0.18880141, 0.16791905, 0.16549185, -0.26726744, -0.12813666, -0.021510791, 0.070805945, 0.043350577, 0.0035756908, 0.11776675, -0.01824196, 0.12618026, 0.07424072, 0.032886628) * go_0(-1.0, -1.0);
result += mat4(-0.11678059, 0.0565686, 0.04392921, -0.27621672, 0.2116695, 0.038044345, -0.015018062, -0.028636303, 0.049744565, -0.12935996, 0.027176194, -0.13208814, -0.21195693, 0.08980974, 0.013893243, -0.018403184) * go_0(-1.0, 0.0);
result += mat4(0.3214697, -0.03143518, 0.19927292, 0.12566878, 0.16190828, 0.11784847, 0.09943727, 0.11755882, 0.017959306, -0.064603634, -0.14054321, -0.11917774, 0.0056958874, 0.06461699, 0.104604125, 0.021947173) * go_0(-1.0, 1.0);
result += mat4(-0.24738057, -0.034892898, -0.03364674, 0.017340986, 0.02933764, -0.08090866, -0.034651175, -0.17391174, 0.08536477, -0.17446008, 0.22706915, -0.10555482, 0.0877744, 0.0681237, -0.035909466, -0.10355238) * go_0(0.0, -1.0);
result += mat4(-0.090646185, -0.12971672, -0.14531808, -0.060838025, 0.24902023, 0.1310588, 0.18602785, 0.21283495, -0.32160765, -0.070119165, -0.10350057, 0.19260244, -0.2610542, -0.3030521, 0.08432348, -0.22286619) * go_0(0.0, 0.0);
result += mat4(0.28333843, -0.053968847, 0.08344997, 0.19987041, 0.22163449, 0.22161576, 0.0030572868, 0.10848695, -0.20529847, 0.08406883, -0.07130339, 0.09987656, 0.29774663, -0.08768785, 0.15567012, -0.010313759) * go_0(0.0, 1.0);
result += mat4(-0.1260916, -0.071901485, -0.30566844, 0.19393384, -0.05133266, 0.07868844, -0.24817581, 0.055521224, 0.23277187, 0.16324161, 0.07110341, -0.042626668, 0.052509766, -0.014292625, -0.019677468, 0.041733738) * go_0(1.0, -1.0);
result += mat4(-0.04264262, -0.06528029, 0.0013520801, -0.02140956, 0.27304867, -0.029477939, -0.1859993, 0.01418354, 0.07256604, 0.14302284, 0.03309569, -0.15932149, 0.01500576, -0.053860538, 0.1131707, -0.06272606) * go_0(1.0, 0.0);
result += mat4(-0.0400483, -0.030188695, -0.108427785, 0.057873204, 0.42774406, -0.11353873, 0.110134825, 0.052191462, 0.00087113964, 0.040683694, 0.100507155, -0.16746339, -0.26971558, 0.06506685, -0.20950548, 0.040783025) * go_0(1.0, 1.0);
result += mat4(0.11394146, -0.10693933, 0.2377026, -0.03783948, -0.16496852, 0.046675198, -0.23396324, 0.05696911, -0.02770668, 0.12922443, -0.093586415, 0.102305606, 0.0040032533, -0.038440734, -0.0035825048, -0.22108772) * go_1(-1.0, -1.0);
result += mat4(0.17577791, -0.024538597, -0.19877498, -0.14544973, 0.16614193, -0.3279891, 0.14678721, -0.16355143, -0.012954231, 0.20982395, 0.044255227, 0.087878115, 0.11289659, -0.26981032, -0.10789584, 0.24094439) * go_1(-1.0, 0.0);
result += mat4(0.0041394173, -0.0937936, 0.15251775, 0.1026978, -0.01999847, -0.02865502, 0.16765144, -0.17490439, -0.016996933, 0.03891808, -0.01858217, -0.106255606, 0.027496144, -0.14120618, 0.023483312, -0.08291959) * go_1(-1.0, 1.0);
result += mat4(0.060642462, -0.2957824, 0.33968493, -0.04501478, -0.14999421, -0.0067213452, -0.018236576, 0.01627547, -0.07771579, 0.0124932695, -0.11797959, -0.090979554, 0.0096479915, 0.021336472, -0.07794724, 0.030138575) * go_1(0.0, -1.0);
result += mat4(-0.091704845, -0.20800348, -0.22158638, 0.048748583, 0.15139692, -0.2832814, 0.09610812, 0.41077513, 0.0007106381, -0.14465855, 0.0056652213, 0.031696238, -0.03384328, 0.1940933, 0.19262145, 0.014331562) * go_1(0.0, 0.0);
result += mat4(-0.16637586, -0.22008398, 0.102937706, 0.15260033, 0.039856806, -0.21082906, -0.19694057, 0.0712475, 0.015049883, 0.17320138, 0.06505415, -0.020279367, -0.018576574, 0.201407, -0.08108244, 0.04151909) * go_1(0.0, 1.0);
result += mat4(-0.12496581, 0.107817784, 0.10645319, 0.035113968, 0.0166165, 0.1316661, -0.045253787, -0.03863719, 0.09126881, 0.07553792, -0.029150097, -0.07629157, -0.17978054, -0.27080613, -0.028408276, -0.15366451) * go_1(1.0, -1.0);
result += mat4(0.081859134, -0.11599677, 0.027383117, 0.092724435, 0.059302155, 0.10008954, -0.12217131, 0.07471211, -0.20396213, -0.040741358, 0.118772194, -0.21725504, 0.099645875, 0.09691941, -0.07696025, -0.016445495) * go_1(1.0, 0.0);
result += mat4(-0.18712623, -0.14458412, 0.03693652, 0.014525352, -0.09607279, -0.19400409, 0.032149505, 0.07106094, 0.051436905, -0.07765334, 0.017043818, 0.17777587, 0.05274306, 0.0062209824, -0.080005355, 0.026041988) * go_1(1.0, 1.0);
result += mat4(-0.090594456, -0.041637532, 0.10346829, -0.09393943, 0.027663473, 0.20729685, -0.011156861, 0.021863503, 0.04781304, -0.039483577, -0.092933334, -0.25187445, 0.033062164, 0.010756357, -0.13035728, -0.008321023) * go_2(-1.0, -1.0);
result += mat4(0.07772912, 0.010776647, -0.018709056, 0.25634038, 0.00906326, 0.21411708, 0.122652486, 0.07725616, 0.15266491, 0.1274286, 0.10400329, 0.20354506, 0.013765407, -0.039089683, 0.25870228, -0.08919069) * go_2(-1.0, 0.0);
result += mat4(-0.14971368, 0.06935879, -0.089983195, 0.01406992, 0.16989979, -0.037809014, 0.07157283, -0.050660506, -0.032826405, 0.033794664, -0.0051332368, 0.089349195, 0.06263488, -0.07048108, 0.07263597, -0.11618368) * go_2(-1.0, 1.0);
result += mat4(0.013391823, -0.07888697, -0.13984044, -0.01241464, -0.06475807, 0.06978077, -0.20329754, 0.16602662, 0.013664227, 0.12317301, -0.10240692, -0.0657491, -0.31402445, -0.14472555, 0.1739024, 0.0005437834) * go_2(0.0, -1.0);
result += mat4(0.16330495, 0.02644609, 0.23837087, -0.07734767, 0.12377497, -0.18478604, 0.35040903, -0.05262452, 0.049074646, -0.0077528385, 0.15370984, -0.22888668, 0.3603141, 0.29372314, -0.4432887, 0.20702155) * go_2(0.0, 0.0);
result += mat4(-0.18785694, 0.21085343, -0.111042105, 0.0478716, -0.08214944, -0.0922987, 0.29570273, 0.025100114, 0.25403878, 0.01271447, 0.21851794, -0.1434596, -0.21153769, 0.023305666, -0.10386609, 0.043919638) * go_2(0.0, 1.0);
result += mat4(-0.117247805, 0.013329102, 0.0313911, -0.08055777, -0.0053445757, -0.2886372, 0.07938673, -0.06659165, 0.20798062, 0.030106818, -0.04811631, 0.036332276, -0.057687126, 0.03813657, 0.035860628, -0.11273985) * go_2(1.0, -1.0);
result += mat4(-0.0031557097, 0.027456097, -0.14444692, 0.08411739, 0.13466308, -0.13212901, -0.0034804344, 0.1464661, -0.21033211, 0.05913627, 0.10233881, 0.009844489, -0.15369488, -0.018978333, -0.07518442, -0.010549853) * go_2(1.0, 0.0);
result += mat4(0.112989105, -0.011166866, -0.08277204, 0.046827227, -0.08067428, 0.13465053, -0.1656419, 0.07280515, 0.037523627, -0.050147127, -0.17731906, 0.1067486, 0.119732924, -0.102017604, 0.31421226, -0.14060387) * go_2(1.0, 1.0);
result += mat4(-0.1106223, 0.09229271, -0.09355422, -0.02413533, -0.096457504, -0.13282233, 0.022983741, -0.13534859, -0.0056585902, -0.07214356, 0.14617127, -0.13723095, 0.058078192, -0.1038417, -0.10452195, -0.18855028) * go_3(-1.0, -1.0);
result += mat4(0.16357008, 0.080841675, 0.1663936, 0.20815827, 0.03813903, 0.34158087, -0.012987109, 0.39152008, -0.027927356, -0.14332302, -0.012866622, -0.016149148, -0.08733816, 0.1960951, 0.19572765, -0.2710826) * go_3(-1.0, 0.0);
result += mat4(0.024827998, 0.24175219, 0.030659903, -0.22227505, 0.026898654, 0.009930298, 0.088392995, 0.32644793, -0.10351868, -0.08717382, 0.22931585, 0.05197704, 0.06534648, 0.13636068, 0.062107667, 0.024806283) * go_3(-1.0, 1.0);
result += mat4(-0.18550465, 0.062058095, -0.08620093, 0.20158216, -0.1460996, 0.14275469, -0.28057688, -0.11685651, -0.09627509, 0.09029933, 0.03669734, 0.1257313, -0.07974307, 0.020742215, -0.0039170664, 0.11340528) * go_3(0.0, -1.0);
result += mat4(0.15225565, 0.171972, 0.13573253, 0.0056740018, -0.1667786, 0.06028638, -0.1255049, -0.23327217, -0.139949, 0.029957669, -0.16713464, 0.046236664, -0.05070503, 0.18714412, -0.20076098, 0.1672637) * go_3(0.0, 0.0);
result += mat4(0.18468563, 0.07733334, 0.14463845, -0.10712052, 0.36213547, 0.29404843, 0.2110929, 0.14646721, -0.059985258, -0.2709805, 0.073061034, -0.039072156, 0.015898943, -0.17166951, 0.20194982, -0.04723745) * go_3(0.0, 1.0);
result += mat4(-0.26353067, 0.050225407, -0.42643914, 0.06601958, -0.10513071, -0.1654714, 0.0593609, 0.027410276, -0.19465327, -0.13865606, 0.05579213, 0.07982532, -0.20893136, -0.008150932, 0.053529713, -0.0317475) * go_3(1.0, -1.0);
result += mat4(-0.012075693, -0.27574313, 0.22184552, -0.117393926, -0.49310133, -0.13997443, -0.079180904, -0.053438634, -0.07552426, -0.045796394, -0.037434675, 0.24076645, -0.04395852, 0.10325762, -0.19867313, -0.070216134) * go_3(1.0, 0.0);
result += mat4(-0.026107877, -0.030023552, -0.047810435, 0.20572239, 0.061861858, 0.1776161, -0.306099, 0.16332485, -0.1843373, 0.06758581, -0.23902373, -0.10575018, 0.03990962, -0.046113137, 0.14876197, -0.21280771) * go_3(1.0, 1.0);
result += vec4(-0.009669773, 0.036289547, -0.050454646, 0.051479716);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_3_tf
//!BIND conv2d_3_tf1
//!SAVE conv2d_4_tf1
//!WIDTH conv2d_3_tf.w
//!HEIGHT conv2d_3_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_3_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_3_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.14542116, -0.15827142, -0.20811677, -0.103433, 0.19787271, 0.33990738, 0.17085013, -0.059132278, 0.013047369, -0.1687924, 0.06732661, -0.050968684, 0.09197164, -0.041265316, -0.108277336, -0.014430892) * go_0(-1.0, -1.0);
result += mat4(-0.022837132, 0.20440012, -0.14266612, 0.019944299, 0.069084294, 0.3171199, -0.1521742, -0.35806596, 0.13581008, -0.13811131, 0.12219503, 0.17329764, -0.15100783, 0.0862648, 0.118227705, 0.18736814) * go_0(-1.0, 0.0);
result += mat4(0.013604392, 0.11496102, -0.18734755, -0.047555517, 0.05297245, 0.006461213, 0.06247472, -0.0202791, 0.02329791, 0.11530998, -0.148774, 0.0965498, 0.1487269, 0.061629567, -0.22488646, -0.005393787) * go_0(-1.0, 1.0);
result += mat4(-0.29286116, 0.11958281, -0.11193505, -0.17139061, -0.035151243, -0.2635945, 0.0002499315, -0.16346519, 0.23779829, 0.04454211, 0.21293561, 0.25617847, 0.12194803, -0.0017443774, -0.009216221, -0.034387548) * go_0(0.0, -1.0);
result += mat4(0.28791443, -0.25421545, -0.058626153, -0.1520494, -0.16808414, -0.39723453, -0.13199537, 0.056999452, -0.048155293, 0.38699663, -0.114719056, 0.001293743, -0.0959443, -0.08189709, 0.26921842, 0.061219636) * go_0(0.0, 0.0);
result += mat4(0.00781977, -0.07103863, -0.21942843, 0.2419546, 0.20016691, -0.28697264, -0.034715973, -0.03381459, -0.028126812, 0.046806023, -0.14423183, -0.13472253, 0.009225362, -0.086190686, 0.0041205613, 0.08953202) * go_0(0.0, 1.0);
result += mat4(-0.04926224, -0.099740155, -0.088695474, 0.09950333, -0.06495916, 0.20126842, -0.0062843356, -0.034764495, -0.10808971, -0.19946553, 0.075991094, 0.14746219, 0.08247818, 0.07382381, -0.056908615, -0.026823666) * go_0(1.0, -1.0);
result += mat4(-0.04837408, 0.12605472, -0.23957102, -0.14252385, -0.046534102, -0.07511751, -0.21040416, 0.2064639, -0.006026243, -0.25005546, -0.063780144, 0.076840036, -0.07484346, 0.017368162, 0.04657373, -0.022188455) * go_0(1.0, 0.0);
result += mat4(0.04545079, -0.002226373, -0.11695467, 0.12954631, 0.054903183, 0.15162702, -0.19222596, 0.05351421, -0.079599276, -0.036238387, 0.1362261, 0.037431743, -0.0015106505, 0.18739921, 0.122365154, -0.05871144) * go_0(1.0, 1.0);
result += mat4(-0.005558987, -0.13553315, -0.006372213, 0.06633917, -0.22141413, -0.15780807, 0.057122614, -0.057320844, -0.06306763, 0.19112623, -0.041758966, 0.03555483, -0.005718873, 0.009167371, 0.050909385, -0.14599234) * go_1(-1.0, -1.0);
result += mat4(0.18175003, 0.10442485, 0.052994236, -0.4001252, -0.08328538, 0.06380226, -0.055015627, 0.010929493, -0.22888647, -0.033181675, -0.07570874, 0.07933599, -0.07894686, 0.12202901, 0.13679314, -0.054344065) * go_1(-1.0, 0.0);
result += mat4(0.030145945, -0.06121175, -0.08550973, 0.10082535, 0.07198805, 0.21414264, -0.25636044, 0.028803539, 0.043738026, -0.0367658, 0.27998537, -0.06274612, -0.22862338, 0.002624325, 0.28519824, 0.18540645) * go_1(-1.0, 1.0);
result += mat4(-0.012136538, -0.07059324, 0.018098673, 0.12078888, -0.087637, 0.041642863, 0.034997553, -0.16741107, 0.04701011, -0.004160269, 0.122639626, 0.0043271836, 0.011551197, -0.16421974, -0.102481335, 0.014233497) * go_1(0.0, -1.0);
result += mat4(-0.37945676, 0.25232047, -0.03707734, -0.1985225, -0.11536396, 0.22039749, -0.21809638, -0.10596801, -0.17211124, -0.2035486, 0.011822896, 0.27510995, -0.105182275, 0.022503568, -0.0063389307, -0.071560584) * go_1(0.0, 0.0);
result += mat4(-0.16101715, -0.034247126, 0.16626042, 0.031131435, 0.03048031, -0.105447404, -0.05728527, -0.14518815, -0.019103229, -0.15152888, -0.119154684, 0.028724093, 0.05836196, -0.35943082, -0.016481897, -0.0437348) * go_1(0.0, 1.0);
result += mat4(-0.07719413, -0.33214888, -0.0541927, 0.16506542, -0.032792456, 0.016834807, 0.1724155, 0.073768586, 0.002303886, -0.001382793, -0.0562648, -0.10167158, -0.19101655, 0.052783452, -0.1422853, 0.09653729) * go_1(1.0, -1.0);
result += mat4(-0.30030164, 0.11637444, -0.23238538, -0.27238008, -0.077208534, -0.027645003, 0.10369907, 0.20162316, -0.14428844, 0.1766293, 0.024419712, 0.11301171, 0.07772854, 0.18613201, 0.20721672, -0.1751799) * go_1(1.0, 0.0);
result += mat4(-0.1026615, -0.12484944, 0.15386428, 0.038676128, -0.119472496, -0.032417197, -0.14208497, -0.05254358, -0.0035079278, -0.011276316, 0.043117497, -0.010022288, 0.031624593, 0.014969992, -0.031410277, 0.15284787) * go_1(1.0, 1.0);
result += mat4(0.018149922, -0.05906194, 0.054767277, 0.008161979, -0.076949194, 0.040888708, -0.006419542, -0.12897012, -0.0028229658, 0.20937827, 0.02741711, -0.04013348, -0.12731804, 0.008064522, 0.002870103, 0.027690327) * go_2(-1.0, -1.0);
result += mat4(0.023197446, -0.08888926, 0.15531142, 0.13745947, 0.054352283, -0.121785395, 0.16237587, 0.023567237, -0.36160588, 0.30499592, -0.033180915, -0.1515843, 0.04251452, -0.17903805, 0.03235283, -0.08062386) * go_2(-1.0, 0.0);
result += mat4(-0.0072868476, -0.2010616, 0.13061914, 0.12846659, 0.11725315, 0.14589547, -0.05373261, -0.081606135, -0.07010131, -0.025378224, 0.10265872, 0.18658938, -0.12165338, 0.036297683, 0.03925332, 0.16576236) * go_2(-1.0, 1.0);
result += mat4(0.10300252, -0.11548347, -0.08691649, -0.014866044, -0.3213804, 0.47206497, -0.16032113, 0.026284516, 0.046302956, -0.052474245, -0.025335522, -0.10957576, -0.16872157, 0.19049212, -0.023881195, 0.061396897) * go_2(0.0, -1.0);
result += mat4(-0.16202278, 0.52128345, -0.2601511, 0.06116799, -0.21123995, 0.39389637, -0.350544, -0.16157438, -0.02823116, -0.39056876, -0.14267299, 0.03262984, 0.342303, -0.20556125, -0.0019219286, -0.1824844) * go_2(0.0, 0.0);
result += mat4(0.23399737, -0.0912646, 0.11152403, -0.20945886, -0.053451832, -0.09786892, -0.059099484, 0.18103573, -0.117154315, -0.18342866, 0.12650815, 0.0067340015, -0.037984423, 0.17667364, 0.071636364, -0.011689163) * go_2(0.0, 1.0);
result += mat4(-0.099510275, -0.0925438, -0.009136904, -0.03774997, -0.13348748, 0.3605135, -0.078298144, -0.14712195, 0.22566219, 0.18659295, 0.05614545, 0.10792911, -0.12477693, -0.03587624, 0.08050775, -0.054740936) * go_2(1.0, -1.0);
result += mat4(0.10312337, -0.063681684, 0.16496794, 0.09038492, -0.08903926, 0.41163155, -0.013669214, -0.21472235, -0.054991595, 0.0033639956, 0.18160143, 0.17240305, -0.039428882, 0.17087695, -0.1729076, 0.09871825) * go_2(1.0, 0.0);
result += mat4(-0.13123736, 0.0802573, 0.077981554, -0.101768315, 0.089998, -0.13781744, 0.122858986, 0.054121554, -0.02640825, 0.13577555, -0.037485655, -0.04179625, 0.000106130996, -0.100183845, 0.00046665114, 0.21791616) * go_2(1.0, 1.0);
result += mat4(0.011894387, -0.030088445, 0.025817253, 0.08193235, 0.109322436, 0.10855583, -0.19661167, -0.09405307, 0.2073779, -0.33972177, 0.048635002, -0.14883177, 0.056954246, 0.3953476, 0.18765114, -0.014010224) * go_3(-1.0, -1.0);
result += mat4(-0.22594279, -0.014942035, -0.1519647, 0.25367293, 0.16330296, 0.03317176, -0.32148597, -0.46503916, 0.19944623, -0.26229686, 0.019909514, -0.059794176, 0.12912126, 0.044948537, -0.08649492, 0.08024645) * go_3(-1.0, 0.0);
result += mat4(-0.022943841, -0.068013534, 0.11032515, 0.011685601, 0.020096298, -0.3285243, 0.08196111, -0.089537136, -0.03976742, -0.1315977, -0.36306036, 0.24678081, 0.22115967, -0.017472323, -0.19451386, -0.035218123) * go_3(-1.0, 1.0);
result += mat4(-0.020891193, -0.12721714, -0.15030408, 0.026523203, -0.12413139, -0.11235275, -0.21476477, -0.11326953, 0.028815055, -0.18552732, -0.0076828003, -0.14679903, 0.020509586, -0.18695217, 0.06696879, 0.103938386) * go_3(0.0, -1.0);
result += mat4(0.057521313, 0.28509304, -0.2525733, 0.16745082, -0.26614547, 0.18545172, -0.27140215, 0.018639714, 0.19730581, 0.1659491, -0.058363054, -0.4048628, 0.024913948, -0.44124457, 0.13872208, -0.0371103) * go_3(0.0, 0.0);
result += mat4(0.100904405, 0.06700356, -0.035322092, 0.21781014, 0.018047005, -0.21737386, -0.3734802, 0.13506944, 0.012760691, 0.06620756, -0.0253398, 0.0030280363, -0.044015452, -0.055860534, -0.3547194, -0.04230283) * go_3(0.0, 1.0);
result += mat4(-0.19012743, -0.34408915, 0.18940191, 0.13152952, 0.107553795, -0.00694412, -0.07930157, -0.30964044, 0.034710668, -0.031806916, 0.019838978, 0.017044948, 0.110688254, -0.0029772928, 0.09414367, -0.10760175) * go_3(1.0, -1.0);
result += mat4(-0.05745392, 0.29022983, 0.014998233, 0.27365527, 0.08169933, 0.0734232, -0.09404464, -0.26870936, 0.21171738, -0.19529793, -0.064401075, -0.18972695, -0.08024953, -0.027122354, -0.11661348, 0.010131282) * go_3(1.0, 0.0);
result += mat4(0.07599435, -0.06851123, 0.06258365, 0.10296892, 0.15556085, -0.041609086, -0.11303363, 0.07082365, 0.013949174, -0.087201476, -0.0855705, -0.12979257, 0.04048528, 0.4211556, 0.04118289, -0.22093314) * go_3(1.0, 1.0);
result += vec4(0.07789114, 0.0024746545, 0.1891165, -0.0023716448);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_4_tf
//!BIND conv2d_4_tf1
//!SAVE conv2d_5_tf
//!WIDTH conv2d_4_tf.w
//!HEIGHT conv2d_4_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_4_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_4_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.10883355, -0.14958352, 0.026701333, 0.090302855, 0.033934478, 0.120340124, 0.027125617, -0.16792692, -0.075757094, 0.28692973, 0.013230067, -0.040618937, 0.087148145, -0.05985753, -0.06352023, -0.05775848) * go_0(-1.0, -1.0);
result += mat4(-0.18206549, -0.10363482, 0.097648725, -0.08801144, 0.31633568, 0.058347676, -0.009121898, 0.02594872, 0.14757825, 0.4730546, -0.008518203, -0.3090668, -0.004052835, -0.14166127, -0.010156037, 0.21191326) * go_0(-1.0, 0.0);
result += mat4(0.05735183, 0.039180398, -0.12357178, 0.04830351, 0.120369986, -0.052775342, 0.005902798, 0.07695394, 0.00602021, 0.16758691, 0.10287989, -0.1718468, -0.1319741, 0.16932078, -0.2055026, -0.31820264) * go_0(-1.0, 1.0);
result += mat4(0.05427556, -0.28392607, 0.08579091, -0.0015861926, 0.062348455, -0.27778792, -0.07450379, 0.01616914, -0.012357131, -0.056992117, -0.1896176, 0.018156245, 0.06499259, -0.076558664, 0.10341699, -0.08993959) * go_0(0.0, -1.0);
result += mat4(-0.05741742, -0.05414434, 0.18006511, 0.09840777, -0.11849741, 0.40419933, 0.21349974, 0.40268886, 0.23218039, -0.0680356, -0.3130592, -0.21271054, 0.13776754, 0.19114101, 0.17373541, 0.43457666) * go_0(0.0, 0.0);
result += mat4(-0.060757063, 0.11339545, -0.042958036, -0.06483378, -0.06681766, -0.056395415, 0.037868995, 0.033861663, -0.1041215, 0.0046828864, 0.14360638, 0.087886184, -0.26808187, 0.19876598, -0.05276215, -0.07073776) * go_0(0.0, 1.0);
result += mat4(-0.24029991, -0.14217372, -0.011767948, 0.011623913, 0.33820602, -0.24501325, -0.11444902, 0.14536968, 0.16780593, 0.0065867775, -0.074971735, 0.021472024, -0.10853042, 0.09527126, 0.009436061, -0.09688826) * go_0(1.0, -1.0);
result += mat4(-0.31893802, -0.0016892607, -0.105592966, -0.116694786, -0.007851739, 0.1429722, 0.0741952, 0.050125953, 0.07185179, 0.1900389, 0.030889044, 0.15422693, 0.12550323, 0.3556344, 0.108276874, -0.099125646) * go_0(1.0, 0.0);
result += mat4(-0.33620578, -0.11113713, -0.15881014, 0.028243937, -0.12028756, -0.028566968, -0.002682634, -0.15635195, -0.06869284, -0.03309234, 0.03086361, 0.050773233, -0.08939835, 0.15237434, -0.024076303, -0.13092752) * go_0(1.0, 1.0);
result += mat4(-0.31200737, 0.32207087, -0.068700634, -0.39202076, 0.0676771, 0.083766654, -0.05696634, 0.03088338, 0.046761762, 0.09732023, 0.030844063, -0.03369749, -0.12664944, -0.029924957, 0.10551989, 0.086157694) * go_1(-1.0, -1.0);
result += mat4(-0.1919761, 0.17179352, -0.025805056, -0.05570367, -0.16736336, 0.07430868, -0.13228212, 0.10702857, -0.09723214, 0.1884809, 0.09422538, -0.16902041, -0.1964137, 0.17877853, 0.17453954, -0.11339361) * go_1(-1.0, 0.0);
result += mat4(0.11865004, 0.013131073, 0.17317963, -0.2077911, -0.1116894, 0.09672745, -0.023348883, -0.1176519, 0.15893579, 0.22941695, 0.18798698, 0.059098385, 0.09498779, 0.10118143, 0.08737761, -0.016268898) * go_1(-1.0, 1.0);
result += mat4(-0.025380889, 0.17163627, -0.014800655, 0.12669696, 0.050048903, -0.06513837, 0.020915661, 0.2144372, -0.17799327, 0.0068409992, 0.06751171, -0.16618991, 0.14637277, 0.010591964, -0.15909241, 0.02660789) * go_1(0.0, -1.0);
result += mat4(0.3178319, 0.15036377, -0.03386948, 0.13883169, -0.33842105, 0.061425313, -0.04195804, 0.22558802, 0.2250625, 0.060225345, -0.08467863, 0.0014776831, 0.080328, 0.03221249, 0.20838667, 0.11489719) * go_1(0.0, 0.0);
result += mat4(-0.0013924981, 0.28233197, -0.17997956, -0.10959627, -0.16253087, 0.016549526, -0.1571556, 0.017017027, -0.14697123, 0.0869202, 0.2104898, -0.15658243, 0.13424201, -0.022636503, -0.09512045, 0.0927298) * go_1(0.0, 1.0);
result += mat4(-0.038486905, -0.19215351, -0.2446516, -0.02958912, 0.06899297, 0.028667469, -0.05537665, 0.066711955, -0.0017354499, -0.07466053, 0.028587297, -0.042017035, 0.023596823, 0.0067433366, -0.14685915, 0.13400853) * go_1(1.0, -1.0);
result += mat4(0.0573442, 0.1424536, 0.19606829, 0.07141616, -0.032276712, 0.20030099, 0.16644277, 0.10393295, 0.27240822, 0.0071844175, -0.023368603, -0.14067268, -0.20310283, 0.039528254, 0.103837095, 0.08236034) * go_1(1.0, 0.0);
result += mat4(0.15616669, 0.3495403, -0.05678421, -0.069600284, -0.07361787, 0.079501756, 0.009530261, -0.032385882, 0.029831208, -0.095407076, 0.010261287, 0.15250465, -0.04868275, 0.058579214, 0.03779718, -0.10810775) * go_1(1.0, 1.0);
result += mat4(0.06492073, 0.018667994, -0.004712761, -0.032692235, 0.04027288, -0.114499666, -0.04327484, 0.13778907, -0.09373396, -0.08822919, 0.04796151, -0.057756703, -0.26161298, 0.07182931, 0.12998815, -0.14389744) * go_2(-1.0, -1.0);
result += mat4(0.19001032, 0.13091461, -0.2551175, 0.013365716, -0.031779066, 0.002531366, -0.13807543, -0.14165778, -0.2701911, -0.0890182, 0.34704998, -0.008494185, 0.16179956, -0.060182545, 0.060827415, -0.17249492) * go_2(-1.0, 0.0);
result += mat4(0.10665868, 0.15999752, -0.042796712, -0.14010513, -0.014244899, 0.017433831, 0.053657144, -0.0965679, 0.23623326, 0.0690172, 0.1290121, -0.025523739, 0.122357905, -0.18172716, 0.02829383, 0.10042929) * go_2(-1.0, 1.0);
result += mat4(-0.09273112, 0.09466892, -0.009225705, 0.16772579, 0.0813042, -0.16461512, 0.038097944, 0.19834967, -0.033650465, -0.12888893, 0.1414859, -0.021587005, -0.0047441716, 0.08880282, 0.020621201, 0.065779164) * go_2(0.0, -1.0);
result += mat4(0.0051817205, 0.20322648, -0.077459775, 0.07461627, 0.1817634, -0.5371515, -0.29336745, -0.57652086, 0.035826538, 0.41058993, 0.21512514, -0.041881148, -0.2490056, -0.07172767, 0.20821427, -0.69866294) * go_2(0.0, 0.0);
result += mat4(0.18961228, 0.027452804, -0.0075194626, -0.029665018, 0.28770384, -0.099777386, -0.12160496, 0.07690297, 0.30273837, 0.026466522, 0.18100439, -0.09078488, 0.2035407, -0.062081084, 0.06744994, -0.07512911) * go_2(0.0, 1.0);
result += mat4(0.008473044, 0.07501521, -0.11242355, -0.039451122, -0.21818535, -0.07779562, 0.13194147, 0.084983595, 0.0770609, -0.034488454, 0.08823556, -0.07168295, 0.041894365, 0.0789253, 0.06191209, 0.013991105) * go_2(1.0, -1.0);
result += mat4(0.10582237, 0.1514222, 0.10751824, 0.08231926, 0.23913008, -0.2673503, 0.036170945, 0.31463087, 0.026397424, -0.26629624, -0.07428361, -0.077513516, 0.0768238, -0.026638538, 0.12589583, -0.11521212) * go_2(1.0, 0.0);
result += mat4(0.30389515, 0.18963532, 0.023015842, -0.10240883, 0.045651495, -0.036785256, -0.13346411, 0.16431254, -0.030950911, -0.03381929, 0.09413111, 0.03924852, 0.11044091, -0.10149653, 0.14114548, 0.07801978) * go_2(1.0, 1.0);
result += mat4(0.029622428, 0.14528686, -0.034057826, 0.010664312, 0.059213262, -0.29354423, -0.08448559, 0.10569036, -0.02988314, -0.016480735, 0.042203777, -0.028342744, 0.36807576, 0.09301971, 0.123721026, 0.07806503) * go_3(-1.0, -1.0);
result += mat4(0.04849538, -0.09201287, 0.10069803, -0.031749677, 0.18774022, -0.27789372, 0.05288653, 0.08097265, 0.006918896, -0.060978457, -0.113319606, 0.008844536, 0.021804892, -0.0011744015, -0.35720357, -0.24996938) * go_3(-1.0, 0.0);
result += mat4(-0.07147501, -0.09339197, 0.16154395, 0.3372506, -0.0004858638, -0.056553435, -0.12463908, -0.0047342298, -0.009141984, -0.13796125, -0.14035304, -0.104403175, -0.07054226, 0.12142519, -0.24971877, -0.1914648) * go_3(-1.0, 1.0);
result += mat4(-0.008194284, -0.027617034, 0.004994261, -0.07672895, 0.25697777, -0.18313397, 0.03266311, -0.029157834, 0.010476624, 0.12394092, -0.059660904, 0.08561672, -0.0008583816, -0.044442356, 0.28336492, 0.065344445) * go_3(0.0, -1.0);
result += mat4(-0.3570137, -0.06802815, -0.10298613, -0.21256869, 0.3025278, -0.263425, 0.13547331, 0.038517762, 0.14951234, -0.16869017, 0.03293678, 0.21897063, -0.14688788, 0.21619378, -0.27550143, 0.048003722) * go_3(0.0, 0.0);
result += mat4(0.15607022, -0.111073844, 0.2733694, 0.05423378, 0.25116092, -0.17350473, 0.13460433, 0.09602139, 0.17372625, -0.0024815476, -0.30154657, 0.0062206364, -0.0051755225, 0.04985103, -0.06310478, -0.30450678) * go_3(0.0, 1.0);
result += mat4(0.057571005, -0.019051064, 0.054884393, 0.03993782, 2.6782007e-05, -0.05726912, 0.067192145, -0.08955987, -0.11937056, 0.15837386, -0.011670469, -0.06299701, -0.014917928, 0.23921679, 0.0054613873, -0.23099245) * go_3(1.0, -1.0);
result += mat4(-0.035849575, -0.06785954, -0.15053692, 0.011964653, 0.1975448, -0.1633047, -0.024539666, 0.03170174, -0.12585635, -0.021171011, 0.15862562, 0.10296358, 0.3114039, 0.10010659, -0.09519227, -0.12945092) * go_3(1.0, 0.0);
result += mat4(0.044433746, -0.058466546, -0.13258536, -0.033972915, 0.0037206819, -0.057343487, 0.13798106, 0.044445634, -0.22623023, 0.2408462, 0.048287082, -0.30717465, -0.13402344, 0.20024839, -0.026932377, -0.034217034) * go_3(1.0, 1.0);
result += vec4(-0.05988374, -0.23198523, -0.058251306, -0.038808554);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_4_tf
//!BIND conv2d_4_tf1
//!SAVE conv2d_5_tf1
//!WIDTH conv2d_4_tf.w
//!HEIGHT conv2d_4_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_4_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_4_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.045249436, -0.040327657, -0.2667367, 0.0913868, 0.14961123, 0.07253207, 0.29162952, -0.11320944, 0.017569833, 0.012350104, 0.22532712, 0.025312115, -0.12193993, 0.037391737, 0.03220835, 0.12102545) * go_0(-1.0, -1.0);
result += mat4(-0.020587588, -0.07043244, -0.28093454, 0.18336722, 0.08153308, -0.05914772, -0.15255487, 0.079236075, -0.4269835, -0.11470208, -0.19043571, 0.2723162, 0.0066251885, -0.17115718, 0.022036036, 0.07349558) * go_0(-1.0, 0.0);
result += mat4(-0.09441315, 0.042170826, 0.071251415, -0.13891962, 0.10236482, 0.05356262, 0.0291025, 0.063867815, -0.14530063, -0.08727925, -0.0048300857, 0.06766869, -0.3481536, -0.10943503, 0.014951926, 0.11993114) * go_0(-1.0, 1.0);
result += mat4(0.13420522, 0.095721036, -0.1756104, -0.09906728, 0.09808904, -0.27402034, -0.102161326, 0.40162942, 0.13465238, 0.20237032, 0.3192343, -0.061512157, -0.20711629, -0.09659007, 0.06838548, 0.30256763) * go_0(0.0, -1.0);
result += mat4(0.025805298, -0.0322599, 0.23653145, -0.2760735, 0.11291006, -0.10836205, 0.20742846, 0.06974535, -0.4191803, -0.10882523, 0.038603242, 0.22662747, -0.08845715, -0.26151156, -0.16670766, 0.008536192) * go_0(0.0, 0.0);
result += mat4(-0.085842185, -0.21239999, -0.032774646, 0.088163696, 0.038300447, -0.09510875, 0.10113864, -0.14712982, 0.14264707, -0.10895432, 0.03051617, -0.06791873, -0.35589013, -0.12884575, -0.09460007, -0.0879575) * go_0(0.0, 1.0);
result += mat4(0.19235751, -0.109611385, -0.037397474, -0.26632717, 0.07878826, 0.19749992, 0.0035685285, 0.11793927, 0.019899402, 0.085741036, 0.08433813, -0.018344546, -0.0901484, 0.08221562, 0.12735383, 0.12801875) * go_0(1.0, -1.0);
result += mat4(0.19123435, 0.007882246, -0.018564796, -0.09904253, 0.28052533, 0.6360808, 0.25001726, -0.30590564, 0.07646281, -0.34298185, -0.33293694, -0.036753535, 0.18719083, 0.22131144, -0.1420962, -0.0014709529) * go_0(1.0, 0.0);
result += mat4(0.23060241, -0.14145076, -0.113213465, 0.037221998, 0.22163334, 0.18520229, 0.2961799, -0.063605964, 0.022606356, 0.043340076, -0.3233993, -0.075055614, -0.0038865958, 0.19558622, -0.018503085, -0.22932632) * go_0(1.0, 1.0);
result += mat4(0.11712158, -0.03590364, 0.38039652, -0.019910801, 0.13338004, -0.07078425, 0.09404417, -0.27607328, -0.02205519, -0.013522961, 0.2924021, -0.16088538, -0.034280356, -0.063614614, -0.061583273, -0.22479968) * go_1(-1.0, -1.0);
result += mat4(-0.05624079, 0.32659104, 0.47335497, -0.14091404, 0.14739423, -0.07122778, -0.009384643, -0.058900848, 0.06260307, -0.17574102, 0.3538743, 0.2842822, -0.18150197, 0.26806462, 0.24673693, 0.19710627) * go_1(-1.0, 0.0);
result += mat4(-0.24837571, -0.01663848, -0.13093965, 0.30109972, -0.09680959, 0.074526474, 0.024111765, -0.012781737, -0.08591349, -0.100348584, 0.02363011, -0.02687084, -0.27630556, 0.14074354, -0.016993485, 0.084373675) * go_1(-1.0, 1.0);
result += mat4(0.1543391, -0.2008408, -0.21885285, 0.2320177, 0.06669948, -0.05171086, -0.25833863, -0.14085051, -0.035878573, -0.1632403, 0.09782713, 0.22973235, -0.14022017, -0.018347954, -0.29652777, 0.10912002) * go_1(0.0, -1.0);
result += mat4(-0.050962634, -0.040519282, -0.04381614, 0.084133334, 0.21222316, -0.091010064, 0.13157965, -0.21375372, -0.021148674, -0.044127557, -0.11400533, 0.097688414, 0.31571037, -0.05167655, 0.27606225, 0.12169133) * go_1(0.0, 0.0);
result += mat4(-0.1329087, 0.14291021, 0.043337896, -0.25970098, -0.11379552, -0.040157612, 0.08379851, -0.24104865, 0.1593102, -0.031879216, -0.004603848, -0.019003935, -0.24769545, -0.17577063, 0.16019398, 0.04640235) * go_1(0.0, 1.0);
result += mat4(-0.11615644, 0.12189521, 0.12919527, -0.104224406, -0.10143574, 0.14024515, -0.02759362, -0.1467619, 0.09028311, -0.06510291, 0.061612967, 0.10227729, -0.08785846, 0.06464871, -0.05048917, 0.09055746) * go_1(1.0, -1.0);
result += mat4(0.34443164, 0.013906371, -0.0595573, 0.09354196, 0.12184454, -0.02698316, -0.06208632, -0.11266858, 0.004904335, -0.33987018, -0.2494041, 0.127125, 0.040493876, 0.0280356, -0.037431944, 0.05823802) * go_1(1.0, 0.0);
result += mat4(-0.1762869, -0.20683959, -0.37788594, -0.1244979, -0.17202286, -0.038234763, 0.015924744, -0.014006752, 0.07097758, -0.25219876, -0.3164728, 0.022413896, -0.41423917, -0.03191542, 0.009464804, 0.0770316) * go_1(1.0, 1.0);
result += mat4(0.12442388, 0.031095076, 0.18799834, -0.18449762, -0.11995044, 0.11634828, -0.0055850362, 0.08558657, -0.025694892, -0.2854381, -0.32876188, 0.14690274, -0.1835963, -0.1786755, -0.44678628, 0.1678422) * go_2(-1.0, -1.0);
result += mat4(0.031241562, -0.1265462, 0.081369035, -0.1184643, 0.0010021052, -0.10810683, -0.039572187, 0.13850863, -0.010703417, -0.057981443, 0.30309856, 0.13869847, -0.16935349, 0.16969836, 0.045642667, 0.26460654) * go_2(-1.0, 0.0);
result += mat4(0.28779998, 0.04767888, -0.011856489, 0.114210494, 0.034624737, 0.19084676, -0.02740287, 0.035041407, -0.049002927, 0.10928203, 0.17362499, -0.1280889, 0.00077811617, -0.17594084, -0.18379052, 0.22303762) * go_2(-1.0, 1.0);
result += mat4(0.0008487252, -0.060438234, 0.109334275, -0.18768874, 0.13844973, 0.09226474, 0.18361697, -0.19385563, -0.29241335, -0.1033556, -0.3289991, 0.10027422, -0.09454755, -0.22817631, -0.2964217, -0.19499257) * go_2(0.0, -1.0);
result += mat4(-0.057920385, 0.06342629, -0.048577324, 0.15952215, -0.061343953, 0.16471362, 0.1501856, 0.027373426, 0.01837245, -0.0732048, 0.09776471, 0.14817989, -0.112215854, 0.109101914, 0.058316242, 0.29969788) * go_2(0.0, 0.0);
result += mat4(-0.12411656, -0.033170763, -0.08715826, 0.110862456, 0.1871076, 0.14550175, 0.23373431, 0.19281025, -0.37016305, -0.11924462, 0.026793748, 0.092801645, 0.04318573, 0.20969667, -0.39267823, 0.1938874) * go_2(0.0, 1.0);
result += mat4(-0.15932916, 0.22217506, 0.007901788, -0.04037383, 0.09095982, -0.043115042, 0.098845564, -0.073432215, -0.14535685, 0.11504512, -0.07950504, -0.010718905, -0.050012022, -0.13089752, -0.3323894, -0.005423676) * go_2(1.0, -1.0);
result += mat4(0.007320675, 0.21108273, 0.20758918, -0.04005568, -0.13234317, -0.15708306, 0.41804615, -0.09720499, -0.09623786, 0.2441289, 0.33276868, 0.17716111, -0.45670444, -0.026252905, -0.01958701, 0.24028622) * go_2(1.0, 0.0);
result += mat4(-0.14936383, -0.023504466, -0.028479185, -0.053541556, -0.060263615, -0.087681144, 0.2435555, 0.08470686, -0.17713271, -0.2303349, 0.09337386, 0.039068084, -0.16263027, 0.034289114, 0.16604292, 0.10550447) * go_2(1.0, 1.0);
result += mat4(-0.16556105, 0.12211341, -0.0036831333, 0.13802956, 0.065256506, 0.03395266, -0.2296282, 0.21284704, 0.017770419, -0.1722762, -0.1741687, 0.10708671, 0.331979, 0.11924846, -0.09410989, -0.123036265) * go_3(-1.0, -1.0);
result += mat4(-0.096586555, -0.30475244, -0.24065268, 0.053860847, 0.19413544, 0.05542323, -0.06327867, 0.012265184, -0.08913778, 0.13779551, -0.099127166, 0.007493773, -0.07125554, -0.0011684593, -0.003005287, -0.094847135) * go_3(-1.0, 0.0);
result += mat4(0.21711998, -0.13086027, 0.07825239, -0.21121782, 0.055840425, -0.0019166623, -0.05480048, 0.019817038, 0.007626905, 0.14126389, 0.04515749, -0.029315706, 0.18555732, -0.114861906, -0.21993469, 0.031716693) * go_3(-1.0, 1.0);
result += mat4(-0.06716353, -0.11964145, 0.09711908, -0.061763637, -0.0948045, 0.14189975, 0.2810092, 0.2505306, 0.08872909, 0.086749084, -0.17528322, -0.048835423, 0.124959685, -0.12602286, 0.065660164, -0.06783225) * go_3(0.0, -1.0);
result += mat4(-0.23066516, -0.0068310793, -0.0021060852, 0.09136854, 0.09919007, 0.2259628, -0.026603302, 0.1367709, -0.07940821, 0.14962214, 0.00652088, -0.3114987, -0.18900892, -0.20450105, 0.09329685, -0.19482759) * go_3(0.0, 0.0);
result += mat4(0.095197074, 0.06346413, -0.05207484, -0.086378016, 0.19733003, 0.1448027, -0.02410627, 0.024829205, -0.20296144, -0.09551166, 0.022987023, 0.09035918, -0.15824226, 0.1350293, -0.06641893, 0.11739518) * go_3(0.0, 1.0);
result += mat4(0.08381447, -0.13171835, -0.030271608, 0.14649504, 0.0007350431, 0.15303299, -0.001797464, 0.30294403, -0.07635094, -0.102541, -0.12176348, 0.053775523, 0.08070882, -0.035387367, -0.09521456, 0.22530125) * go_3(1.0, -1.0);
result += mat4(-0.04650126, 0.12029137, 0.009236626, -0.1371486, -0.119391896, 0.20490645, 0.17123316, -0.015455403, 0.05842872, 0.14354227, 0.37586045, 0.054906923, 0.062954046, 0.07285954, 0.12260665, -0.08675996) * go_3(1.0, 0.0);
result += mat4(0.22510684, -0.010087092, 0.005660375, 0.05069907, 0.10297958, 0.1411009, 0.09538159, 0.00922383, -0.31313825, -0.06449414, 0.109746836, 0.30148697, 0.35861742, -0.045380104, 0.09908991, -0.1933117) * go_3(1.0, 1.0);
result += vec4(0.012253057, 0.13434875, -0.10318777, -0.074252345);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_5_tf
//!BIND conv2d_5_tf1
//!SAVE conv2d_6_tf
//!WIDTH conv2d_5_tf.w
//!HEIGHT conv2d_5_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_5_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_5_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.04279202, -0.01698567, 0.18318103, -0.18172316, 0.04757184, 0.07232096, -0.054900512, 0.11956132, 0.048900753, 0.0006714882, -0.09200336, 0.16104606, 0.38940707, 0.2754208, -0.12735553, -0.30017206) * go_0(-1.0, -1.0);
result += mat4(0.2469705, 0.103162065, 0.10321547, -0.1292231, 0.3013039, -0.018333653, -0.19897339, 0.122247696, 0.14719778, 0.003909129, -0.19585025, 0.03670547, -0.2132921, 0.33642963, 0.17569672, 0.07414473) * go_0(-1.0, 0.0);
result += mat4(0.015335451, 0.15161209, 0.0447609, -0.042884503, 0.14257035, 0.07775234, -0.2064044, 0.03842874, -0.1660166, -0.19817057, -0.10740875, -0.123968095, 0.14156081, -0.2197906, -0.08622206, 0.4185408) * go_0(-1.0, 1.0);
result += mat4(-0.33392438, -0.12483512, -0.062084857, 0.16336447, 0.09862199, 0.1659862, 0.034751434, -0.11968266, -0.017155796, 0.21001562, -0.053017724, 0.10386376, 0.07066254, 0.50014263, 0.31065208, -0.026068505) * go_0(0.0, -1.0);
result += mat4(-0.34320992, -0.030056434, -0.24118581, -0.024320357, 0.327435, -0.036838267, -0.19433706, 0.24561343, -0.1489437, 0.225435, 0.18421564, 0.021147838, 0.264245, 0.16846146, -0.51724315, 0.039252095) * go_0(0.0, 0.0);
result += mat4(-0.25945047, 0.12058094, 0.2889452, -0.061687145, -0.10309796, -0.19476385, -0.10393912, 0.16837607, -0.05198191, -0.036113493, -0.11847194, 0.16367626, 0.018113747, 0.059499823, 0.0062132217, 0.15846115) * go_0(0.0, 1.0);
result += mat4(0.094601326, 0.053219795, 0.027610637, 0.12041253, 0.21425363, 0.15754686, 0.08518286, -0.00661778, -0.021661628, -0.17554528, -0.014842315, 0.22240937, 0.15908821, -0.20964032, 0.21754523, 0.30307937) * go_0(1.0, -1.0);
result += mat4(0.13757955, 0.06684095, -0.03616685, -0.014618309, 0.04168136, -0.17148526, -0.16317028, 0.14210777, 0.102521434, -0.19108291, -0.14441934, 0.14435884, 0.24228935, -0.10589834, 0.24029285, 0.27317202) * go_0(1.0, 0.0);
result += mat4(-0.16239886, -0.073841535, 0.067964345, -0.11332664, 0.07695667, -0.047180675, -0.08260769, 0.09427637, 0.09471068, 0.012713836, 0.14605078, -0.062490974, -0.11498225, 0.04150893, 0.37402585, 0.21953487) * go_0(1.0, 1.0);
result += mat4(-0.07445113, -0.14220217, 0.09271495, -0.014715529, -0.37606132, -0.14938155, -0.024809113, 0.22279873, -0.011379667, -0.04545505, -0.033382278, 0.08971831, 0.016359061, -0.016230864, 0.052939463, -0.07754285) * go_1(-1.0, -1.0);
result += mat4(0.10961948, 0.09230085, 0.061259165, 0.0015837378, 0.053883027, -0.22557226, 0.018400123, 0.43234614, 0.08967873, 0.06687854, -0.4389578, -0.01658211, -0.040707946, 0.0048945122, 0.1433802, 0.049759727) * go_1(-1.0, 0.0);
result += mat4(-0.027641231, 0.026085567, 0.109188825, -0.19011945, 0.19309571, 0.0084956605, 0.05034047, -0.08674781, -0.008124587, 0.031490494, -0.0744263, 0.084508896, -0.007835403, 0.13120581, 0.0021786217, -0.025225073) * go_1(-1.0, 1.0);
result += mat4(0.020191731, 0.24703082, -0.36845222, 0.0032569442, -0.1497622, 0.05968502, 0.09595371, 0.008410154, 0.119981945, -0.09983294, -0.19541258, -0.111814305, -0.25664008, 0.31031236, -0.23063917, -0.13823026) * go_1(0.0, -1.0);
result += mat4(-0.092747286, 0.23009373, -0.29804415, 0.05036082, 0.031480987, 0.18805481, 0.3676576, 0.06004687, 0.19841099, -0.058367446, -0.44229323, -0.19645047, 0.037667975, 0.12398346, -0.25753063, -0.26919344) * go_1(0.0, 0.0);
result += mat4(-0.019061154, 0.03841801, -0.28433323, 0.38128456, -0.059526864, 0.29960185, 0.014484517, -0.10234412, 0.05444907, -0.12615138, 0.14936689, -0.079120934, 0.028092088, 0.096715964, 0.0037780635, -0.12791039) * go_1(0.0, 1.0);
result += mat4(0.26949528, 0.015951393, 0.15355164, -0.030336212, -0.100286454, -0.052609976, 0.03197625, -0.092190474, 0.06131517, 0.18291938, -0.15216532, -0.026021928, 0.18581273, -0.10659101, 0.14806952, 0.20509768) * go_1(1.0, -1.0);
result += mat4(-0.2205839, 0.11654808, 0.43800604, 0.03188946, 0.13840868, 0.020377772, 0.038510147, 0.03779825, -0.23494276, 0.08624197, 0.036650848, -0.115041405, -0.03776705, -0.32108167, 0.0094707385, 0.37881464) * go_1(1.0, 0.0);
result += mat4(-0.031778246, -0.38020673, 0.16956653, 0.33444092, -0.042172886, -0.03465591, -0.17585713, 0.025507452, 0.07595919, -0.06807453, -0.100295454, -0.019174794, 0.07763043, -0.09321411, -0.05212223, 0.112239085) * go_1(1.0, 1.0);
result += mat4(-0.048172995, -0.012284629, 0.12846173, -0.13459995, 0.25443402, -0.013064909, 0.15480834, 0.14016332, 0.036635883, -0.049085367, 0.0506487, 0.26623604, -0.023176057, 0.012088936, -0.1844897, 0.040488705) * go_2(-1.0, -1.0);
result += mat4(0.2147455, 0.17323543, -0.2943051, -0.053386763, -0.023367947, 0.090753146, -0.011997397, -0.0626111, -0.13558747, -0.035944186, -0.014752113, 0.25506687, 0.055502877, 0.31465453, -0.16283247, -0.08967175) * go_2(-1.0, 0.0);
result += mat4(0.033773236, -0.09510872, -0.09313707, 0.046486538, -0.1699796, -0.11685979, 0.22197925, -0.013884658, 0.12514, -0.12129843, -0.09695589, -0.075202964, -0.12321221, 0.18949097, -0.03694664, -0.2306249) * go_2(-1.0, 1.0);
result += mat4(0.08668444, -0.22983012, -0.30873656, 0.07371376, 0.082137264, -0.014844924, 0.2283955, 0.24782042, 0.31113505, 0.14810014, 0.32804835, -0.12014127, -0.17742543, -0.15872951, -0.080107674, -0.16898526) * go_2(0.0, -1.0);
result += mat4(0.29746926, 0.19479977, 0.13996765, -0.4268552, -0.16478531, 0.0835479, 0.45685142, -0.05510062, -0.1282004, 0.12359051, 0.34026766, -0.26152933, -0.13128015, 0.329812, 0.27172327, -0.06600192) * go_2(0.0, 0.0);
result += mat4(-0.06552484, 0.19600633, 0.12407863, -0.13815112, 0.17426166, 0.040930413, 0.06495108, 0.034157254, -0.029772963, 0.015127817, 0.10718436, -0.13752984, -0.0205358, 0.1884735, 0.104591034, -0.020779913) * go_2(0.0, 1.0);
result += mat4(-0.053475305, -0.13616458, 0.05487909, 0.13256747, -0.10030239, -0.12376705, 0.062755466, 0.03264356, 0.068466686, 0.05019395, -0.034875803, -0.17806669, -0.21720818, 0.25592342, -0.2685692, -0.27576914) * go_2(1.0, -1.0);
result += mat4(-0.04562929, 0.04225299, -0.22311088, -0.09517893, -0.19886662, -0.11944208, 0.11044239, -0.10464355, 0.037634842, 0.124069214, 0.0927385, 0.108838566, -0.088783056, 0.17008123, -0.1007014, -0.23137446) * go_2(1.0, 0.0);
result += mat4(0.10306672, 0.027472405, -0.069015354, -0.14412996, 0.24068132, -0.10624665, -0.25597134, 0.05208812, -0.10230778, 0.006520562, -0.11931577, 0.26738268, -0.09168354, 0.13557245, -0.008878644, -0.22292739) * go_2(1.0, 1.0);
result += mat4(-0.09403718, 0.11993688, -0.036254726, -0.053109076, 0.18422048, 0.25203657, 0.10025996, -0.11272799, -0.22040273, -0.05758331, -0.07059054, -0.054108664, -0.20009018, -0.22061199, 0.057880517, -0.26669186) * go_3(-1.0, -1.0);
result += mat4(-0.08534496, 0.0027822452, -0.01112169, -0.13484463, -0.09446875, -0.057457812, -0.03910888, -0.2816038, -0.096015625, -0.03636662, 0.12532772, 0.092033, 0.038156748, -0.101240925, 0.024886698, -0.086328045) * go_3(-1.0, 0.0);
result += mat4(0.2349796, 0.19884427, -0.0734711, 0.08422328, -0.07201622, 0.020658491, 0.1331021, 0.039766714, 0.19280422, 0.13086005, -0.11339721, -0.14782044, 0.19341573, 0.16767374, -0.03593828, 0.18139753) * go_3(-1.0, 1.0);
result += mat4(-0.040663462, -0.15233721, 0.524604, 0.26603413, 0.07202415, 0.053382196, 0.030758869, -0.06144292, -0.010495834, 0.13868876, -0.020688854, -0.15551737, -0.2958513, -0.32805985, -0.25359175, -0.036683984) * go_3(0.0, -1.0);
result += mat4(-0.06644081, -0.145321, 0.24945419, 0.031560224, 0.17245345, 0.23418438, 0.20341763, -0.2619872, 0.038787205, 0.16488725, 0.0019107185, 0.03820528, 0.04169643, -0.34155026, -0.11183654, 0.028614044) * go_3(0.0, 0.0);
result += mat4(-0.028469078, 0.010781976, 0.05263661, -0.15337946, -0.20491667, -0.13879907, 0.13934538, 0.061196275, 0.056804053, 0.063193604, -0.2389496, 0.037072126, -0.058510017, 0.036215063, 0.3074709, 0.10517675) * go_3(0.0, 1.0);
result += mat4(0.028534278, 0.0022668538, 0.04492863, -0.060705435, 0.06349762, -0.016823182, -0.09148226, 0.03930522, -0.083295114, 0.14799853, -0.08089152, -0.21993661, -0.23298621, 0.05106244, -0.013708201, -0.16311577) * go_3(1.0, -1.0);
result += mat4(0.05885827, 0.122300275, -0.16086812, -0.21892425, -0.07548077, 0.09286181, -0.027564062, -0.028723463, -0.0056181233, 0.23472206, -0.0049285595, -0.45054138, 0.07592325, -0.044704806, 0.019616256, -0.06956836) * go_3(1.0, 0.0);
result += mat4(0.036423888, 0.20839189, -0.16420732, -0.15954947, -0.11311323, -0.24191359, 0.19845375, 0.084540576, -0.20946553, 0.09259613, 0.03234368, -0.056766506, -0.11992363, -0.06882079, -0.020428827, -0.093375795) * go_3(1.0, 1.0);
result += vec4(0.013113342, -0.2905848, -0.029724011, 0.1769613);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_5_tf
//!BIND conv2d_5_tf1
//!SAVE conv2d_6_tf1
//!WIDTH conv2d_5_tf.w
//!HEIGHT conv2d_5_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_5_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_5_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.093678355, -0.08574688, 0.007699401, -0.038818456, -0.10667588, 0.043627866, 0.23127791, 0.061317544, -0.32790044, 0.08618836, 0.009400048, -0.17129329, 0.23541448, -0.015561885, -0.11172365, -0.1190039) * go_0(-1.0, -1.0);
result += mat4(-0.0052874424, 0.08136584, -0.12633958, -0.016064916, 0.14033778, 0.07755252, -0.26242834, 0.063312635, 0.06861756, 0.14867078, -0.2561066, 0.33325562, -0.106489345, -0.10068009, -0.039633382, -0.016305668) * go_0(-1.0, 0.0);
result += mat4(-0.27784392, -0.14990395, -0.35981888, -0.2564094, -0.07480205, -0.026457628, 0.1027643, 0.19381845, -0.07160986, -0.15616457, -0.032070953, 0.32998616, 0.15383582, 0.16622585, -0.1435993, -0.02287804) * go_0(-1.0, 1.0);
result += mat4(-0.09360053, 0.58019537, 0.02028909, 0.413114, 0.025173154, -0.030326266, -0.028177274, -0.12964654, -0.25432733, -0.06556034, 0.023097439, -0.09458851, -0.21772051, -0.10324596, -0.36674342, -0.14803977) * go_0(0.0, -1.0);
result += mat4(-0.1227467, 0.20252965, 0.2559927, 0.08719227, 0.030749539, -0.2526622, -0.25694713, -0.2960799, -0.34960067, -0.25393236, -0.28439638, 0.086787805, -0.34202877, 0.21933395, 0.23473133, 0.079260886) * go_0(0.0, 0.0);
result += mat4(-0.00147522, -0.16591258, -0.030617915, 0.10052425, -0.1822102, 0.038774874, -0.04285007, 0.07312042, 0.052175622, -0.33510515, 0.027545406, 0.2995306, -0.08535316, 0.11144203, 0.27999434, -0.09770663) * go_0(0.0, 1.0);
result += mat4(-0.04394928, -0.26842886, -0.08354109, 0.04077001, -0.009221606, 0.0328837, 0.006459338, 0.08984004, -0.13035133, 0.20004508, 0.21950854, -0.12742348, 0.32386312, 0.085903555, -0.29273173, -0.056370437) * go_0(1.0, -1.0);
result += mat4(0.019171638, -0.1824711, -0.10899421, -0.16201603, 0.054712642, -0.020315547, -0.048609916, -0.068621606, -0.055706583, -0.25671515, -0.019494208, 0.08366393, 0.09531471, -0.05988052, -0.024995802, 0.019303525) * go_0(1.0, 0.0);
result += mat4(-0.08694609, 0.26762635, 0.10477892, -0.15392998, -0.059596587, -0.047562487, -0.25932398, -0.054960977, -0.00015596532, 0.07196634, -0.017385524, -0.18826845, -0.017969077, -0.27291682, -0.153906, -0.107691295) * go_0(1.0, 1.0);
result += mat4(0.17340474, -0.1285696, -0.04484238, 0.15782213, -0.06190358, 0.27896214, 0.28475145, -0.042519942, -0.19862229, -0.1354097, 0.14344497, 0.015599392, 0.18698554, 0.035121564, -0.018465763, 0.0010143917) * go_1(-1.0, -1.0);
result += mat4(-0.13428356, -0.06612225, 0.19397905, 0.14209093, 0.1526626, 0.2617573, -0.15316434, 0.35452205, 0.05003259, 0.07679617, -0.008399171, -0.0062716682, 0.11833864, 0.1331285, -0.006803729, 0.22615404) * go_1(-1.0, 0.0);
result += mat4(0.0020632436, -0.173174, -0.15404437, 0.05430569, 0.21100305, 0.39063898, -0.019479724, 0.17396629, -0.061121427, -0.13424753, -0.008459669, -0.04975768, 0.20599939, -0.11374013, -0.21116278, 0.063624285) * go_1(-1.0, 1.0);
result += mat4(-0.0073831948, -0.12009769, -0.16402034, 0.054093774, 0.061061747, -0.009054565, -0.02815144, -0.17071937, -0.22791979, 0.073427565, 0.25161973, 0.1011713, -0.23804636, 0.13810354, 0.09063126, -0.23065178) * go_1(0.0, -1.0);
result += mat4(-0.31885087, 0.21730177, -0.20516786, 0.04075695, -0.2736768, -0.38779113, -0.19445951, -0.14024325, -0.11824961, -0.102919355, -0.17858729, -0.013441498, 0.16320607, -0.27105078, -0.00019549616, 0.024509901) * go_1(0.0, 0.0);
result += mat4(-0.16024838, -0.3132909, -0.15461555, 0.34874174, -0.0051668375, 0.1811257, 0.3384939, 0.16381103, 0.047184363, -0.20424844, -0.1330078, -0.13795874, 0.21890834, -0.08242861, 0.22677775, 0.031102268) * go_1(0.0, 1.0);
result += mat4(0.19408257, 0.016361775, -0.202373, 0.2245766, -0.008954751, -0.047279913, -0.09170596, 0.01567793, -0.0019059096, -0.07785436, 0.0756357, 0.09683383, 0.034215495, -0.030802004, -0.077977195, -0.1101297) * go_1(1.0, -1.0);
result += mat4(-0.1060503, -0.0044663083, -0.14942732, -0.11696249, -0.04550482, 0.11463188, 0.17801443, 0.07229662, -0.14176941, 0.02773344, -0.10770335, -0.08745911, -0.023052111, -0.17474785, 0.016645849, -0.059080444) * go_1(1.0, 0.0);
result += mat4(-0.050500304, -0.14716387, 0.04525464, 0.23543595, 0.08411192, 0.16031684, 0.1659825, -0.03595111, -0.012943453, 0.13354135, -0.051425032, -0.0075654723, 0.11174184, 0.1266808, -0.18799087, 0.10571744) * go_1(1.0, 1.0);
result += mat4(-0.15583408, 0.09837484, 0.19239932, -0.03557196, -0.05406335, 0.096456856, -0.13921897, -0.2212671, 0.28973594, 0.04017474, -0.25423512, 0.1522156, -0.10563249, -0.033190794, 0.101713456, -0.08922746) * go_2(-1.0, -1.0);
result += mat4(-0.0787607, -0.14545321, 0.099762656, -0.2824299, 0.10130184, 0.019948835, -0.1013831, 0.06604923, 0.089561954, 0.28344154, 0.05757009, 0.04981809, -0.15927236, 0.008129835, -0.04280382, 0.10653281) * go_2(-1.0, 0.0);
result += mat4(0.28149363, 0.019583186, 0.25983065, 0.30190885, 0.055435803, -0.01970755, 0.04546505, -0.027456624, 0.43886992, -0.032305803, -0.23557569, 0.12753153, -0.18509789, -0.073295385, 0.0083466545, -0.08271229) * go_2(-1.0, 1.0);
result += mat4(0.016040009, -0.20475672, -0.015803276, 0.18247975, 0.21178837, -0.041543446, -0.24716362, 0.10105528, 0.19479224, -0.06583694, -0.09192672, -0.037776746, 0.09636229, -0.12086331, 0.13989103, 0.014564729) * go_2(0.0, -1.0);
result += mat4(0.19923596, -0.4132588, -0.4254784, -0.33433357, -0.16956097, -0.25086832, 0.23311833, -0.08976422, 0.06432824, -0.0071802614, 0.0033370545, -0.11073493, -0.46609998, -0.09332235, -0.27287352, 0.052513942) * go_2(0.0, 0.0);
result += mat4(-0.06954148, -0.06908355, -0.01875471, -0.35067585, 0.038715206, 0.08843527, 0.28899097, -0.024983376, 0.05879495, 0.110363334, 0.055481512, -0.0046147215, -0.035302363, -0.2722019, -0.0829261, 0.21088009) * go_2(0.0, 1.0);
result += mat4(-0.101971015, -0.18584369, 0.1469676, 0.025965, 0.07205807, 0.08838771, 0.08537094, 0.023344917, -0.106373414, -0.09254277, -0.25996596, 0.24570447, 0.00590166, -0.20074098, -0.05443169, -0.10562662) * go_2(1.0, -1.0);
result += mat4(0.12980327, -0.16834956, -0.1635997, 0.23437372, -0.07374834, 0.0062907683, 0.17292136, 0.0018093853, 0.04122969, -0.025285576, 0.29646805, 0.13402736, -0.040267725, 0.0011441729, -0.18658921, 0.12006417) * go_2(1.0, 0.0);
result += mat4(0.13221453, 0.15109141, 0.07707579, 0.05148666, -0.039716493, 0.12869143, -0.012840577, 0.10953536, -0.05721115, -0.120122276, -0.07632444, 0.32949027, 0.00022400127, 0.22217369, 0.2180494, -0.028773604) * go_2(1.0, 1.0);
result += mat4(-0.08405412, 0.11332542, 0.120847605, 0.00520135, -0.13689686, -0.1459117, -0.029643068, 0.16147274, 0.21844815, -0.036921967, -0.12862785, -0.15930249, -0.11265427, -0.17471205, 0.0026749703, 0.2048758) * go_3(-1.0, -1.0);
result += mat4(-0.03768306, -0.07585988, 0.046583172, -0.35557657, 0.012359812, -0.05498573, 0.19581361, -0.08186999, -0.008727976, -0.16623624, -0.03647879, 0.22760212, 0.048297524, -0.12502927, 0.08636729, -0.26437047) * go_3(-1.0, 0.0);
result += mat4(-0.19518375, 0.17423135, 0.19473018, -0.22721744, -0.25087392, -0.17043075, -0.021999557, -0.27388734, -0.096786864, -0.012226921, 0.16101876, 0.030362492, -0.017619403, -0.2494354, -0.07336028, 0.06842719) * go_3(-1.0, 1.0);
result += mat4(0.13816363, 0.14551367, -0.08497621, 0.15563537, -0.01600614, -0.010629245, 0.007773828, 0.2733634, 0.13066974, -0.2223056, -0.12664202, -0.19242655, -0.13211249, 0.065143794, 0.23912583, 0.19819915) * go_3(0.0, -1.0);
result += mat4(0.001870705, -0.0028601827, 0.14014813, 0.14659253, -0.037523735, 0.3726274, 0.13139205, 0.0112125, -0.16308945, -0.17571904, 0.12799808, -0.032106552, 0.013872656, 0.432307, -0.14197885, 0.24013121) * go_3(0.0, 0.0);
result += mat4(0.117900506, -0.08039036, -0.17504077, -0.08337764, -0.0068703834, -0.07430392, -0.17125578, -0.3470726, -0.20989974, -0.019394008, -0.027336912, 0.18668686, 0.052886557, -0.023217537, 0.004054446, 0.055974416) * go_3(0.0, 1.0);
result += mat4(-0.055653654, 0.08726097, 0.01206228, -0.25783783, -0.08736529, 0.19947968, -0.010166337, 0.36168414, 0.20298903, -0.15769973, -0.21389212, -0.19638214, -0.093130395, -0.067289785, 0.10245741, -0.14167903) * go_3(1.0, -1.0);
result += mat4(0.04559992, -0.102125205, 0.21949212, -0.07308472, -0.15511832, 0.23785073, 0.04275021, 0.085007004, 0.079402514, 0.10851189, -0.151969, -0.29738536, -0.0776658, 0.1113102, -0.18987878, -0.045522977) * go_3(1.0, 0.0);
result += mat4(0.073690206, -0.016468357, 0.122353435, -0.023995928, 0.095143944, 0.23051415, 0.17702249, 0.030164838, -0.09111423, -0.14219609, -0.19734482, -0.24854833, -0.0067356345, -0.1760497, 0.22637916, 0.119141534) * go_3(1.0, 1.0);
result += vec4(0.22705397, -0.029518934, -0.026397338, -0.08183741);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_6_tf
//!BIND conv2d_6_tf1
//!SAVE conv2d_7_tf
//!WIDTH conv2d_6_tf.w
//!HEIGHT conv2d_6_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_6_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_6_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_6_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_6_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.16737834, 0.35369134, 0.14049083, 0.017871622, 0.0058661173, -0.035960242, -0.039154284, -0.01920433, 0.0729212, -0.03617972, -0.42717552, -0.019914677, -0.30816802, -0.07726792, 0.2088459, -0.09198307) * go_0(-1.0, -1.0);
result += mat4(-0.0991125, 0.11411345, 0.15300295, -0.09510225, 0.014268626, -0.42914182, -0.13365223, -0.19440699, -0.27214321, 0.085696176, 0.1527733, -0.21056797, -0.062475704, -0.023041902, -0.29080424, -0.54386055) * go_0(-1.0, 0.0);
result += mat4(-0.30736786, -0.16801229, 0.07400606, -0.31128535, -0.11047924, 0.16556956, -0.33445996, -0.09190697, -0.06132585, -0.11021996, 0.014628762, -0.45183894, 0.08186993, 0.19378273, 0.113438204, 0.038364496) * go_0(-1.0, 1.0);
result += mat4(0.24129803, 0.29174972, -0.1250327, 0.14254767, 0.0026774528, 0.1742466, -0.021835174, 0.01668921, 0.13646975, 0.313305, -0.23293279, -0.16737306, -0.059818722, 0.06404477, 0.108172625, 0.22065729) * go_0(0.0, -1.0);
result += mat4(-0.3504013, 0.20759478, 0.28683922, 0.2771802, 0.13761812, -0.21180478, -0.17020214, -0.21419087, -0.031916566, -0.040439468, 0.39206958, 0.715565, 0.46198523, 0.05055317, -0.07409331, -0.050633535) * go_0(0.0, 0.0);
result += mat4(0.122958206, 0.0071205017, -0.21314384, -0.22197853, 0.016202174, -0.15960938, -0.14601983, -0.023609173, -0.07586023, 0.099936776, -0.0480375, -0.08681468, -0.14976887, -0.38979456, 0.16078879, -0.12263952) * go_0(0.0, 1.0);
result += mat4(0.1687149, 0.108331114, 0.10112296, 0.01738403, -0.06773097, -0.19410455, -0.09728116, 0.0013846151, -0.038603816, -0.05495021, 0.2453317, -0.40052003, -0.022453755, 0.045039784, 0.0474246, -0.2665161) * go_0(1.0, -1.0);
result += mat4(0.06805519, -0.052276067, 0.052459523, -0.0033053474, 0.13439268, -0.06845637, -0.20462433, -0.09088968, -0.00096404477, -0.35103628, 0.15096465, 0.3285226, 0.018747555, -0.06623108, 0.1754265, 0.3211156) * go_0(1.0, 0.0);
result += mat4(-0.04583627, 0.122267574, -0.44002235, -0.20039988, 0.039372742, -0.16505809, -0.26659602, 0.12207268, 0.03337428, 0.23131758, -0.009866899, 0.010381569, 0.29676, -0.020599596, 0.17816995, 0.32852224) * go_0(1.0, 1.0);
result += mat4(0.09469788, -0.12531966, -0.11786524, -0.3115985, -0.2213199, -0.012536277, -0.13176842, 0.14986996, 0.12069894, 0.2744789, 0.21674646, 0.46060535, -0.4101697, -0.55295914, 0.29993954, 0.114459395) * go_1(-1.0, -1.0);
result += mat4(0.18347421, -0.29010707, 0.29127017, 0.087738656, 0.17509815, 0.03982794, 0.1731455, 0.38041735, 0.110374, -0.25045586, 0.36446962, 0.016104888, -0.012112869, 0.10154983, -0.45384112, -0.11416608) * go_1(-1.0, 0.0);
result += mat4(-0.033837743, -0.020894403, -0.287127, -0.21196121, -0.03255823, 0.2599821, -0.38386443, 0.30563655, 0.39044768, -0.112917066, -0.021323297, 0.12623324, 0.06885038, -0.20750642, 0.07642818, -0.103580445) * go_1(-1.0, 1.0);
result += mat4(0.1723114, -0.3726216, -0.21184283, 0.1761503, -0.24993578, -0.31068864, 0.19998416, -0.23127908, -0.052656204, -0.04243976, 0.4397144, 0.01863219, -0.04796025, -0.11009142, -0.0073631364, 0.2716381) * go_1(0.0, -1.0);
result += mat4(0.04202001, 0.27142277, -0.027491128, 0.27428457, -0.11009916, 0.39839938, -0.7223327, -0.124673314, 0.08123618, -0.11884722, -0.20375855, -0.7179687, 0.30648115, -0.28195357, -0.3350774, -0.29778734) * go_1(0.0, 0.0);
result += mat4(0.071278594, -0.09155223, 0.06417857, 0.08250104, -0.45117077, -0.023316784, 0.38917172, -0.19110887, -0.09265943, -0.2643835, -0.09707039, -0.33238646, -0.0818088, 0.17623149, -0.28457013, 0.13986786) * go_1(0.0, 1.0);
result += mat4(0.019971045, -0.046649583, -0.03036858, 0.07944429, 0.26344573, 0.054998036, 0.07139812, 0.21139374, 0.08021858, -0.025791258, -0.0423707, 0.25174072, -0.021300986, 0.13209766, 0.19120613, 0.3840775) * go_1(1.0, -1.0);
result += mat4(-0.11456406, -0.33503455, 0.21409267, -0.056933913, -0.12204284, -0.37379473, 0.33474764, 0.38634798, 0.12618992, 0.1353635, -0.22651522, -0.3160159, 0.18621005, 0.024818055, -0.11935204, 0.014005666) * go_1(1.0, 0.0);
result += mat4(0.1501391, 0.0014716414, -0.22049955, -0.10928345, -0.07085164, -0.08778668, 0.19251469, -0.4932493, 0.071784936, -0.06903646, -0.060333923, 0.020552203, -0.33637995, -0.22848415, 0.21518159, 0.23815839) * go_1(1.0, 1.0);
result += mat4(-0.04230713, -0.19312756, -0.0613665, 0.058912925, -0.17639293, -0.029920885, -0.027867602, -0.16602923, 0.10262268, -0.0743682, 0.15286638, 0.08042581, -0.042299524, 0.0022034592, 0.15304253, 0.049871147) * go_2(-1.0, -1.0);
result += mat4(0.004346093, -0.07895582, 0.02089975, 0.13429636, -0.1020282, 0.5270822, 0.017983409, 0.1531299, -0.02891241, -0.07050933, -0.18729019, 0.13855362, -0.11538968, 0.20733222, 0.1546878, 0.11550679) * go_2(-1.0, 0.0);
result += mat4(0.21800312, 0.20944421, -0.1817274, 0.022868395, -0.019241469, 0.038916696, 0.088702604, 0.1467791, 0.0048542274, 0.10344671, -0.0107803065, 0.23302868, 0.049728952, -0.016042534, -0.08694045, -0.0028224774) * go_2(-1.0, 1.0);
result += mat4(-0.1570157, 0.08688841, 0.03926086, -0.040503077, -0.052700017, -0.1432353, -0.04516745, -0.09649034, -0.053716175, 0.07059194, -0.07360609, 0.26307717, 0.121471435, -0.13640986, -0.1113535, -0.38560814) * go_2(0.0, -1.0);
result += mat4(-0.014722592, -0.39773384, 0.28259715, -0.10905738, 0.07889424, 0.1415529, -0.15419348, -0.2064834, -0.15126482, -0.28288555, -0.0014232624, -0.26178944, -0.025823193, 0.008017357, -0.08547297, 0.26373458) * go_2(0.0, 0.0);
result += mat4(0.2978961, -0.020236012, -0.101216674, 0.15498216, -0.0069343713, -0.088363856, 0.20511419, 0.23958007, 0.045810107, -0.19189738, -0.14137349, 0.04177724, -0.1394684, 0.0071990825, 0.06991723, -0.21052721) * go_2(0.0, 1.0);
result += mat4(-0.05615232, 0.22506002, -0.12479586, -0.0070057763, 0.092545755, 0.096306436, 0.041890718, 0.1226944, -0.07541768, -0.08369033, -0.15144373, 0.09310172, 0.28388003, 0.09935607, 0.11299509, 0.0014283776) * go_2(1.0, -1.0);
result += mat4(-0.005848455, 0.117699094, 0.23539856, 0.11006195, 0.10962903, 0.28139547, 0.18785141, -0.11635996, 0.057289902, 0.2370178, -0.29825503, -0.13706475, -0.3869794, 0.024066223, 0.36742347, 0.35919484) * go_2(1.0, 0.0);
result += mat4(0.13744523, 0.09239356, 0.01173183, 0.119055405, -0.07841836, 0.0668925, 0.22598477, -0.016510552, 0.07971727, -0.17154713, 0.03333588, -0.13790733, 0.15421963, 0.2895701, -0.28440917, 0.015132756) * go_2(1.0, 1.0);
result += mat4(-0.054354303, 0.36663428, 0.02634933, 0.18688667, 0.0607547, 0.17321853, 0.086784445, -0.023283, 0.0027200899, 0.026914112, -0.07438439, 0.27042162, 0.09985293, 0.012430832, -0.20694605, -0.20363812) * go_3(-1.0, -1.0);
result += mat4(-0.42759168, 0.15540305, -0.18979609, 0.0073875943, 0.034251947, -0.34551802, 0.53327596, 0.17446762, -0.25879666, 0.2780996, 0.11094055, 0.17597, 0.13790102, 0.2615357, 0.09666047, 0.36155468) * go_3(-1.0, 0.0);
result += mat4(0.052614138, -0.1880028, 0.361331, 0.07957976, 0.12552904, -0.0042941784, 0.096562445, -0.041199915, 0.07412456, 0.16379668, 0.05464284, 0.050022952, -0.028281605, 0.09332573, 0.21379845, 0.21396561) * go_3(-1.0, 1.0);
result += mat4(-0.07546953, 0.16393837, -0.3060623, -0.64610606, -0.013715101, 0.18005042, 0.045286633, -0.21057944, -0.12779316, -0.10310629, 0.14360385, 0.011625261, 0.05597252, 0.023864657, -0.00018915108, -0.24224915) * go_3(0.0, -1.0);
result += mat4(-0.08550672, 0.2438917, -0.30383766, -0.2463794, 0.13835424, -0.079946786, -0.060197506, 0.051599402, -0.24983203, -0.06691107, -0.0041784844, 0.07539119, -0.030340329, -0.23565106, -0.17968354, -0.10262371) * go_3(0.0, 0.0);
result += mat4(0.19315718, -0.045718513, 0.120446794, -0.225136, 0.22922774, -0.046026126, 0.11448238, 0.114267804, -0.22327735, -0.03368635, 0.29763463, 0.03673529, -0.0583939, -0.092253424, 0.045279544, 0.04475646) * go_3(0.0, 1.0);
result += mat4(-0.062286656, -0.06241419, -0.23600577, -0.24818502, -0.058666106, 0.17710151, -0.1751668, 0.05758226, 0.18278669, 0.033297777, 0.046349872, 0.09178792, -0.0745512, 0.20019765, 0.037281513, 0.22204825) * go_3(1.0, -1.0);
result += mat4(-0.24708512, -0.1318695, -0.24966322, -0.31206796, 0.079176836, 0.11837155, -0.12882641, -0.01013533, -0.009065797, 0.0789075, 0.016151598, 0.00020127615, 0.1450729, 0.10825556, 0.09322918, 0.07283566) * go_3(1.0, 0.0);
result += mat4(0.2604332, 0.25550258, 0.07709474, 0.28426003, 0.10387355, 0.09152259, 0.18742633, -0.0073229484, -0.20327723, -0.26013616, 0.055792782, -0.1713302, 0.14862068, 0.06698207, 0.17608787, -0.11622757) * go_3(1.0, 1.0);
result += vec4(-0.20551574, 0.073114716, -0.21843387, -0.28057778);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_6_tf
//!BIND conv2d_6_tf1
//!SAVE conv2d_7_tf1
//!WIDTH conv2d_6_tf.w
//!HEIGHT conv2d_6_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_6_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_6_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_6_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_6_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.18413043, -0.12355504, 0.2708789, 0.17259507, -0.069752574, 0.12640886, 0.01075919, -0.028221423, -0.020598855, -0.17259665, 0.16907778, -0.10040477, 0.017177016, 0.0176426, 0.23724149, 0.14657862) * go_0(-1.0, -1.0);
result += mat4(0.16921899, -0.33950835, 0.37508205, 0.09996622, 0.13377811, -0.036743056, -0.11633877, -0.23046862, -0.009307903, 0.027441062, 0.054166224, 0.011627087, -0.22831611, 0.043198805, -0.12695734, 0.0062862337) * go_0(-1.0, 0.0);
result += mat4(0.17216596, -0.15588646, -0.14179194, 0.12487524, 0.10507964, 0.124544986, -0.0046104924, -0.116668865, -0.006100901, -0.022074439, 0.03376759, 0.10498887, 0.109659016, -0.03567928, 0.29972833, -0.045950003) * go_0(-1.0, 1.0);
result += mat4(-0.29127, 0.21912472, 0.16494286, 0.027708547, 0.043136686, 0.04409876, -0.07686145, -0.13180132, -0.16630307, 0.15650205, -0.005864527, 0.03916553, 0.15750135, 0.1705246, 0.21626697, 0.06906506) * go_0(0.0, -1.0);
result += mat4(0.055395894, 0.28228188, 0.114794776, 0.020619212, -0.031812593, 0.11964309, -0.24317431, -0.36277202, 0.54564184, -0.032843567, -0.118973784, -0.40999004, -0.118530475, 0.09256661, 0.06583871, -0.36627474) * go_0(0.0, 0.0);
result += mat4(0.17914769, 0.33976436, -0.11220768, 0.1325754, 0.40586957, 0.3064959, -0.19086123, 0.014164092, -0.17376979, -0.0037554938, 0.11771888, 0.44933778, -0.15937245, -0.10635065, 0.084963776, 0.14630255) * go_0(0.0, 1.0);
result += mat4(-0.3723194, 0.21509883, 0.020062352, 0.094394304, 0.030794155, -0.11394617, -0.09103134, -0.0042343247, -0.28981096, -0.061873477, -0.17772584, 0.36440176, 0.007828069, -0.012121627, 0.25862312, 0.24646287) * go_0(1.0, -1.0);
result += mat4(0.10368119, -0.06185447, -0.022830853, 0.10918094, 0.18888599, -0.09235343, -0.055134308, -0.2210923, 0.15334128, -0.3084707, 0.31606838, 0.39931116, 0.29489174, -0.24794856, -0.4799932, -0.2617589) * go_0(1.0, 0.0);
result += mat4(0.32550937, -0.17103608, 0.3257806, -0.23358762, 0.20370598, 0.13325407, -0.020303056, -0.105462655, -0.22264756, -0.034177396, 0.36885822, 0.20504399, 0.36375418, -0.26149705, 0.022433946, 0.15646128) * go_0(1.0, 1.0);
result += mat4(0.007481421, 0.005642636, -0.170087, -0.08915849, 0.6329519, 0.06880098, -0.20856442, -0.1801066, -0.1342754, 0.13643123, 0.26994216, -0.27503812, 0.018052012, 0.058687408, -0.19784917, 0.021157453) * go_1(-1.0, -1.0);
result += mat4(-0.1486918, 0.12212738, -0.03104796, 0.08664756, 0.3464865, 0.27309546, -0.022896903, -0.32080007, -0.28113958, 0.74847424, -0.33735126, -0.04616876, -0.23119605, 0.4214322, -0.16457441, 0.09162191) * go_1(-1.0, 0.0);
result += mat4(0.15863913, 0.1303683, -0.06339421, 0.06328312, -0.3100047, -0.33906308, 0.13805804, -0.14923394, 0.4997829, -0.14977637, 0.02265068, -0.04585939, 0.29802153, 0.3767994, -0.031849556, -0.051892217) * go_1(-1.0, 1.0);
result += mat4(-0.04541847, -0.13645087, 0.14119779, 0.06409465, -0.29877988, -0.0009743694, 0.028256422, 0.14978185, -0.13014801, -0.24171488, -0.10782599, 0.010709664, 0.21880737, -0.34132662, 0.22972895, -0.07159475) * go_1(0.0, -1.0);
result += mat4(-0.1510528, 0.115773134, 0.036761034, -0.284284, -0.35684052, 0.16348189, -0.105475456, 0.08259931, -0.6489164, -0.033928663, -0.04243186, 0.25324553, -0.31829014, 0.066608824, -0.11131264, 0.51919967) * go_1(0.0, 0.0);
result += mat4(-0.06517726, 0.1933327, 0.044391852, -0.013346896, -0.3033368, 0.106350735, -0.1351003, -0.13414839, 0.11720078, -0.24844061, -0.2900742, -0.047861837, 0.42789885, -0.47915378, -0.09643217, -0.22915216) * go_1(0.0, 1.0);
result += mat4(0.109821886, 0.31451595, 0.13300805, -0.08792569, -0.023928089, -0.038061168, 0.17821129, 0.003772247, 0.14684688, -0.12646271, 0.16072205, 0.011095222, 0.09209181, 0.005167038, -0.08823252, 0.079890974) * go_1(1.0, -1.0);
result += mat4(-0.20074554, 0.39979288, -0.007316405, -0.047838025, 0.10849111, -0.22469573, -0.059183244, -0.13663793, 0.07881898, 0.105663374, -0.3152222, 0.08104766, -0.22965154, 0.118780024, -0.07886757, 0.073527716) * go_1(1.0, 0.0);
result += mat4(0.1304303, 0.023158893, -0.081089824, -0.15955788, 0.42183343, -0.12898655, -0.14028409, 0.011985, 0.3977131, -0.313598, -0.148818, -0.048350018, -0.13534498, -0.12760727, -0.014968193, 0.06646305) * go_1(1.0, 1.0);
result += mat4(0.18085147, -0.11859402, 0.117530234, -0.10420847, 0.1848264, -0.12192718, -0.18729533, -0.10098887, 0.011134682, -0.23658146, 0.12963286, 0.117404245, 0.054487415, -0.030003065, -0.32175776, -0.08044254) * go_2(-1.0, -1.0);
result += mat4(-0.07251758, 0.073430285, -0.22191651, 0.030512359, -0.029650904, -0.15816379, 0.0418705, 0.04776615, -0.014070836, -0.14669086, -0.009874937, -0.015444495, -0.2747725, -0.061624944, -0.11261252, 0.14757589) * go_2(-1.0, 0.0);
result += mat4(-0.09274913, 0.046194065, 0.05642919, -0.07803342, 0.23578037, 0.01224276, 0.015608659, 0.05847865, -0.091819406, -0.14424564, -0.034869857, 0.019276984, -0.031180726, -0.21905676, 0.100375675, -0.13659117) * go_2(-1.0, 1.0);
result += mat4(-0.072157644, -0.13294607, 0.24301524, 0.048643183, -0.04338094, -0.0021709928, -0.06530963, -0.22672611, 0.07479903, 0.08388352, -0.07460508, -0.14517406, -0.072923675, -0.26912874, -0.2769797, 0.054033212) * go_2(0.0, -1.0);
result += mat4(-0.5648679, -0.28059873, -0.039906785, -0.39112374, -0.3841447, -0.20383365, 0.12607281, 0.16049421, -0.34394273, -0.022326993, 0.16646549, -0.23433913, 0.071224056, 0.048073303, 0.122035526, 0.14941359) * go_2(0.0, 0.0);
result += mat4(-0.11803124, 0.114169255, 0.018188128, 0.0053847185, -0.07537228, -0.048262373, 0.073838905, -0.041833423, 0.044405136, -0.03813592, 0.076818384, -0.06015139, -0.085042655, -0.14306667, -0.21477652, 0.31548396) * go_2(0.0, 1.0);
result += mat4(0.19307283, -0.014985916, -0.14332882, -0.05549754, 0.14551677, 0.11406769, 0.2744144, -0.031179624, 0.17578745, -0.11309805, 0.010072839, -0.07453384, -0.23163621, 0.19061968, 0.11016298, 0.108093746) * go_2(1.0, -1.0);
result += mat4(0.23180474, -0.12522835, -0.03218773, -0.0031955864, -0.14057393, 0.07269213, -0.20883523, 0.09332164, -0.16037942, 0.25845763, -0.002303125, -0.014625506, 0.17063208, -0.11648214, 0.13988028, -0.024688654) * go_2(1.0, 0.0);
result += mat4(0.043369994, 0.12473897, 0.108142346, 0.10268199, 0.16159926, -0.17804666, -0.007889351, 0.07232418, 0.26326916, 0.0474316, -0.41637155, -0.11879895, 0.14051722, 0.08747377, 0.1162202, -0.06443569) * go_2(1.0, 1.0);
result += mat4(0.0041097966, 0.109841965, 0.097240336, 0.08123332, -0.081065506, 0.12650634, 0.23450434, 0.09631333, 0.21942414, -0.108897425, -0.033703003, 0.047280088, -0.017764917, -0.058596086, -0.15305139, 0.09055131) * go_3(-1.0, -1.0);
result += mat4(0.26824722, 0.014116421, 0.11844865, -0.156046, 0.057152968, 0.21287468, -0.3243975, -0.18181354, -0.07131152, -0.17860547, 0.18918999, 0.15399154, 0.20270234, 0.11524436, 0.05146645, -0.18196748) * go_3(-1.0, 0.0);
result += mat4(-0.2745638, -0.026905773, 0.045458756, 0.22942849, -0.21052304, 0.20649272, -0.03713028, 0.33655703, -0.12467089, -0.015030098, 0.15504798, -0.05647672, 0.18751477, 0.08505986, 0.04756538, -0.058810517) * go_3(-1.0, 1.0);
result += mat4(0.1737789, 0.06552432, -0.34797582, -0.05370679, -0.036056817, 0.085242435, -0.12802805, 0.03710984, -0.09883285, 0.08946925, -0.0446528, 0.07734006, -0.10973603, 0.262812, 0.14010249, -0.1543792) * go_3(0.0, -1.0);
result += mat4(0.316673, -0.16414417, -0.23147403, -0.3080756, -0.056620106, -0.11389848, 0.0948114, 0.13236332, -0.40048537, -0.090742044, 0.12090404, 0.024549136, -0.19124876, -0.3007761, 0.16159211, -0.28620452) * go_3(0.0, 0.0);
result += mat4(0.032962102, -0.05481415, -0.1185786, 0.18153866, -0.2105442, -0.03802839, 0.14060515, 0.072460145, -0.1523761, -0.11426362, 0.02610123, -0.053477813, -0.20768824, 0.04533907, 0.14381588, -0.041578818) * go_3(0.0, 1.0);
result += mat4(-0.021694858, -0.028784249, -0.09928565, 0.07335764, 0.1315628, 0.11288982, 0.078681685, -0.1229723, -0.09618894, -0.07387309, 0.04340066, -0.036534667, 0.37295115, -0.08176548, -0.16579813, -0.13485877) * go_3(1.0, -1.0);
result += mat4(0.45979, -0.289226, -0.15456465, 0.0117592, 0.22803205, 0.15497394, -0.38995707, 0.005227681, -0.20515667, 0.17184737, -0.069968715, -0.24724679, -0.048521046, 0.013277072, 0.049562644, -0.05522196) * go_3(1.0, 0.0);
result += mat4(0.14561136, -0.18995416, 0.18104567, 0.063063085, -0.09728072, 0.018328888, -0.17258182, 0.069259025, 0.15187183, 0.16760696, -0.14086077, 0.013297849, -0.07579904, -0.09294852, -0.24227127, -0.048749007) * go_3(1.0, 1.0);
result += vec4(0.31939298, 0.03303962, -0.010749771, 0.084496394);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-Soft-(VL)-Conv-3x1x1x112
//!HOOK MAIN
//!BIND MAIN
//!BIND conv2d_1_tf
//!BIND conv2d_1_tf1
//!BIND conv2d_2_tf
//!BIND conv2d_2_tf1
//!BIND conv2d_3_tf
//!BIND conv2d_3_tf1
//!BIND conv2d_4_tf
//!BIND conv2d_4_tf1
//!BIND conv2d_5_tf
//!BIND conv2d_5_tf1
//!BIND conv2d_6_tf
//!BIND conv2d_6_tf1
//!BIND conv2d_7_tf
//!BIND conv2d_7_tf1
//!SAVE MAIN
//!WIDTH conv2d_1_tf.w
//!HEIGHT conv2d_1_tf.h
#define g_0 (max((conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_1 (max((conv2d_1_tf1_tex(conv2d_1_tf1_pos)), 0.0))
#define g_2 (max(-(conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_3 (max(-(conv2d_1_tf1_tex(conv2d_1_tf1_pos)), 0.0))
#define g_4 (max((conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_5 (max((conv2d_2_tf1_tex(conv2d_2_tf1_pos)), 0.0))
#define g_6 (max(-(conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_7 (max(-(conv2d_2_tf1_tex(conv2d_2_tf1_pos)), 0.0))
#define g_8 (max((conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_9 (max((conv2d_3_tf1_tex(conv2d_3_tf1_pos)), 0.0))
#define g_10 (max(-(conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_11 (max(-(conv2d_3_tf1_tex(conv2d_3_tf1_pos)), 0.0))
#define g_12 (max((conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_13 (max((conv2d_4_tf1_tex(conv2d_4_tf1_pos)), 0.0))
#define g_14 (max(-(conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_15 (max(-(conv2d_4_tf1_tex(conv2d_4_tf1_pos)), 0.0))
#define g_16 (max((conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_17 (max((conv2d_5_tf1_tex(conv2d_5_tf1_pos)), 0.0))
#define g_18 (max(-(conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_19 (max(-(conv2d_5_tf1_tex(conv2d_5_tf1_pos)), 0.0))
#define g_20 (max((conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_21 (max((conv2d_6_tf1_tex(conv2d_6_tf1_pos)), 0.0))
#define g_22 (max(-(conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_23 (max(-(conv2d_6_tf1_tex(conv2d_6_tf1_pos)), 0.0))
#define g_24 (max((conv2d_7_tf_tex(conv2d_7_tf_pos)), 0.0))
#define g_25 (max((conv2d_7_tf1_tex(conv2d_7_tf1_pos)), 0.0))
#define g_26 (max(-(conv2d_7_tf_tex(conv2d_7_tf_pos)), 0.0))
#define g_27 (max(-(conv2d_7_tf1_tex(conv2d_7_tf1_pos)), 0.0))
vec4 hook() {
vec4 result = mat4(0.121882804, 0.055417646, 0.037575886, 0.0, 0.040015355, 0.10440659, 0.120197006, 0.0, 0.008896276, 0.07269119, 0.09253319, 0.0, 0.009000448, -0.033739295, -0.059260685, 0.0) * g_0;
result += mat4(-0.048027042, 0.09210703, 0.123745404, 0.0, -0.007914943, 0.05483587, 0.054822505, 0.0, -0.005998682, 0.005822986, 0.009868176, 0.0, -0.05866792, -0.04236153, -0.022935968, 0.0) * g_1;
result += mat4(-0.091270015, -0.033997003, -0.012321896, 0.0, -0.037983265, -0.078790314, -0.085029654, 0.0, 0.10656225, 0.0008334142, -0.0041227583, 0.0, 0.077364065, 0.033960085, 0.029391684, 0.0) * g_2;
result += mat4(0.15057671, -0.037442014, -0.037083894, 0.0, 0.015493511, -0.016119987, -0.027061606, 0.0, -0.012329675, 0.0060544596, -0.019787522, 0.0, 0.12182345, 0.11346318, 0.08640806, 0.0) * g_3;
result += mat4(0.19254518, 0.009179287, 0.023821035, 0.0, 0.020269603, 0.025629226, 0.040180814, 0.0, -0.025135614, -0.07785793, -0.099851295, 0.0, -0.122886, 0.03322616, 0.0509256, 0.0) * g_4;
result += mat4(0.060054794, 0.053996198, 0.047226787, 0.0, 0.038959846, -0.025839888, -0.030583512, 0.0, -0.034999896, 0.011966571, -0.011057454, 0.0, 0.05765179, -0.041760337, -0.0694113, 0.0) * g_5;
result += mat4(-0.20393562, -0.0055942894, -0.02089636, 0.0, 0.14781304, -0.01954523, -0.0746086, 0.0, 0.071556985, 0.07512172, 0.067927115, 0.0, 0.084076844, -0.0561336, -0.06856403, 0.0) * g_6;
result += mat4(-0.039552618, -0.04448951, -0.04170605, 0.0, -0.00886809, 0.06708884, 0.07120977, 0.0, 0.04834384, -0.10599933, -0.11024835, 0.0, -0.015948117, 0.084044695, 0.10778199, 0.0) * g_7;
result += mat4(0.050153337, 0.012563414, 0.014994658, 0.0, 0.10498867, 0.07151875, 0.06761489, 0.0, 0.061650798, -0.035183728, -0.050987806, 0.0, 0.0017240314, 0.041055307, 0.020366805, 0.0) * g_8;
result += mat4(0.110105395, -0.044468552, -0.072567016, 0.0, -0.049364448, -0.015713394, -0.021540897, 0.0, -0.01636263, -0.084110685, -0.08281401, 0.0, -0.08940374, 0.047863875, 0.051104594, 0.0) * g_9;
result += mat4(-0.081597924, 0.002422661, 0.01143175, 0.0, -0.07504751, -0.09938017, -0.1063178, 0.0, -0.10390281, 0.0262197, 0.060155805, 0.0, -0.24289346, -0.0054961476, 0.045964316, 0.0) * g_10;
result += mat4(-0.1829316, 0.047622137, 0.07963877, 0.0, 0.048703995, -0.0026299425, -0.003712008, 0.0, 0.029338706, 0.096882835, 0.102083966, 0.0, 0.078538164, -0.07247937, -0.06820231, 0.0) * g_11;
result += mat4(-0.02302231, -0.035528302, -0.030674051, 0.0, 0.029780716, 0.031591274, 0.045867007, 0.0, 0.01335752, 0.037001595, 0.04351411, 0.0, -0.11126892, 0.038589563, 0.06444906, 0.0) * g_12;
result += mat4(0.0047764573, -0.063372664, -0.065609895, 0.0, 0.0478139, 0.025694113, 0.025097322, 0.0, -0.1019169, 0.029989049, 0.050038517, 0.0, 0.07504127, -0.017047737, -0.026222635, 0.0) * g_13;
result += mat4(0.0024485083, 0.00640911, 0.008171829, 0.0, -0.014622121, -0.06078096, -0.0800138, 0.0, -0.0062360805, -0.014344496, -0.021332184, 0.0, 0.117842786, -0.103745885, -0.13756834, 0.0) * g_14;
result += mat4(-0.01942775, 0.08720701, 0.104858086, 0.0, -0.05545872, -0.041375194, -0.035368554, 0.0, 0.080331706, -0.021207837, -0.043905254, 0.0, -0.12515299, 3.445463e-05, 0.018742712, 0.0) * g_15;
result += mat4(0.013106969, 0.010379314, 0.012753471, 0.0, 0.07086715, -0.020893, -0.03968904, 0.0, -0.06114372, 0.029510446, 0.035070244, 0.0, 0.11180839, -0.087067656, -0.124039896, 0.0) * g_16;
result += mat4(-0.056521703, -0.001166792, -2.3704073e-05, 0.0, 0.011961608, 0.01848977, 0.019861937, 0.0, 0.012167056, 0.018613879, 0.020505793, 0.0, 0.009734187, -0.0308419, -0.035206888, 0.0) * g_17;
result += mat4(0.0048758825, 0.018046578, 0.014597015, 0.0, -0.061724614, 0.040989272, 0.05644141, 0.0, 0.070315465, 0.008318584, 0.0028647361, 0.0, -0.11316492, 0.043919202, 0.07653594, 0.0) * g_18;
result += mat4(0.031487904, -0.010548384, -0.009984509, 0.0, -0.0022647562, 0.0043304027, 0.0029451603, 0.0, -0.0063251094, -0.013420807, -0.011919729, 0.0, -0.022760967, 0.019141173, 0.01782793, 0.0) * g_19;
result += mat4(0.023055293, 0.028219413, 0.024810018, 0.0, 0.031653803, 0.050207954, 0.04504577, 0.0, 0.03877294, 0.0280465, 0.025589157, 0.0, 0.0019387804, 0.023891818, 0.016049948, 0.0) * g_20;
result += mat4(0.006562233, 0.03880659, 0.037682824, 0.0, -0.021441424, -0.011277022, -0.012471097, 0.0, -0.030526241, -0.013880651, -0.014213582, 0.0, 0.0075785257, -0.0017350517, -0.0024610942, 0.0) * g_21;
result += mat4(0.015097556, 0.020325955, 0.015611413, 0.0, -0.014755199, -0.034323387, -0.032325987, 0.0, -0.008603291, 0.010346807, 0.011044969, 0.0, -0.004739154, -0.026397636, -0.01995132, 0.0) * g_22;
result += mat4(0.0097906375, -0.015094543, -0.016887931, 0.0, -0.0007786067, -0.0069163437, -0.008449091, 0.0, 0.025534432, 0.018064791, 0.017047096, 0.0, 0.00055667467, 0.001493328, 0.003636564, 0.0) * g_23;
result += mat4(-0.042251963, -0.042396102, -0.040224236, 0.0, -0.004492444, -0.0069470624, -0.0065821502, 0.0, 0.062203273, 0.06213223, 0.053592753, 0.0, 0.06424337, 0.07964681, 0.07316769, 0.0) * g_24;
result += mat4(0.026366957, 0.02789826, 0.027239393, 0.0, -0.006712127, -0.0035723334, -0.0032348586, 0.0, -0.04960562, -0.062758155, -0.058574595, 0.0, -0.02896146, -0.020999067, -0.021301663, 0.0) * g_25;
result += mat4(-0.013106142, -0.017057793, -0.014653614, 0.0, -0.04254173, -0.043040022, -0.041918345, 0.0, -0.011146975, -0.0043820064, -0.003768677, 0.0, -0.0027743059, -0.0114479, -0.0082087545, 0.0) * g_26;
result += mat4(-0.10087762, -0.10447133, -0.1005168, 0.0, -0.04165659, -0.04558967, -0.040086865, 0.0, 0.0016493691, 0.0055392827, 0.0070476984, 0.0, -0.018665023, -0.035552308, -0.03375731, 0.0) * g_27;
result += vec4(0.018580848, -0.022256816, -0.0266178, 0.0);
return result + MAIN_tex(MAIN_pos);
}
@@ -0,0 +1,873 @@
// MIT License
// Copyright (c) 2019-2021 bloc97
// All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//!DESC Anime4K-v4.0-Restore-CNN-(VL)-Conv-4x3x3x3
//!HOOK MAIN
//!BIND MAIN
//!SAVE conv2d_tf
//!WIDTH MAIN.w
//!HEIGHT MAIN.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (MAIN_texOff(vec2(x_off, y_off)))
vec4 hook() {
vec4 result = mat4(0.1690102, -0.2560719, 0.39658326, -0.3679659, -0.27616683, -0.35619372, -0.3748396, 0.08430813, -0.29574734, -0.31511316, -0.09773105, 0.13616018, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, -1.0);
result += mat4(-0.1326393, -0.259433, 0.025070239, 0.58914864, -0.036478516, 0.30723435, 0.007458902, 0.012962684, 0.2493056, 0.13007334, -0.08448256, -0.38414413, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 0.0);
result += mat4(-0.11539356, 0.35253766, 0.26143202, 0.2760807, -0.09371543, -0.028165473, -0.028452158, -0.27050856, 0.06718067, -0.0056619495, -0.17654495, 0.17288211, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 1.0);
result += mat4(-0.16145481, -0.3204927, -0.54317135, 0.11830119, 0.49315026, 0.12008072, 0.50857407, -0.30382085, 0.25807253, 0.020755528, 0.29388228, 0.106109895, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, -1.0);
result += mat4(-0.22728722, 0.50484747, -0.07904469, 0.33114597, 0.50306976, -0.22760947, 0.14773269, 0.17628263, 0.14788547, -0.08223464, -0.10880935, -0.3151985, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 0.0);
result += mat4(0.3414351, 0.057279214, -0.14419858, 0.09761111, -0.11794496, 0.021717256, -0.22750235, 0.13986664, -0.38932344, 0.28996095, 0.3773904, 0.13175532, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 1.0);
result += mat4(0.1376552, -0.19587159, -0.35147396, -0.097646296, 0.1686707, -0.14385861, 0.031198, 0.12383533, -0.23089902, 0.08707301, 0.3362293, -0.100579016, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, -1.0);
result += mat4(-0.056774966, 0.047585852, -0.36395878, -0.20211312, 0.4077735, 0.12631284, 0.39813092, -0.033365678, 0.2307249, -0.09131807, 0.20823865, 0.31084216, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 0.0);
result += mat4(-0.12456089, 0.09755632, 0.31490886, -0.06579996, -0.13386595, 0.07564795, -0.26605195, -0.075180635, -0.11182657, 0.06757017, -0.14351276, -0.16828312, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 1.0);
result += vec4(-0.046043985, 0.055581126, -0.08791638, -0.13022089);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(VL)-Conv-4x3x3x3
//!HOOK MAIN
//!BIND MAIN
//!SAVE conv2d_tf1
//!WIDTH MAIN.w
//!HEIGHT MAIN.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (MAIN_texOff(vec2(x_off, y_off)))
vec4 hook() {
vec4 result = mat4(-0.15485518, -0.29363206, -0.22610365, -0.14291525, -0.45240572, -0.18319772, -0.12209436, 0.15031648, 0.09878383, 0.06711082, 0.25763842, -0.084633484, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, -1.0);
result += mat4(-0.10204406, 0.16167697, 0.22371867, -0.37947702, -0.24476196, -0.038824454, 0.060157117, 0.15764871, -0.08072927, -0.2210841, -0.31835055, 0.009979876, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 0.0);
result += mat4(0.20506924, 0.21132155, -0.0922578, -0.07430473, 0.14529926, 0.20549752, 0.0077948375, 0.13246094, -0.32353187, 0.21074104, 0.092629515, 0.17590871, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 1.0);
result += mat4(0.04125819, -0.44050243, 0.23729716, 0.3218237, 0.12943116, -0.011674174, 0.10390632, 0.027775545, -0.20308031, -0.16904089, -0.2121676, -0.022515794, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, -1.0);
result += mat4(0.09664124, 0.20127031, 0.60345304, 0.16697013, 0.23093723, -0.38116834, 0.109695725, 0.0007595324, 0.4092646, 0.009624758, 0.11229678, 0.25326383, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 0.0);
result += mat4(0.014879592, 0.19204311, 0.07102085, -0.7312604, 0.34860876, 0.3429918, -0.027331594, 0.27636307, 0.1342437, 0.107820466, -0.12645108, 0.21081445, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 1.0);
result += mat4(-0.12687613, -0.09247973, -0.25973785, 0.4350873, -0.18987224, 0.028678741, -0.0903819, -0.63974863, 0.205591, 0.11308998, 0.18458389, -0.4149041, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, -1.0);
result += mat4(0.34691808, -0.025498383, 0.3428986, 0.21663484, 0.23404741, -0.1725327, -0.0036315925, -0.13299675, -0.1873967, 0.031331502, -0.08785591, -0.0013278709, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 0.0);
result += mat4(-0.35846514, 0.048703704, -0.104165934, 0.16529736, -0.15378916, 0.26030356, -0.07134151, 0.03692383, -0.15807101, -0.18885155, 0.044707954, -0.11444462, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 1.0);
result += vec4(-0.0022791293, -0.024132347, -0.57621074, 0.028573977);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_tf
//!BIND conv2d_tf1
//!SAVE conv2d_1_tf
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.010346764, 0.07230188, -0.24734616, -0.09937907, 0.02228549, -0.19550583, -0.019540425, -0.1037373, 0.033996485, -0.075554, -0.20228972, 0.07090153, -0.09194035, -0.058972966, 0.1768268, 0.27517542) * go_0(-1.0, -1.0);
result += mat4(0.020078976, 0.12433655, -0.1620775, 0.036401592, 0.079748705, 0.11660013, 0.17917652, -0.017513236, -0.18936846, 0.24478136, -0.45726213, -0.045004416, -0.08295188, 0.067733586, -0.080548316, 0.2744211) * go_0(-1.0, 0.0);
result += mat4(0.024916803, 0.27562472, 0.043771956, -0.012240604, 0.0786355, 0.042651594, 0.16049327, -0.14577515, -0.032735053, 0.17658092, 0.16382934, -0.02337374, 0.11551492, 0.056343183, -0.17930213, 0.14259394) * go_0(-1.0, 1.0);
result += mat4(0.20010485, 0.06747722, -0.19026905, 0.11013709, 0.13062745, -0.044626113, -0.0062261797, 0.2189639, 0.1403497, -0.022713251, -0.19452858, -0.010305412, -0.06407589, 0.09836748, 0.025805516, 0.23430973) * go_0(0.0, -1.0);
result += mat4(-0.14664203, 0.034910418, 0.024714258, -0.066872925, -0.15717538, -0.14179383, -0.14091893, 0.05859166, 0.18919097, -0.18544437, -0.09068573, -0.08615929, -0.051434122, 0.2170678, 0.18409058, -0.17461225) * go_0(0.0, 0.0);
result += mat4(-0.11354446, 0.10745854, 0.2682663, 0.05949201, -0.10695986, 0.1407851, -0.03551388, 0.10691649, -0.17148238, -0.38287184, 0.2074456, 0.11828914, 0.048535194, 0.1464864, -0.18169662, -0.14074169) * go_0(0.0, 1.0);
result += mat4(0.22160622, -0.1513045, -0.053284165, 0.033202525, 0.15574448, -0.043640967, -0.0093824165, -0.0019965349, -0.097964935, -0.08289824, 0.08239996, 0.07868361, 0.05731752, -0.20441617, -0.013016076, -0.253108) * go_0(1.0, -1.0);
result += mat4(-0.031249097, -0.2272863, 0.23573665, 0.03357689, 0.011395065, -0.10885564, -0.06287508, -0.031719524, 0.10331069, 0.17560169, 0.18303394, 0.022961004, -0.17011635, -0.24371737, 0.10678694, -0.3222825) * go_0(1.0, 0.0);
result += mat4(-0.1275465, -0.08844758, 0.10994917, -0.00910273, 0.09393154, 0.03894992, 0.14367905, -0.11811715, -0.09077633, -0.015776094, 0.27427456, -0.13283503, 0.18724327, -0.08139094, 0.04933602, -0.051852766) * go_0(1.0, 1.0);
result += mat4(-0.06764611, -0.27426586, 0.12045272, 0.09410856, -0.14258035, 0.11802992, -0.09093882, 0.0022018093, 0.4590643, 0.046258576, -0.07827223, 0.448011, -0.103631735, -0.016930219, -0.15421398, 0.11045997) * go_1(-1.0, -1.0);
result += mat4(-0.17295076, 0.00151352, 0.14938255, 0.08336512, -0.07496541, -0.07561223, -0.0846474, 0.14979269, -0.09142163, 0.23925088, -0.015199518, -0.37749895, -0.20636298, -0.022585187, -0.20371509, 0.0745308) * go_1(-1.0, 0.0);
result += mat4(0.06458832, -0.009722021, -0.123604394, 0.06548835, -0.3039139, -0.022024399, 0.05297587, -0.0626883, 0.23556642, 0.1516464, -0.07004877, -0.1845364, -0.05918428, 0.19158973, -0.14983447, 0.030489758) * go_1(-1.0, 1.0);
result += mat4(0.36604697, 0.17516142, -0.10853731, -0.22694224, -0.107650936, 0.23013335, 0.094055794, -0.17047717, -0.3006048, -0.08621717, -0.18815655, -0.03570218, 0.09676118, -0.017718751, 0.059138596, 0.073388465) * go_1(0.0, -1.0);
result += mat4(-0.12791575, 0.101956226, 0.13091874, -0.046373338, 0.04955811, -0.04030444, 0.13869923, -0.046699073, -0.42611042, -0.7173929, 0.052184317, 0.6178025, -0.02929954, -0.07638965, -0.15000828, 0.030710017) * go_1(0.0, 0.0);
result += mat4(0.057806686, 0.20842272, -0.20148766, 0.006666912, 0.13356528, -0.45265228, -0.07354092, 0.21447696, 0.019552143, -0.13645506, 0.14643854, -0.0071413796, -0.15487236, -0.002250615, 0.30622452, 0.0033902125) * go_1(0.0, 1.0);
result += mat4(0.06896002, 0.24397352, -0.06479052, 0.20676947, -0.24259068, 0.055320013, -0.09032122, -0.11222854, -0.08982342, -0.114818625, -0.06399291, -0.3024516, -0.06302166, -0.1925528, 0.03458982, 0.028828239) * go_1(1.0, -1.0);
result += mat4(0.09764086, 0.09599894, -0.0073313303, 0.14418933, -0.045712367, 0.12657364, 0.04620374, -0.069778584, 0.30047333, -0.012418192, 0.15516461, -0.18087754, 0.08178273, 0.14262857, -0.01741533, -0.12509112) * go_1(1.0, 0.0);
result += mat4(0.04697884, -0.1506804, 0.031823065, 0.13397239, -0.18396698, 0.10681781, -0.29586303, -0.0039136545, 0.17560847, -0.12486726, -0.018646788, -0.20688744, -0.030614454, -0.0527634, 0.23593572, -0.10542146) * go_1(1.0, 1.0);
result += mat4(-0.19182229, -0.32615846, 0.26283535, -0.1371942, -0.071202695, 0.12056063, -0.11450658, -0.27711076, -0.42096004, 0.0014352369, 0.1559669, -0.14464542, -0.17973948, 0.079166576, -0.12501791, -0.20623216) * go_2(-1.0, -1.0);
result += mat4(0.12469872, 0.32190827, -0.059510354, 0.1393449, -0.12845798, -0.019571869, -0.22630808, -0.14031963, 0.36072046, 0.05858427, 0.19278921, 0.121090546, -0.067538865, -0.018770566, 0.14318037, -0.15561756) * go_2(-1.0, 0.0);
result += mat4(0.024663208, 0.21110268, -0.016415706, 0.060093414, -0.03739678, -0.107412934, -0.077527136, 0.30331334, 0.17196326, -0.15512557, -0.09499732, -0.15748607, -0.16680105, -0.015185634, 0.16114107, -0.21288376) * go_2(-1.0, 1.0);
result += mat4(-0.17739037, -0.1190967, 0.13191372, -0.2527187, -0.14992718, -0.30511454, 0.19145966, 0.002194003, -0.12888977, 0.19152176, 0.27528167, 0.099714965, 0.12865707, -0.12051514, -0.055013947, 0.26231763) * go_2(0.0, -1.0);
result += mat4(0.46433613, -0.11708138, -0.20157282, 0.32022122, 0.079468675, 0.029407484, 0.2559102, -0.15651533, 0.08644574, -0.09747344, -0.07528584, 0.17354868, 0.19167562, -0.17698488, -0.09896657, 0.17093097) * go_2(0.0, 0.0);
result += mat4(0.20283653, -0.33680332, 0.2282385, 0.18832158, 0.20866042, 0.00076752366, 0.16471444, -0.21548858, 0.16193539, 0.17141372, 0.03140222, 0.03913644, -0.030161971, 0.00014570929, 0.08993654, -0.064823024) * go_2(0.0, 1.0);
result += mat4(-0.3075755, 0.19942546, 0.015526995, -0.120868504, -0.254515, -0.07791228, 0.03271691, 0.11794217, 0.11258601, 0.045204375, -0.061196107, -0.115958795, 0.3861869, 0.048215542, 0.07016682, -0.009975758) * go_2(1.0, -1.0);
result += mat4(-0.07623697, 0.16094944, -0.02283455, 0.14112763, -0.051149167, 0.20429814, 0.011314802, 0.18914083, -0.24240434, -0.08784008, -0.16763984, -0.08492233, 0.31062725, -0.11925119, -0.33195966, 0.2060798) * go_2(1.0, 0.0);
result += mat4(-0.016709225, -0.14472668, -0.3677625, -0.09832719, 0.030297454, -0.05775362, -0.1401375, 0.08119674, -0.01795042, 0.05183797, -0.24320887, 0.066842034, -0.22245285, -0.02740993, 0.06316751, 0.053399116) * go_2(1.0, 1.0);
result += mat4(-0.039214406, -0.08876633, 0.045552462, 0.19226661, 0.1355001, -0.13942362, 0.17398876, 0.2914014, -0.191809, 0.037143208, 0.013333581, -0.16632195, 0.113767646, -0.106692605, 0.1589787, 0.030107044) * go_3(-1.0, -1.0);
result += mat4(0.21997562, 0.13855208, -0.05783191, -0.033682413, -0.010961168, 0.10524961, 0.02177416, 0.18289444, 0.043692037, 0.07853899, -0.039936125, -0.1004449, 0.04494073, -0.020680292, 0.17578089, -0.106598996) * go_3(-1.0, 0.0);
result += mat4(0.026852835, -0.16037546, 0.11278316, 0.12656097, -0.006857894, -0.03400118, -0.051564034, 0.00085412664, -0.37556714, -0.05279987, 0.029383834, -0.14246808, -0.056380164, -0.002399925, 0.16025752, 0.036324855) * go_3(-1.0, 1.0);
result += mat4(0.022709966, 0.046350412, 0.03390721, 0.02810572, -0.14394265, 0.04215361, -0.3206118, 0.15034916, -0.0028448137, 0.1682989, -0.042686664, 0.020543462, -0.2786501, -0.007482015, -0.040313292, -0.20745736) * go_3(0.0, -1.0);
result += mat4(0.05417556, 0.18728684, -0.046121832, -0.27939513, 0.05907976, -0.09191223, -0.16625418, -0.26038164, 0.39956605, -0.052594025, -0.0596556, 0.29517552, -0.015181923, -0.0763375, 0.25131205, 0.13038464) * go_3(0.0, 0.0);
result += mat4(-0.036903054, -0.0066989153, -0.062650286, 0.05614359, -0.0064960583, 0.028512698, -0.10906273, -0.010047654, 0.23030473, 0.049983572, 0.10439064, 0.26643834, 0.05041243, 0.09185424, -0.32352915, 0.11295159) * go_3(0.0, 1.0);
result += mat4(0.09724027, -0.34962535, 0.06586686, 0.016635379, 0.13831381, 0.01707076, -0.04690347, 0.022350075, 0.018352794, 0.022000022, 0.070613205, 0.117735535, -0.025971051, 0.18832101, -0.09643588, -0.08512127) * go_3(1.0, -1.0);
result += mat4(-0.17324433, 0.06810613, -0.057295907, -0.05115964, -0.101570815, 0.12491774, 0.08762367, -0.005862404, -0.05342927, -0.031942457, -0.039624047, -0.04298937, -0.1303138, -0.11869282, -0.024832053, 0.070463404) * go_3(1.0, 0.0);
result += mat4(-0.010514842, 0.1376259, -0.11750346, -0.03786737, 0.03459249, 0.015408171, -0.031430878, -0.060825355, -0.072958425, -0.0037895301, 0.041686177, -0.12352204, -0.06261361, 0.054514423, -0.34072715, 0.13860728) * go_3(1.0, 1.0);
result += vec4(0.018166734, -0.11002478, -0.05554318, -0.0988193);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_tf
//!BIND conv2d_tf1
//!SAVE conv2d_1_tf1
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.040142782, 0.0288423, 0.07569487, -0.01490842, 0.14402796, -0.13682005, 0.027765118, 0.03907358, 0.07117706, 0.058157545, -0.23862502, -0.057674367, -0.19220531, 0.0147159435, -0.18028538, 0.0963821) * go_0(-1.0, -1.0);
result += mat4(-0.1676744, -0.11937339, 0.12137117, 0.07119485, 0.14148116, -0.043578617, -0.029261118, -0.0016938087, -0.057269357, -0.080076694, 0.12193026, 0.07326153, -0.056278303, -0.01630716, -0.03792076, 0.1483611) * go_0(-1.0, 0.0);
result += mat4(-0.3021578, 0.011601693, 0.11266048, 0.19086999, -0.0122412145, 0.08431291, 0.11615175, -0.008039614, -0.39987534, 0.07820729, 0.03509667, 0.1963505, -0.08839513, -0.21571854, 0.059425723, -0.06830175) * go_0(-1.0, 1.0);
result += mat4(0.23135209, -0.12452708, 0.0943565, 0.0028859286, -0.09836373, 0.10681712, -0.3535964, 0.08457615, 0.045332734, 0.16579892, -0.03809797, -0.021596594, 0.2937497, -0.028294371, 0.046484597, -0.037604347) * go_0(0.0, -1.0);
result += mat4(0.072675414, -0.16431206, 0.28952035, 0.0076831076, -0.020242939, 0.029483542, -0.092415355, 0.08673106, 0.12109694, 0.14307201, 0.23134442, 0.11731775, 0.09981601, -0.16968462, 0.037470713, 0.14948717) * go_0(0.0, 0.0);
result += mat4(0.0029752052, 0.06526503, 0.1866458, 0.07451277, -0.31836876, 0.17115082, -0.13969697, 0.23844297, -0.03244903, -0.08832665, 0.023691226, -0.18230624, -0.074933805, -0.00044301842, 0.050572682, 0.081511915) * go_0(0.0, 1.0);
result += mat4(0.039502528, 0.051221415, -0.13968123, -0.091212444, -0.016925618, 0.15409444, -0.017455677, -0.11653652, 0.03539446, -0.00087720866, -0.12839639, 0.037198763, 0.03674469, -0.26444665, 0.019721227, -0.13013805) * go_0(1.0, -1.0);
result += mat4(0.039229527, 0.25667152, 0.0032586441, -0.00718359, 0.1617932, 0.10409968, 0.07182867, -0.09810605, 0.07789241, -0.02014911, 0.025767172, -0.14604759, 0.07175764, 0.32513744, -0.20473222, -0.16266066) * go_0(1.0, 0.0);
result += mat4(0.13418433, 0.061813723, -0.13927278, -0.2498272, 0.03468218, 0.29483125, 0.063289374, -0.04726235, 0.1898295, -0.33132064, 0.032045014, 0.02159535, -0.1148363, 0.31306976, 0.06456038, 0.048988886) * go_0(1.0, 1.0);
result += mat4(0.07151646, 0.2799246, -0.107190795, -0.16431166, -0.28007045, 0.07206954, 0.06775463, 0.009758042, 0.07032184, -0.20843789, 0.087045245, 0.1360676, -0.25718534, 0.028249472, -0.12614648, 0.009949602) * go_1(-1.0, -1.0);
result += mat4(0.020241471, -0.23390484, -0.0083223935, 0.08344701, 0.08222297, 0.12026539, -0.08652223, -0.08228822, -0.039576706, -0.24677879, -0.1157289, 0.2590508, -0.23809408, 0.19911982, -0.116798095, -0.035870325) * go_1(-1.0, 0.0);
result += mat4(0.024991842, 0.050509237, -0.024134455, -0.12659028, 0.24089767, 0.122712664, -0.10482493, -0.19403952, -0.19177693, -0.06538376, -0.041478425, 0.32176673, -0.1534002, -0.18680622, 0.06763643, 0.020806564) * go_1(-1.0, 1.0);
result += mat4(0.03437814, -0.28067374, 0.2830681, 0.038812317, -0.021698112, -0.120865285, 0.22695538, -0.045419116, -0.030475847, -0.01977341, -0.1265364, -0.3109814, 0.012255813, 0.053917278, -0.018620957, -0.14599285) * go_1(0.0, -1.0);
result += mat4(-0.016204128, -0.04093018, 0.054571863, 0.02679643, 0.01756274, -0.057685968, 0.16148666, 0.17370272, -0.11065411, 0.06378157, -0.09331551, 0.22985275, 0.057905316, 0.12323568, 0.07748665, 0.09878629) * go_1(0.0, 0.0);
result += mat4(-0.018112244, 0.063234635, -0.013184602, 0.16241394, 0.08877139, 0.02145378, -0.02490027, -0.038920373, 0.13127136, 0.14391647, 0.020553736, 0.14401346, 0.06685973, -0.25398204, 0.10369067, -0.055949755) * go_1(0.0, 1.0);
result += mat4(0.07710333, 0.047412727, 0.13813803, 0.18624061, 0.16907091, -0.039532468, 0.06234584, 0.06408178, -0.054543987, -0.045220226, -0.11093376, -0.37399602, 0.20372874, 0.004580967, -0.07742308, 0.017989937) * go_1(1.0, -1.0);
result += mat4(0.003485311, -0.08897399, -0.013108594, -0.19473282, -0.27081844, -0.16812073, 0.0052992934, -0.055331517, 0.09446357, 0.019280333, 0.16560757, -0.3230032, 0.043096773, 0.059222896, -0.064184934, -0.059852477) * go_1(1.0, 0.0);
result += mat4(0.06794279, -0.034135245, 0.083064295, 0.13506731, 0.13064219, -0.44978833, -0.03513717, 0.08999715, 0.1124541, 0.42208397, -0.0038724816, -0.014332087, -0.13751853, -0.04929869, 0.09134992, -0.17687531) * go_1(1.0, 1.0);
result += mat4(0.100909084, -0.0131197255, 0.082274795, -0.2138443, -0.08515947, -0.021058358, 0.10951775, -0.06349191, -0.29129833, -0.029262653, 0.25235432, -0.11748315, 0.121980384, 0.062347785, 0.10916932, -0.15993518) * go_2(-1.0, -1.0);
result += mat4(0.28893283, -0.05677308, -0.2641288, -0.058937225, -0.16187571, 0.006647366, -0.063294955, 0.04766719, 0.60601914, -0.07831864, -0.15710756, -0.011491797, 0.15587467, -0.08105375, 0.07847514, -0.2803333) * go_2(-1.0, 0.0);
result += mat4(-0.077989794, -0.09871811, -0.3516344, 0.15292728, 0.010889273, 0.0011189661, -0.16118282, -0.018821161, -0.039708678, -0.00060983415, -0.06367813, 0.009148068, 0.03919827, 0.18782744, 0.028040757, -0.10230145) * go_2(-1.0, 1.0);
result += mat4(-0.4079609, 0.18640275, -0.12475227, 0.13891742, 0.25121725, 0.16942379, 0.14409852, 0.087600805, 0.045335658, -0.12683709, -0.0077387216, 0.06563413, -0.19857128, 0.106910795, -0.048285246, 0.10768945) * go_2(0.0, -1.0);
result += mat4(0.5989075, 0.20941062, -0.20086494, 0.13344856, 0.073034994, 0.22358665, 0.101664364, -0.13463663, 0.18816395, -0.061176624, -0.14712185, 0.027320342, -0.09529667, 0.031148786, -0.28744993, 0.18698911) * go_2(0.0, 0.0);
result += mat4(0.14799193, 0.39471942, -0.23340325, -0.4031061, 0.18926248, -0.11091216, 0.118981816, -0.09155061, 0.17049436, 0.19803695, -0.1513267, 0.023817873, 0.0090933135, -0.04134864, 0.060486555, 0.03536634) * go_2(0.0, 1.0);
result += mat4(-0.39094314, 0.01779997, 0.12710269, 0.0067333193, -0.31255835, -0.08206612, -0.048528638, 0.369439, -0.19351655, -0.03420455, 0.15831526, -0.052294146, -0.08481741, 0.0787108, 0.1312136, -0.108919285) * go_2(1.0, -1.0);
result += mat4(-0.16068119, -0.42190582, 0.19383872, -0.018445708, 0.09803051, -0.020769652, -0.022599563, -0.052448895, -0.20645833, -0.031432863, 0.0025441595, 0.03410379, -0.20268854, 0.04481527, 0.05191063, 0.42317194) * go_2(1.0, 0.0);
result += mat4(-0.12786235, -0.23936178, 0.116561726, 0.30756372, -0.09420156, -0.044529166, -0.03585749, 0.1829332, -0.23939075, 0.24030831, 0.019878127, -0.015069802, 0.24300557, -0.22558568, -0.104956664, -0.09393648) * go_2(1.0, 1.0);
result += mat4(-0.04607054, 0.012677649, -0.027597688, 0.1618836, 0.29210827, 0.014221155, -0.13591036, -0.06895336, -0.09559534, 0.07956421, -0.11112994, -0.13325493, 0.24562472, 0.11046177, 0.057847694, 0.0016315983) * go_3(-1.0, -1.0);
result += mat4(-0.03365951, 0.027391057, 0.09653403, -0.14718771, -0.049631152, -0.06467214, -0.058545876, 0.1424002, -0.06320376, 0.181183, 0.10249362, -0.16052136, 0.3013475, -0.04156266, 0.08862033, 0.06888033) * go_3(-1.0, 0.0);
result += mat4(0.10045977, -0.004198456, -0.025856055, 0.05739418, -0.1328637, -0.025975171, 0.06553717, 0.11301186, 0.0704087, -0.083569765, 0.16066101, -0.24453588, 0.25370175, 0.037184533, 0.062386766, -0.20025635) * go_3(-1.0, 1.0);
result += mat4(-0.017958941, 0.06417776, -0.1525265, 0.12451173, 0.14567685, -0.0049682115, -0.23973411, -0.0783304, -0.010629432, 0.08055161, 0.2028341, 0.17640644, -0.20445108, -0.055524793, -0.019326134, 0.081288636) * go_3(0.0, -1.0);
result += mat4(0.007882519, -0.03722546, 0.053249408, 0.00071846246, -0.07053029, -0.21583866, 0.1415364, -0.19486657, 0.20685542, 0.17660026, -0.32156837, 0.1746825, -0.14957622, -0.09224378, -0.098153435, -0.13054638) * go_3(0.0, 0.0);
result += mat4(0.10051427, -0.17398237, 0.09842799, -0.14187703, 0.116901085, -0.1229543, -0.0007776771, -0.20410055, -0.11373484, -0.111150615, -0.1974002, -0.11641459, 0.024105398, 0.24985977, 0.015871854, -0.10724633) * go_3(0.0, 1.0);
result += mat4(-0.18081793, 0.1209351, -0.12867971, -0.019415248, 0.062617876, -0.037130393, -0.07803658, -0.22862352, 0.2586428, -0.030090366, -0.11894069, 0.18087515, -0.40921417, 0.070013195, 0.030540073, 0.035120826) * go_3(1.0, -1.0);
result += mat4(-0.13185939, 0.12992652, 0.08125049, 0.075331174, 0.064219765, 0.056629725, -0.020012032, -0.0855444, -0.044063166, -0.05396545, -0.028002812, 0.21837157, -0.15206428, -0.12681007, 0.14895032, 0.12339962) * go_3(1.0, 0.0);
result += mat4(0.08066341, -0.14773634, -0.0212227, -0.014011867, -0.048505764, 0.075407125, -0.020620076, 0.0003291325, -0.21815202, -0.23136546, 0.10853532, -0.036058456, 0.10952532, -0.052677035, -0.13005799, 0.18398996) * go_3(1.0, 1.0);
result += vec4(0.022609137, -0.028548084, 0.024431901, 0.010504478);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_1_tf
//!BIND conv2d_1_tf1
//!SAVE conv2d_2_tf
//!WIDTH conv2d_1_tf.w
//!HEIGHT conv2d_1_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_1_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_1_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.069641694, 0.104958326, 0.14786446, 0.027633663, -0.004279524, -0.020451711, 0.0883571, -0.016224537, 0.13585235, 0.11078269, 0.20198658, -0.042161036, 0.020466218, 0.20994963, 0.20072585, -0.028024657) * go_0(-1.0, -1.0);
result += mat4(0.050872434, 0.12874635, 0.1298729, 0.115810685, 0.07087254, 0.09885682, 0.23018982, 0.19187538, 0.10953604, 0.0033836907, -0.13325337, 0.09830315, -0.06528767, 0.05096927, -0.016355392, -0.039334368) * go_0(-1.0, 0.0);
result += mat4(0.027010268, 0.018263958, 0.0360758, 0.016791478, 0.2815702, 0.15517488, 0.43415815, 0.044976447, -0.0070842914, -0.12546758, 0.16874593, 0.077622116, 0.02252915, 0.1769774, 0.07181055, -0.15128697) * go_0(-1.0, 1.0);
result += mat4(0.057129618, 0.118046716, 0.07237424, -0.07842637, -0.044214778, -0.12886304, 0.08603301, -0.10416606, -0.15852053, 0.3788151, 0.26181692, -0.09092249, 0.31635332, 0.064212754, 0.21923725, 0.07500004) * go_0(0.0, -1.0);
result += mat4(-0.16981383, 0.044409662, -0.3717617, -0.031610407, 0.03658662, -0.09459229, -0.09449437, -0.014000666, -0.19656453, 0.03934163, -0.16304104, -0.12761801, -0.06235523, 0.16438273, -0.036933117, -0.095564745) * go_0(0.0, 0.0);
result += mat4(0.09725091, 0.034022827, 0.17699842, 0.1079676, -0.13236652, 0.03718181, -0.06968635, -0.23288171, 0.10275666, 0.08464966, -0.37162134, -0.35782215, -0.11023659, 0.2519236, -0.035197742, -0.019324787) * go_0(0.0, 1.0);
result += mat4(-0.09968464, 0.01102193, 0.0073735216, 0.011999313, -0.004998707, 0.09518938, 0.045727003, -0.21544908, 0.006879454, -0.06398254, -0.12584935, -0.06759933, -0.0820037, -0.07775104, 0.021957919, -0.122708224) * go_0(1.0, -1.0);
result += mat4(-0.08869767, 0.031296413, -0.0034280645, 0.13778855, 0.10073061, -0.08393937, -0.032959275, -0.0500518, 0.010908757, -0.09189417, -0.057760105, 0.17652664, -0.08729078, -0.09639096, -0.25654703, 0.055152636) * go_0(1.0, 0.0);
result += mat4(0.0027847723, -0.12885433, 0.038065907, 0.17450769, 0.0864409, 0.04592345, -0.015443841, 0.077010944, 0.08967368, 0.06800111, -0.23636387, 0.35023567, 0.03165923, 0.03132063, 0.17964344, 0.035610788) * go_0(1.0, 1.0);
result += mat4(-0.032017227, -0.0022808525, -0.08470573, 0.05332408, -0.14674746, 0.025374275, -0.018281924, 0.041163016, 0.00096549373, 0.014724006, 0.004913065, 0.18494442, 0.034953076, -0.15731992, -0.13792977, 0.08041999) * go_1(-1.0, -1.0);
result += mat4(0.08305006, 8.6318905e-05, -0.007895379, 0.02731387, -0.061324496, 0.050034665, 0.22662131, -0.013876427, -0.074468784, -0.008136604, -0.23337875, -0.1742574, 0.011753501, -0.11666686, -0.22541048, -0.14549944) * go_1(-1.0, 0.0);
result += mat4(-0.028333234, 0.121047184, 0.06720256, -0.058930036, 0.030258363, 0.07292774, 0.06455556, 0.0019076486, 0.0073987027, 0.17144889, 0.06084024, -0.08762086, -0.114422195, -0.16595861, -0.08706028, -0.10736261) * go_1(-1.0, 1.0);
result += mat4(-0.02519315, -0.14611271, 0.0388848, 0.19481422, -0.05970354, -0.08391417, 0.18982239, -0.10447052, 0.15587378, -0.023997072, 0.0781739, 0.2182389, -0.023886079, -0.1422596, -0.13352804, 0.005008043) * go_1(0.0, -1.0);
result += mat4(0.08842712, -0.100292705, 0.18925671, 0.12198875, 0.061771665, -0.04473232, 0.025053164, 0.039047796, -0.1672479, -0.08934517, 0.33099812, -0.20269585, -0.21640155, -0.22029749, 0.16539703, -0.2442679) * go_1(0.0, 0.0);
result += mat4(-0.16332205, -0.101898365, 0.02919932, -0.11900455, 0.14442924, 0.0916815, 0.037550304, 0.024123482, 0.02042624, 0.033472955, -0.059437107, -0.18735693, -0.013749093, -0.06199881, -0.08685079, 0.04252364) * go_1(0.0, 1.0);
result += mat4(-0.09047013, -0.055188328, -0.09106191, -0.048969727, 0.05114009, -0.12753403, 0.07116141, 0.060749624, -0.074034564, -0.21952136, -0.09479503, 0.2753584, -0.014141759, -0.14883812, -0.0673838, -0.012279045) * go_1(1.0, -1.0);
result += mat4(0.013816464, -0.0747162, -0.19202435, -0.064403646, 0.34980014, 0.04375546, 0.20264609, 0.006684355, 0.11523799, 0.024674915, -0.08697566, -0.04662527, -0.12743855, -0.39463726, 0.0057380227, 0.01286557) * go_1(1.0, 0.0);
result += mat4(-0.08146522, 0.074080914, -0.16856177, -0.183158, 0.19228102, 0.12373886, 0.017574452, -0.01753772, 0.045071773, 0.07725093, 0.023422163, -0.011545186, 0.20751388, -0.10795588, 0.07606346, 0.10282933) * go_1(1.0, 1.0);
result += mat4(0.12512013, -0.102208994, -0.09125398, 0.12043188, -0.066011876, 0.08831903, -0.017038671, -0.005541508, -0.049607087, 0.08654939, -0.02037085, 0.26887566, 0.005012545, 0.01869507, -0.013064982, -0.010649147) * go_2(-1.0, -1.0);
result += mat4(0.006824864, -0.05071593, -0.20786697, -0.07327317, 0.011382597, 0.030494886, -0.04754353, -0.018284699, 0.01305972, -0.036589053, 0.26637617, 0.021887446, -0.026669119, -0.037982125, -0.063445956, -0.009104248) * go_2(-1.0, 0.0);
result += mat4(0.032602567, 0.07094331, 0.052653246, 0.08342047, -0.085082285, -0.14674088, -0.23073354, -0.07915851, 0.0017120204, 0.032407638, -0.039819505, 0.16942178, 0.023192152, -0.0353237, 0.10930186, 0.22939779) * go_2(-1.0, 1.0);
result += mat4(0.0010455973, -0.11821993, -0.12639599, 0.12250084, -0.12756817, 0.11478416, -0.1862587, 0.016819192, 0.02110181, -0.25492984, -0.1766048, 0.22188173, -0.21305011, 0.113442205, 0.04599144, -0.15840286) * go_2(0.0, -1.0);
result += mat4(-0.15086032, -0.17428935, 0.39080557, 0.07576757, 0.121703945, 0.17944208, -0.003140103, -0.11231332, 0.12102969, 0.15310267, 0.17578171, 0.40631834, -0.21299168, 0.024928993, 0.030104794, 0.020753227) * go_2(0.0, 0.0);
result += mat4(-0.098734386, -0.020072265, -0.14308836, -0.08490801, 0.017175158, 0.02250534, 0.04060829, 0.033022214, 0.0046218676, 0.17923212, 0.0112105915, 0.09574084, 0.14819936, -0.14692923, 0.12634254, 0.060762513) * go_2(0.0, 1.0);
result += mat4(0.030521613, -0.097913325, -0.016720278, 0.11273997, 0.013019863, -0.06557118, 0.0405774, 0.0915019, 0.022414956, -0.053254984, 0.18639986, 0.07820968, 0.06498986, 0.058922634, -0.02240318, -0.086019725) * go_2(1.0, -1.0);
result += mat4(0.2058775, 0.01502064, 0.05847032, 0.007249146, 0.086483665, 0.19420148, 0.03892261, -0.013546935, -0.07980237, 0.04347281, -0.10376214, -0.1366535, 0.05285337, 0.07213318, 0.3642818, -0.11331124) * go_2(1.0, 0.0);
result += mat4(-0.025740806, 0.14551482, -0.037410017, -0.17477523, -0.11853099, -0.060820814, -0.102599286, -0.13267937, -0.103053465, -0.014044828, -0.01888072, -0.06499249, 0.22311528, -0.051850274, -0.034120858, 0.044562567) * go_2(1.0, 1.0);
result += mat4(-0.21360217, 0.10093803, -0.0016407765, -0.1473997, 0.26524043, 0.02112132, 0.23173104, -0.013157391, 0.05945182, 0.044635538, 0.06031638, -0.21435826, -0.10147484, 0.069090195, 0.09641844, -0.09581093) * go_3(-1.0, -1.0);
result += mat4(-0.08576515, -0.122861005, 0.049567085, -0.085854456, 0.23809357, -0.024966082, -0.10294079, 0.046241313, 0.008621132, -0.08323767, 0.20277941, 0.163423, -0.07386535, -0.088738985, 0.05274358, -0.025479877) * go_3(-1.0, 0.0);
result += mat4(-0.041135542, -0.008365642, 0.17088248, 0.04025207, 0.13809255, -0.056895368, -0.01582834, 0.07361908, -0.00068995473, -0.09300962, 0.19117641, 0.24832036, -0.06572358, -0.026025, -0.019093119, -0.049720034) * go_3(-1.0, 1.0);
result += mat4(0.024900286, 0.11525501, 0.025882801, 0.037742402, 0.36976853, 0.052211333, -0.15143296, 0.1802276, -0.059080046, 0.017990451, 0.026395092, -0.12689115, -0.07705386, 0.1232379, 0.13273561, -0.12521964) * go_3(0.0, -1.0);
result += mat4(-0.19788785, 0.044887315, 0.07663442, 0.16688696, -0.2842248, -0.15684547, 0.028387763, 0.0063470444, -0.012245601, -0.038382255, -0.8187406, -0.25245667, 0.23014604, 0.22746666, 0.1594356, 0.16469443) * go_3(0.0, 0.0);
result += mat4(-0.12663333, 0.014730006, 0.03765697, 0.15704912, -0.106595434, -0.05317512, -0.081759915, -0.08797109, 0.064620756, -0.06341419, 0.16493447, 0.23102313, 0.068325415, -0.088058695, 0.16885915, 0.036382258) * go_3(0.0, 1.0);
result += mat4(0.035389822, -0.11811836, -0.035656307, -0.0680554, 0.1338908, 0.065852076, 0.023307983, 0.0675308, 0.09690683, 0.18170924, 0.09862692, -0.20964378, -0.08601271, -0.20016764, -0.01879598, -0.14629345) * go_3(1.0, -1.0);
result += mat4(-0.27183273, 0.013525998, -0.14995874, -0.23938845, -0.26218823, -0.0009874097, -0.13385512, -0.10664239, -0.048931994, 0.039898522, 0.047444753, 0.10934722, 0.10969629, 0.123539805, 0.11692802, 0.14172275) * go_3(1.0, 0.0);
result += mat4(-0.1656506, 0.019683002, 0.0221048, 0.12596753, 0.20420644, -0.07930122, 0.04653823, 0.11492255, -0.0050175437, -0.03271697, 0.013389486, 0.034583613, -0.2196601, -0.1615663, -0.013763388, -0.056037936) * go_3(1.0, 1.0);
result += vec4(-0.022956269, 0.029688787, -0.070148066, -0.07163476);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_1_tf
//!BIND conv2d_1_tf1
//!SAVE conv2d_2_tf1
//!WIDTH conv2d_1_tf.w
//!HEIGHT conv2d_1_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_1_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_1_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.15104648, 0.05522861, -0.0654341, -0.053517453, -0.08264124, -0.0062249107, -0.20364265, -0.05015117, -0.18837251, 0.030655831, 0.046844713, -0.20673253, -0.14042036, -0.05655449, 0.13994302, 0.011745607) * go_0(-1.0, -1.0);
result += mat4(-0.16517559, 0.1489214, -0.09149559, 0.025003506, -0.124926426, 0.16974348, -0.020857265, 0.08017403, 0.21836148, 0.0025619378, 0.2331612, 0.085599184, -0.030934382, -0.055194855, 0.09527726, -0.10081552) * go_0(-1.0, 0.0);
result += mat4(0.041800212, 0.028859638, 0.09395546, 0.05211183, -0.038541477, 0.021495212, 0.04862346, -0.007864793, 0.038407274, -0.13841268, -0.14963801, 0.26470762, 0.16691841, -0.07262008, 0.034374326, -0.14709206) * go_0(-1.0, 1.0);
result += mat4(0.00094978884, -0.028974704, -0.0900548, -0.08401967, -0.08935931, -0.043606587, -0.14497143, -0.05226239, -0.21516493, 0.19410603, -0.089924194, -0.04335071, -0.012618276, -0.2671613, 0.020422975, -0.037739716) * go_0(0.0, -1.0);
result += mat4(-0.13403237, -0.02524383, -0.03474901, 0.054432765, 0.11946775, 0.107336655, -0.1431715, -0.13370377, 0.015087512, -0.1917613, 0.073493585, 0.2788855, -0.010510839, 0.06891479, -0.06741307, -0.05271205) * go_0(0.0, 0.0);
result += mat4(-0.15432046, 0.04021662, -0.16979513, 0.13660534, -0.10518303, -0.10095502, -0.13092068, 0.022805348, -0.16676381, -0.4273298, 0.020867536, 0.3506733, -0.29459694, -0.055828743, -0.069241956, 0.04106382) * go_0(0.0, 1.0);
result += mat4(-0.08890133, 0.07549666, -0.040735144, -0.1506932, -0.22227979, -0.0762723, -0.17766447, -0.05741318, -0.21885683, 0.2379157, -0.15525854, -0.07306285, 0.15580738, -0.04394069, -0.19175608, 0.018283797) * go_0(1.0, -1.0);
result += mat4(-0.08503275, -0.105500385, -0.114987396, -0.07166016, -0.2147138, 0.09378708, 0.24550334, -0.0834075, -0.033147786, -0.022304727, -0.31062204, 0.027651973, 0.109098755, 0.18889032, 0.1163026, 0.13863255) * go_0(1.0, 0.0);
result += mat4(0.15266588, -0.14901319, 0.033916786, 0.09381096, -0.08196443, -0.16194504, 0.035789456, 0.21234898, -0.48724765, 0.2619442, -0.11215393, 0.25061038, 0.022344576, 0.0116525125, 0.111661114, -0.15242295) * go_0(1.0, 1.0);
result += mat4(0.020475458, 0.0797404, -0.13576819, 0.009681671, 0.030504882, 0.049232908, 0.022025917, 0.16912088, -0.23914136, -0.084663324, 0.020925451, -0.1023938, 0.035916872, -0.07538111, -0.11470242, 0.15238516) * go_1(-1.0, -1.0);
result += mat4(-0.12941381, 0.08509899, -0.029489802, -0.09148447, -0.089406274, -0.116145454, -0.08979843, 0.11908148, 0.15473351, -0.21687616, 0.12607013, -0.08244334, -0.079580925, -0.16613089, -0.09287793, -0.03412643) * go_1(-1.0, 0.0);
result += mat4(-0.023578499, 0.07394217, -0.13069086, -0.1060499, -0.07559958, -0.21839201, 0.1090753, 0.0787872, 0.07677037, -0.25998843, 0.20039314, 0.046882212, 0.31871012, -0.3048051, 0.15118991, -0.00518087) * go_1(-1.0, 1.0);
result += mat4(-0.15338503, -0.11057532, 0.075839415, -0.18592294, -0.0155324, 0.038140323, -0.10498194, 0.09070477, 0.05108992, -0.047939524, -0.091004305, 0.09649005, -0.10967152, -0.051909525, -0.05314551, 0.09661584) * go_1(0.0, -1.0);
result += mat4(-0.14458802, -0.053263694, -0.0010885567, 0.23342133, 0.01918937, 0.12026143, -0.15691495, 0.30480555, -0.08725869, 0.19082253, 0.3594973, 0.016653897, 0.045152336, -0.088590585, 0.0069655925, 0.1392425) * go_1(0.0, 0.0);
result += mat4(0.17944881, -0.17950764, 0.13282645, 0.030974053, 0.32233685, 0.18067117, -0.11472813, 0.097301506, -0.047649745, -0.1053861, -0.081039384, 0.035132434, 0.10204545, 0.085582554, -0.13153993, -0.021741152) * go_1(0.0, 1.0);
result += mat4(-0.15573682, 0.16409989, -0.22574787, -0.03877603, -0.18285516, 0.11638645, 0.18321282, -0.017770218, 0.18230622, 0.16433364, -0.12795393, -0.03805153, 0.14386104, -0.0891527, -0.056928284, -0.10961495) * go_1(1.0, -1.0);
result += mat4(0.257622, 0.052519716, -0.25421762, -0.1887382, -0.083568096, -0.0064690276, -0.029110614, 0.103327505, -0.17006217, 0.2254096, -0.29366904, 0.04302887, -0.10198446, -0.24423616, 0.16781262, -0.005019004) * go_1(1.0, 0.0);
result += mat4(0.103393994, -0.059044626, -0.18192382, 0.0990813, -0.26143607, 0.11036474, 0.04788275, -0.096738026, 0.12825653, 0.13631694, -0.077904984, -0.020790676, -0.25118098, 0.122588515, -0.049440473, -0.10758222) * go_1(1.0, 1.0);
result += mat4(0.06693113, -0.13647175, 0.131139, 0.13143918, 0.081720434, 0.117537096, 0.15387627, -0.008771362, 0.08513583, 0.023794742, -0.0661625, 0.115793936, 0.0023350024, 0.02215075, -0.0494433, -0.013404977) * go_2(-1.0, -1.0);
result += mat4(0.041419264, -0.17622781, 0.028418267, 0.12114493, -0.23587078, 0.08457395, 0.014364018, -0.103271864, -0.051572207, -0.026424447, 0.16755055, -0.10763651, -0.033440586, 0.068594255, -0.050668504, 0.1941505) * go_2(-1.0, 0.0);
result += mat4(-0.2780181, 0.037816502, -0.11516711, -0.09822884, 0.13762361, -0.14317706, 0.14350282, 0.000623895, -0.08601606, 0.08118504, 0.15497385, -0.04721711, -0.008936935, -0.014223618, -0.09641698, -0.013884213) * go_2(-1.0, 1.0);
result += mat4(0.14349665, -0.03144472, -0.057813704, 0.0667044, 0.09026094, 0.051366236, 0.11139983, -0.015782114, -0.18314016, -0.18774192, 0.0014838242, 0.15759028, 0.062388215, 0.13626057, 0.02576217, -0.06317815) * go_2(0.0, -1.0);
result += mat4(0.07151769, 0.14508991, 0.1736844, -0.11487795, -0.07999805, -0.07797908, 0.037923355, -0.059138823, -0.23531209, -0.040207293, -0.068355694, -0.024296658, -0.114820175, 0.19726487, 0.21772414, 0.03659222) * go_2(0.0, 0.0);
result += mat4(0.16858695, -0.12135113, 0.009391182, -0.081519485, 0.13340487, 0.07007004, 0.094124354, 0.035519842, -0.3320139, -0.06624027, -0.14716229, -0.09205287, 0.12664132, -0.05655441, 0.0123263765, 0.04641279) * go_2(0.0, 1.0);
result += mat4(0.19018422, -0.15428329, -0.009354114, 0.04165953, 0.11024837, -0.107493006, -0.05807292, -0.048029456, 0.24319384, -0.10542357, -0.013699952, 0.06228662, -0.06808749, -0.023227982, 0.16528323, -0.05610251) * go_2(1.0, -1.0);
result += mat4(-0.008616222, 0.077674195, -0.08638503, 0.09293109, 0.072474636, 0.05004233, -0.20591061, -0.005301386, -0.15486047, 0.15038474, 0.1262478, 0.021724822, 0.02274613, -0.3088281, -0.08437887, -0.10684698) * go_2(1.0, 0.0);
result += mat4(-0.16960032, 0.09365251, -0.030414175, -0.010766254, 0.18181023, 0.12130318, 0.08913089, -0.06070321, 0.05200306, 0.092584535, 0.17694671, 0.033796314, -0.038107123, -0.04335955, -0.049443472, 0.30465958) * go_2(1.0, 1.0);
result += mat4(0.07661484, -0.009945252, 0.12866217, -0.07592757, -0.21030053, 0.014371748, -0.072458774, -0.04700072, 0.15534303, 0.2007125, -0.15699059, -0.032897495, 0.08110436, -0.11243608, 0.008632577, -0.10153441) * go_3(-1.0, -1.0);
result += mat4(-0.034697928, 0.06928288, -0.2796273, 0.14405379, 0.12248569, 0.036539096, 0.06607706, 0.077684596, -0.16473202, 0.1665916, -0.29977503, 0.21047153, 0.13114224, -0.091579035, -0.045458574, 0.03254245) * go_3(-1.0, 0.0);
result += mat4(0.053284872, 0.053366095, -0.26152626, -0.03123967, -0.031794485, 0.17670582, -0.07450994, 0.017521491, -0.040290453, 0.38342363, -0.25021288, -0.014660264, 0.1621895, 0.25041878, -0.12124821, 0.068036206) * go_3(-1.0, 1.0);
result += mat4(0.11366693, -0.030863572, -0.07411263, 0.12475283, -0.046070684, -0.09033321, 0.013222701, 0.06798592, -0.32814804, 0.057653826, -0.14082801, -0.00217398, -0.22856179, -0.19058353, -0.20992154, -0.03701372) * go_3(0.0, -1.0);
result += mat4(0.20345633, -0.1332355, 0.27152926, -0.13477845, -0.25242096, -0.28281286, 0.31289554, 0.14284514, 0.53362453, -0.46766588, 0.4518293, -0.39291728, -0.3573227, -0.014670052, 0.0051881406, 0.16552156) * go_3(0.0, 0.0);
result += mat4(-0.15017267, -0.07792945, -0.204405, 0.13964304, -0.13642666, -0.10228306, 0.03238279, -0.08689329, -0.072262034, -0.0258388, 0.05689183, 0.055701543, -0.19800112, 0.012217054, -0.033292748, -0.047611095) * go_3(0.0, 1.0);
result += mat4(-0.014704416, -0.12203891, 0.066083655, -0.1409769, 0.0041513643, -0.087383606, -0.17498164, 0.11327789, -0.25947225, -0.0016027623, 0.08202566, 0.042270098, 0.006429511, -0.26576808, -0.08461341, 0.049376782) * go_3(1.0, -1.0);
result += mat4(0.0695189, -0.14753938, 0.09578246, -0.16607563, -0.0105561055, 0.17166016, 0.027422488, -0.14175262, -0.009492696, -0.23449713, 0.018270867, 0.14635146, 0.33451268, 0.030959005, -0.46468422, 0.024256868) * go_3(1.0, 0.0);
result += mat4(-0.16865666, -0.00015881563, -0.054488145, -0.06222717, -0.032101758, 0.06485387, -0.0028512608, 0.046645947, 0.017593225, -0.19447896, -0.024742266, 0.03970127, 0.29845607, -0.16168733, 0.035172883, 0.07924657) * go_3(1.0, 1.0);
result += vec4(0.103826486, 0.045373913, 0.11565896, -0.06568643);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_2_tf
//!BIND conv2d_2_tf1
//!SAVE conv2d_3_tf
//!WIDTH conv2d_2_tf.w
//!HEIGHT conv2d_2_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_2_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_2_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.1851775, 0.053705044, 0.033816848, -0.018555025, -0.21204336, -0.01706974, 0.088259794, -0.13126148, 0.10729598, -0.043457437, 0.08634712, 0.09220895, 0.062131613, -0.01995871, 0.05181067, 0.18520063) * go_0(-1.0, -1.0);
result += mat4(0.1662002, -0.14197104, -0.052809287, 0.025287712, -0.08330898, -0.08998097, -0.15642618, -0.14941245, -0.03481203, 0.061857622, 0.26051775, -0.0005498248, 0.086427025, 0.024108192, -0.12418039, 0.022286376) * go_0(-1.0, 0.0);
result += mat4(0.058200672, -0.3073398, 0.17150162, -0.13394679, -0.075118184, -0.14607768, -0.006172172, 0.007731589, -0.21818224, -0.06449433, -0.038958784, 0.037722416, 0.28699976, -0.027563032, 0.23295315, 0.028444216) * go_0(-1.0, 1.0);
result += mat4(0.12871371, 0.0064904913, 0.14985761, -0.10923005, 0.17413563, 0.1599109, -0.08457703, 0.108153716, -0.08871187, -0.06661137, 0.2754416, -0.009667768, 0.39819396, 0.12392097, 0.14145902, 0.0019376524) * go_0(0.0, -1.0);
result += mat4(0.13893189, 0.12715353, 0.015191678, -0.21003054, -0.030412354, -0.01676613, -0.19799289, -0.006130075, 0.37676954, -0.14475077, -0.2065198, -0.30432892, -0.14944535, -0.09121536, -0.107600585, -0.24462196) * go_0(0.0, 0.0);
result += mat4(-0.11653076, -0.0068671284, -0.02249137, -0.17877012, -0.15063138, -0.13514869, 0.107643366, -0.03196477, -0.086422764, 0.3079287, 0.17584166, -0.032449376, -0.06917114, -0.2682637, -0.18978168, -0.037039287) * go_0(0.0, 1.0);
result += mat4(0.12014731, -0.030360512, -0.12954475, -0.110275604, -0.077214256, 0.019689744, 0.22149551, -0.002266716, 0.09697784, -0.124532826, -0.16776511, -0.034212478, -0.36935154, 0.016926935, 0.1363609, 0.20415346) * go_0(1.0, -1.0);
result += mat4(-0.11199535, -0.001692563, -0.09058429, -0.08437503, 0.092625685, 0.06046257, 0.25509837, -0.011657033, -0.17949764, -0.10718947, -0.1180669, -0.24681842, -0.1747311, 0.0014518246, -0.042863015, 0.06103357) * go_0(1.0, 0.0);
result += mat4(0.14979295, -0.037154514, 0.01957725, 0.012282435, 0.09168596, -0.05552286, 0.111671515, 0.0078630615, -0.10319766, -0.06416261, -0.23097566, -0.13931875, 0.2110811, 0.013095802, -0.2306504, -0.025639111) * go_0(1.0, 1.0);
result += mat4(-0.10091975, -0.10095426, -0.023449723, -0.022170888, 0.054953706, -0.13049407, 0.08289061, 0.023241632, 0.08735388, -0.0058387457, 0.17897247, 0.011434436, 0.008181139, -0.0034718404, -0.015372735, -0.07657766) * go_1(-1.0, -1.0);
result += mat4(-0.023442164, 0.07535702, 0.024391165, -0.050532013, 0.044168636, 0.0062343236, -0.019756999, -0.009695123, 0.10102337, 0.0052776975, -0.14944167, -0.060957722, 0.24367364, -0.08069369, 0.12170072, -0.047048368) * go_1(-1.0, 0.0);
result += mat4(-0.18376935, -0.08407229, -0.12943378, 0.0738419, -0.12404976, -0.13367929, 0.11265896, -0.021353, 0.003783386, 0.50088304, 0.14058582, 0.041053623, 0.038247623, -0.014179976, 0.007905778, -0.042492237) * go_1(-1.0, 1.0);
result += mat4(-0.046272535, 0.052449115, 0.17190954, -0.004745371, -0.045572635, -0.09292636, 0.36309823, 0.16673928, -0.099154025, -0.109614775, 0.17803112, 0.19907133, -0.14306267, 0.06898593, 0.11493454, 0.06795014) * go_1(0.0, -1.0);
result += mat4(0.26181114, -0.044014625, -0.21605036, -0.08646438, 0.21038742, -0.084986, 0.0504626, 0.17514943, -0.25218952, -0.18691514, 0.057650108, 0.08653614, -0.101205684, 0.03176334, 0.18569492, 0.17973189) * go_1(0.0, 0.0);
result += mat4(-0.0339215, 0.20112811, -0.12986277, 0.028961731, -0.056813832, 0.04451147, -0.07827432, -0.0860976, 0.096853435, 0.3483546, -0.35758162, -0.11749375, -0.035918653, 0.06140711, -0.08520154, 0.02418808) * go_1(0.0, 1.0);
result += mat4(-0.09643022, -0.10491069, 0.0068604187, 0.023679713, 0.096521445, -0.29323488, 0.33353668, 0.112864286, -0.1172182, -0.07233183, 0.06607239, 0.08589609, 0.055790007, 0.14396138, -0.14191268, 0.00034840964) * go_1(1.0, -1.0);
result += mat4(0.15357164, -0.038462736, 0.08143956, 0.1744909, 0.40503287, -0.114508316, 0.003937322, 0.2536635, -0.042445306, -0.15622465, 0.09155284, 0.010992155, -0.20646071, 0.022801135, 0.08894491, 0.069300614) * go_1(1.0, 0.0);
result += mat4(-0.12663515, 0.023849454, -0.053604446, 0.12082873, -0.247968, -0.020969635, -0.03831894, -0.014617553, 0.22630337, 0.037801865, 0.052950703, 0.04285706, -0.14487264, 0.20786528, -0.08719664, 0.1752347) * go_1(1.0, 1.0);
result += mat4(-0.073527604, -0.050752833, 0.051830504, 0.32868716, 0.17474994, 0.016937364, -0.08792601, -0.024481766, -0.022229593, 0.030706186, 0.09213566, -0.076506205, 0.073404044, 0.10368055, -0.175889, -0.08453031) * go_2(-1.0, -1.0);
result += mat4(-0.06838216, 0.007698341, 0.063972116, -0.015604406, 0.16135305, 0.18044342, 0.024137018, -0.23326185, 0.13235588, -0.009096587, -0.058368143, -0.077040404, 0.0011419816, -0.09246194, 0.061036937, 0.049564146) * go_2(-1.0, 0.0);
result += mat4(0.023225296, -0.00060856267, -0.07775185, 0.016958566, -0.2641349, -0.08263046, -0.15350416, -0.30203494, 0.113956556, -0.010813236, -0.017738314, -0.13689043, -0.10318342, 0.025793184, -0.010336172, 0.09733422) * go_2(-1.0, 1.0);
result += mat4(-0.04462596, 0.052866418, -0.34754288, 0.05540498, -0.24492586, -0.32016864, 0.18145293, 0.24873725, 0.32388234, -0.034801524, -0.1347588, -0.07565546, 0.015183539, 0.05059595, 0.08090056, 0.05930932) * go_2(0.0, -1.0);
result += mat4(0.045346696, -0.052527856, 0.052270077, 0.13417454, 0.05200045, 0.028119288, 0.005115497, 0.22952151, -0.2158375, 0.12241308, 0.3507457, 0.08616576, 0.07592416, 0.28470486, 0.3432788, 0.24857087) * go_2(0.0, 0.0);
result += mat4(0.21311626, 0.052607164, 0.1248861, 0.20193806, 0.045226507, 0.14512901, -0.15103437, -0.17926466, 0.11657411, -0.32711068, -0.16332194, -0.07793982, -0.21802668, 0.5183869, -0.13567342, 0.07823041) * go_2(0.0, 1.0);
result += mat4(0.00796368, 0.048073012, -0.14537893, -0.021708772, 0.036246423, 0.1062395, 0.12605369, 0.007073524, -0.1572743, 0.07439501, 0.089162275, -0.0039608316, 0.332032, -0.05461242, -0.17615359, -0.10240517) * go_2(1.0, -1.0);
result += mat4(0.20636982, -0.0024615112, -0.10625786, 0.024270926, 0.061810836, -0.13585201, -0.16581286, 0.23549418, 0.01928842, 0.07404979, -0.054449487, 0.04096373, 0.046939734, 0.003980803, 0.02111498, 0.064925276) * go_2(1.0, 0.0);
result += mat4(0.10485388, 0.06850885, -0.11292169, 0.16991565, -0.15282536, 0.124175504, -0.050431166, -0.06689582, -0.00059811946, 0.033696912, 0.11055047, 0.033060126, -0.17472714, 0.0048819613, -0.04478706, -0.1344572) * go_2(1.0, 1.0);
result += mat4(-0.20473132, 0.056477875, 0.059559986, 0.115130566, -0.058425788, -0.035971727, 0.08334707, -0.096510135, -0.23206294, 0.10635798, -0.21575621, -0.07063254, 0.03877511, -0.107549034, 0.22248401, 0.21702304) * go_3(-1.0, -1.0);
result += mat4(-0.02557767, 0.09886609, -0.100499466, 0.16687396, -0.084830604, 0.03150401, -0.049512494, 0.05595696, -0.13193256, -0.08585273, 0.14247662, 0.12290477, -0.07168309, 0.14531752, -0.048359327, 0.27716598) * go_3(-1.0, 0.0);
result += mat4(0.13297586, 0.20674329, 0.14469388, 0.08981846, -0.004231366, -0.02819193, 0.15470329, 0.17299837, 0.113062344, -0.22716297, -0.21754944, -0.00083956274, -0.14160508, 0.1808253, 0.11268379, 0.27335623) * go_3(-1.0, 1.0);
result += mat4(0.07497518, -0.06799594, -0.018158078, -0.00038999433, -0.15169668, -0.06928238, -0.33672288, -0.105485775, 0.33106267, 0.06698315, 0.019718744, -0.06810211, -0.35186404, -0.29145968, -0.056863394, 0.21498048) * go_3(0.0, -1.0);
result += mat4(-0.013215512, -0.24763754, 0.20965266, 0.1068435, -0.13234195, 0.053566497, 0.05061848, -0.28645232, 0.15518288, 0.23247199, 0.017553907, -0.25181335, -0.048030723, -0.06663929, -0.111026704, -0.12663394) * go_3(0.0, 0.0);
result += mat4(-0.010501938, -0.17995767, 0.06010859, 0.050185587, 0.108627126, -0.101203434, 0.07558728, 0.060466755, -0.106942676, -0.35854608, 0.16015992, 0.16823332, -0.06543775, -0.37310675, 0.014043972, -0.18328045) * go_3(0.0, 1.0);
result += mat4(0.09712849, 0.013983463, 0.07291423, 0.031715546, 0.030862397, 0.045510456, -0.22066842, 0.063464865, 0.11721659, -0.10596602, -0.20611264, 0.052158818, -0.3961766, -0.03781582, 0.17633812, 0.1316111) * go_3(1.0, -1.0);
result += mat4(-0.25029674, 0.07153423, -0.35125682, -0.18255402, -0.19569087, 0.00432772, -0.0969035, -0.24648514, -0.0040922165, 0.037500706, -0.038137026, 0.056214277, -0.048258524, 0.03567822, -0.05033007, -0.24696785) * go_3(1.0, 0.0);
result += mat4(-0.03465209, -0.012495964, 0.22782089, 0.012034795, 0.2916752, 0.08264436, 0.15387125, -0.1473455, -0.15614432, 0.05536727, -0.027079755, 0.010725311, -0.03325222, -0.089212805, -0.10559839, -0.19647683) * go_3(1.0, 1.0);
result += vec4(0.0001705175, -0.031081453, 0.010100773, -0.027214011);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_2_tf
//!BIND conv2d_2_tf1
//!SAVE conv2d_3_tf1
//!WIDTH conv2d_2_tf.w
//!HEIGHT conv2d_2_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_2_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_2_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.026301445, -0.021575214, 0.22165509, 0.059994068, 0.03341161, 0.1831188, 0.20342293, 0.110160105, 0.03908121, 0.020673111, 0.07239561, 0.038754333, 0.15266368, 0.16526422, 0.062376205, -0.09759537) * go_0(-1.0, -1.0);
result += mat4(0.19817191, 0.10267733, 0.17744653, 0.23283184, 0.18810122, 0.2708428, -0.12651879, 0.020756349, 0.039632563, -0.22201295, 0.04873703, 0.09159713, 0.13838065, 0.21169297, 0.30816007, 0.044463675) * go_0(-1.0, 0.0);
result += mat4(-0.27859214, 0.07277634, 0.0021458792, 0.0089682285, -0.069680706, 0.090415835, -0.057762265, 0.18703683, -0.03514389, -0.102816254, -0.036509827, 0.038066104, -0.0168311, 0.094478935, 0.04079697, -0.049064912) * go_0(-1.0, 1.0);
result += mat4(-0.20913245, -0.110538535, -0.08584027, -0.1222067, 0.05414807, -0.045247085, 0.07351766, -0.002078549, -0.1270987, -0.10164512, -0.1857815, 0.08845066, -0.03743333, -0.098948084, 0.21244387, 0.10441866) * go_0(0.0, -1.0);
result += mat4(0.015990427, 0.36396438, -0.24094687, 0.30236533, -0.13271736, 0.06057376, -0.19678196, -0.28577125, -0.25427434, -0.08400598, 0.07284403, -0.18552442, -0.16425897, 0.097259276, -0.32386774, -0.2190484) * go_0(0.0, 0.0);
result += mat4(-0.004581924, -0.13954072, -0.122360416, 0.14132866, -0.08529257, -0.013296556, 0.0848472, 0.09336581, 0.10332182, -0.016313016, 0.07103558, 0.032564916, -0.13478759, -0.20207484, 0.12986964, 0.1219679) * go_0(0.0, 1.0);
result += mat4(0.09817874, -0.10573357, 0.100535244, 0.19608764, -0.13303067, 0.024192972, -0.030689823, 0.02574889, 0.051233094, 0.03489235, -0.18465245, -0.06943822, -0.031604882, 0.1519888, 0.09348508, 0.09187296) * go_0(1.0, -1.0);
result += mat4(-0.21365458, -0.23696984, 0.13097638, -0.09435498, 0.16467983, -0.066370346, 0.1269104, -0.095128186, 0.09954892, 0.12489504, -0.43418056, 0.106512725, -0.17860703, -0.07114084, -0.07630834, -0.26642478) * go_0(1.0, 0.0);
result += mat4(-0.009044342, 0.02711196, -0.14873673, 0.015405045, 0.0071443473, -0.025285944, 0.07409282, 0.06338527, 0.0149676185, 0.011741382, -0.2133069, -0.028912885, 0.19420496, 0.039629057, 0.057636812, 0.15214856) * go_0(1.0, 1.0);
result += mat4(0.07629928, 0.25540486, -0.050925937, -0.18136702, 0.02261603, 0.22343902, 0.003270321, 0.10735731, -0.12541203, -0.10208828, 0.012832783, 0.2591262, 0.08122926, -0.009837677, 0.10308358, 0.19236866) * go_1(-1.0, -1.0);
result += mat4(0.0896358, 0.27571487, 0.04406029, -0.047453407, -0.08587119, 0.16366854, 0.20622262, 0.08347545, -0.3501584, -0.28434548, -0.07592983, 0.09098784, 0.07605388, 0.09677056, 0.0015295541, 0.05102585) * go_1(-1.0, 0.0);
result += mat4(0.18255898, 0.18618028, 0.0017002645, -0.013004655, -0.06436534, 0.13967068, 0.063077755, -0.10632303, -0.20803222, -0.028537111, -0.03144366, -0.08555215, 0.05154303, 0.02431626, 0.15246728, -0.013708507) * go_1(-1.0, 1.0);
result += mat4(-0.020998938, -0.05026291, 0.03700117, 0.00830308, -0.1949294, 0.0026698054, -0.034649856, 0.19784226, -0.083901435, -0.069783084, -0.1504053, 0.16595264, -0.07480141, 0.16067508, 0.06010996, -0.021359695) * go_1(0.0, -1.0);
result += mat4(-0.040828142, -0.20158486, 0.034770954, -0.1894161, 0.11665004, 0.29729164, -0.10584386, 0.13165873, -0.18863006, -0.26719162, -0.047613148, -0.12728356, -0.2033613, 0.10550052, 0.20095508, -0.11275811) * go_1(0.0, 0.0);
result += mat4(-0.0785033, -0.1896073, -0.051492307, -0.1694358, 0.1368308, 0.049355216, -0.05707422, 0.079159185, 0.024578957, -0.0923136, 0.089215435, 0.28670043, 0.027932687, 0.06510816, 0.10810999, 0.05990052) * go_1(0.0, 1.0);
result += mat4(0.08135192, 0.0001326522, -0.16098668, -0.18663193, -0.10280192, 0.078255914, 0.047648013, 0.08326376, 0.055962667, 0.06302574, -0.080121025, -0.031820554, -0.019117938, 0.12515336, 0.09794088, -0.03276838) * go_1(1.0, -1.0);
result += mat4(0.280923, 0.24079335, 0.007883573, 0.06270414, 0.3055441, 0.19291803, -0.16041607, 0.14836526, 0.0013885222, 0.04538063, 0.10742898, -0.064491205, 0.048174977, 4.237692e-05, -0.15194727, 0.024381457) * go_1(1.0, 0.0);
result += mat4(-0.0009164131, -0.031949926, 0.0076425644, -0.036870714, -0.0031292974, 0.017726978, -0.20172147, -0.0770472, 0.26379177, 0.108997814, 0.08069395, 0.2126177, 0.012075376, -0.029457828, 0.062730506, -0.15754452) * go_1(1.0, 1.0);
result += mat4(0.09167904, -0.2657421, -0.03443356, 0.03315832, -0.015365421, -0.1029612, -0.108251, 0.04261033, -0.097120754, -0.05616668, -0.09275983, 0.024902184, 0.050058514, -0.013761632, 0.07555132, -0.0046676896) * go_2(-1.0, -1.0);
result += mat4(-0.10743835, -0.0007361781, -0.042085417, -0.08237517, -0.10094376, -0.24007876, 0.13924706, -0.07526801, 0.01158322, 0.15491122, 0.0069442675, -0.004242352, 0.11429785, 0.02994726, -0.11829945, -0.04108612) * go_2(-1.0, 0.0);
result += mat4(0.073622055, -0.064717196, -0.0025231615, 0.13256475, 0.20159899, 0.047977835, -0.10289233, -0.18419135, -0.00888952, 0.059428576, -0.053062655, -0.02730631, 0.14545685, -0.08686949, 0.17454128, 0.035443828) * go_2(-1.0, 1.0);
result += mat4(-0.010146019, 0.06712568, 0.12614638, 0.023590917, 0.025756737, 0.06603747, -0.17108095, -0.06179699, 0.027241204, -0.13196802, 0.043475866, -0.0397495, 0.05306092, 0.035672903, 0.047219284, -0.16680142) * go_2(0.0, -1.0);
result += mat4(0.079427816, -0.06716479, 0.19028603, -0.19694683, -0.061598092, -0.07471188, 0.21170339, 0.30140215, -0.0023369973, 0.04688297, -0.14154115, 0.19283508, 0.1339858, -0.09116279, 0.15305163, 0.029108394) * go_2(0.0, 0.0);
result += mat4(-0.14902157, -0.03339153, -0.08532003, -0.10736339, 0.08702709, 0.07607574, -0.09955836, -0.016585784, -0.030078214, -0.060374748, -0.2854279, 0.02441719, 0.034877967, 0.2099041, 0.11125731, -0.059071556) * go_2(0.0, 1.0);
result += mat4(-0.08436325, 0.06893047, -0.045362443, -0.02237741, -0.07583875, -0.034830183, -0.024008518, -0.2882329, -0.011109783, 0.101859994, 0.091137715, 0.0020565533, -0.044729806, -0.18168025, 0.069466636, 0.04994174) * go_2(1.0, -1.0);
result += mat4(0.11915174, 0.089596465, -0.18965814, 0.015218237, 0.13500094, 0.19921367, -0.008298205, 0.29650384, -0.049439427, -0.27590424, 0.36169067, -0.030582754, 0.02151196, 0.019915426, 0.04543398, 0.16126189) * go_2(1.0, 0.0);
result += mat4(0.1620274, -0.08264547, 0.082442135, -0.0034478644, 0.09888509, -0.0034957859, -0.107241705, -0.17729597, -0.05138647, 0.02052103, -0.019507123, 0.037574988, -0.1694345, 0.17871588, -0.22510391, 0.019049853) * go_2(1.0, 1.0);
result += mat4(-0.10962245, -0.1329873, -0.060855392, 0.025941676, -0.19536193, -0.120365486, -0.04313703, -0.052912965, 0.20854498, 0.08341353, 0.008687068, -0.20432276, 0.15677948, -0.19000018, 0.01821201, -0.041512605) * go_3(-1.0, -1.0);
result += mat4(0.012287526, -0.14180368, -0.098788455, 0.025949089, 0.09442778, 0.2247651, -0.12453263, 0.10435483, 0.274603, 0.06133054, 0.10506106, 0.14727746, -0.048299775, -0.082819685, 0.07319359, -0.047460355) * go_3(-1.0, 0.0);
result += mat4(-0.070726536, -0.034744017, 0.07521428, 0.070649154, -0.05958955, -0.100232825, -0.010651838, 0.045392875, 0.2930271, -0.04952355, 0.3112155, 0.117203265, 0.025166962, 0.11176862, 0.06716659, 0.07175864) * go_3(-1.0, 1.0);
result += mat4(-0.011560962, -0.14032063, -0.17424704, 0.07652749, -0.04220116, 0.052874275, -0.00225693, -0.031843517, -0.07520102, -0.13775803, 0.2449317, 0.069658786, 0.052280303, -0.105218224, 0.03574522, -0.020500354) * go_3(0.0, -1.0);
result += mat4(0.08793712, 0.26712346, 0.08315631, 0.23813692, -0.04439029, 0.031587064, 0.09561177, -0.13380238, -0.24982157, 0.31701845, -0.3875432, 0.10487225, 0.09201869, -0.037252493, -0.006935219, -0.14650282) * go_3(0.0, 0.0);
result += mat4(0.077635325, 0.13732299, -0.071563005, 0.096517466, -0.15051986, -0.111744404, 0.03996857, -0.052670125, -0.1819665, 0.054554947, -0.13774712, -0.20061246, -0.0023742192, 0.15647805, -0.024121126, 0.075497724) * go_3(0.0, 1.0);
result += mat4(0.0073632775, -0.06535298, 0.039895996, 0.20666869, 0.13625242, 0.04823007, -0.07135618, 0.04787906, 0.01383074, 0.15382123, -0.15519714, 0.056721795, 0.061946746, -0.0586851, 0.028934354, -0.02264129) * go_3(1.0, -1.0);
result += mat4(-0.19791882, -0.111910924, -0.010451344, -0.30566537, -0.1416239, -0.14523096, 0.116883226, -0.18241516, 0.2680614, -0.18487626, 0.17472346, 0.08346682, -0.14510359, -0.029229192, -0.005879142, 0.050247498) * go_3(1.0, 0.0);
result += mat4(0.030153519, -0.092469186, -0.022912916, 0.10200855, -0.04237032, -0.05917764, 0.10479645, -0.05619482, -0.18949397, -0.019547248, 0.013868889, -0.1524476, 0.14048979, -0.032521486, 0.1322921, 0.070972025) * go_3(1.0, 1.0);
result += vec4(0.012053958, -4.6962363e-05, 0.0020099226, -0.033494607);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_3_tf
//!BIND conv2d_3_tf1
//!SAVE conv2d_4_tf
//!WIDTH conv2d_3_tf.w
//!HEIGHT conv2d_3_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_3_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_3_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.06738501, 0.034009207, -0.21538448, 0.14296548, 0.12896985, -0.23526315, -0.08848608, 0.019602662, 0.14937137, 0.11353096, 0.11884168, -0.016765572, 0.030985225, 0.046430565, 0.06614828, -0.19202724) * go_0(-1.0, -1.0);
result += mat4(-0.10326068, 0.11014975, 0.17069744, -0.21474148, 0.16761585, 0.13434832, -0.101021074, 0.006307025, 0.07478008, -0.1060066, 0.035315692, 0.033488914, -0.24906659, 0.06269967, 0.11120735, -0.040928528) * go_0(-1.0, 0.0);
result += mat4(0.09334615, 0.057705753, 0.12213245, -0.06402275, 0.30694544, 0.034585163, 0.20345578, 0.07489286, 0.07483618, -0.14240396, 0.034846418, -0.03811241, 0.010882573, 0.13204294, 0.017563924, -0.047203008) * go_0(-1.0, 1.0);
result += mat4(-0.21673942, -0.024010994, -0.10238504, -0.041160326, 0.06838163, -0.20950818, 0.06526309, -0.079094924, 0.02208821, -0.28130978, 0.086275116, -0.089067616, 0.12133826, -0.062600106, -0.020521903, -0.07654401) * go_0(0.0, -1.0);
result += mat4(-0.03055029, -0.15683146, -0.20331301, -0.06252028, 0.13350682, 0.20338707, 0.038425338, 0.1581342, -0.27322498, -0.14999662, -0.16681097, 0.0971585, -0.20014858, -0.081635274, -0.0781877, -0.20625232) * go_0(0.0, 0.0);
result += mat4(0.38375977, -0.019825654, 0.1886721, 0.22616312, 0.3402173, 0.1825304, -0.05531195, 0.30973226, -0.2676023, 0.14413352, 0.021706983, 0.01732799, 0.23466855, -0.13805965, 0.22570935, 0.018103868) * go_0(0.0, 1.0);
result += mat4(-0.15169825, 0.0270689, -0.2503316, 0.17289825, -0.16437647, 0.039233048, -0.35572487, -0.048393793, 0.19270042, 0.24260359, 0.12041881, -0.0009793913, 0.11656858, 0.11007414, -0.0757491, 0.047933612) * go_0(1.0, -1.0);
result += mat4(-0.18657999, -0.11252566, -0.05237504, -0.07368097, 0.13882741, -0.13710637, -0.006996468, -0.062354874, 0.23452504, 0.15333645, -0.0022776406, -0.17910439, 0.03629509, -0.16264829, -0.010011833, -0.15313338) * go_0(1.0, 0.0);
result += mat4(-0.060544558, -0.04913478, -0.061717357, 0.02323648, 0.28739056, -0.07434013, 0.19110644, 0.100050166, 0.0073363045, 0.08185653, -0.024797903, -0.14424153, -0.20838726, 0.16154376, -0.048517212, -0.025453888) * go_0(1.0, 1.0);
result += mat4(0.14975396, -0.13142908, 0.36210674, -0.054021083, -0.10632155, 0.045697935, -0.18946633, 0.02228141, -0.08919603, 0.09800842, -0.17634438, 0.09512711, -0.03425503, -0.12298555, -0.05354435, -0.17112055) * go_1(-1.0, -1.0);
result += mat4(0.09958265, -0.057276618, -0.16262266, -0.06415915, 0.14579074, -0.36784375, 0.08034197, -0.04537706, 0.005460582, 0.22313322, 0.07382161, 0.014990379, 0.044636846, -0.2811128, -0.22621547, -0.06044004) * go_1(-1.0, 0.0);
result += mat4(0.10569276, -0.03738662, 0.16100396, 0.058593616, -0.048862137, -0.08796426, 0.20101094, -0.11039573, 0.17196764, -0.04601554, 0.008571281, -0.073729075, 0.051433694, -0.051276565, 0.087334655, -0.0360379) * go_1(-1.0, 1.0);
result += mat4(0.011119538, -0.28781965, 0.28637868, -0.1742508, -0.07121849, 0.10379717, 0.012615981, -0.029563965, -0.18678424, 0.05291095, 0.039143506, -0.028248642, -0.014103922, 0.029155696, 0.10433492, 0.16305852) * go_1(0.0, -1.0);
result += mat4(-0.2231037, -0.13697462, -0.29124337, 0.08519773, 0.15893684, -0.17763218, 0.06950923, 0.34361118, -0.024844287, 0.044008408, -0.033844844, -0.086971916, -0.07884748, 0.2543499, 0.056884114, 0.10068364) * go_1(0.0, 0.0);
result += mat4(-0.07710048, -0.23218372, 0.04346047, 0.21769643, 0.06473219, -0.18066105, -0.2511205, 0.15309611, 0.04535977, 0.16450433, 0.10846344, 0.0016952346, -0.010874939, 0.28966382, -0.121990964, 0.12956186) * go_1(0.0, 1.0);
result += mat4(-0.007910202, 0.17766511, 0.14364475, 0.1016258, 0.0051045395, 0.18691733, 0.005813767, -0.0070582186, 0.019418601, -0.1604435, 0.016088275, -0.18265302, -0.15719391, -0.17369832, -0.036745597, -0.19647408) * go_1(1.0, -1.0);
result += mat4(0.08938396, -0.0073808245, 0.11225727, -0.012303106, 0.096785046, 0.030483445, 0.027719889, -0.052584838, -0.14887555, -0.03422243, 0.12646855, -0.1722482, 0.010239037, 0.06406088, -0.20053658, 0.01964698) * go_1(1.0, 0.0);
result += mat4(-0.120734036, -0.12450362, -0.06582111, 0.1639675, -0.19787048, -0.08049789, -0.014257596, 0.058436662, -0.0009387449, -0.08698089, -0.017400503, 0.06295286, 0.09890349, -0.057190523, -0.103520766, -0.04207548) * go_1(1.0, 1.0);
result += mat4(-0.0118413875, -0.031288836, 0.09749554, -0.012266401, -0.07998591, 0.22615653, -0.06207416, 0.03257896, -0.076378696, -0.079426095, -0.13968349, -0.15423697, -0.1091681, -0.02893125, -0.032659534, -0.063735925) * go_2(-1.0, -1.0);
result += mat4(0.119372696, 0.013176554, -0.029381052, 0.21919228, 0.045041792, 0.24844484, 0.26363325, 0.08480674, 0.087083444, 0.11984778, -0.088715754, 0.06421046, 0.05225977, -0.05140334, -0.055052705, -0.049854077) * go_2(-1.0, 0.0);
result += mat4(0.0035781674, 0.0861361, -0.07675145, -0.056479637, 0.16973391, -0.12113791, 0.10729832, -0.03773517, 0.058618728, 0.12148276, 0.17260705, -0.06968724, 0.076358154, -0.15307103, 0.17700425, -0.13467014) * go_2(-1.0, 1.0);
result += mat4(-0.02752418, -0.06366472, -0.025610954, 0.0013539721, -0.06465272, 0.0806373, -0.07336035, 0.10114861, 0.0041146413, 0.15878421, -0.044668555, -0.12150811, -0.1071482, -0.05086587, 0.18589285, 0.05065092) * go_2(0.0, -1.0);
result += mat4(0.07200056, 0.021739854, 0.29476613, -0.08475931, 0.15018553, -0.07886365, 0.36336347, -0.020576432, 0.25866082, -0.059272554, 0.054249667, -0.17822553, 0.1755872, 0.3244387, -0.39173844, 0.33894604) * go_2(0.0, 0.0);
result += mat4(-0.11570926, 0.1342677, -0.19511898, 0.0075454637, -0.01890476, -0.14239742, 0.18921931, 0.033990458, 0.31306365, -0.006998358, 0.029190077, -0.005679954, -0.15341778, 0.07766778, -0.25691047, -0.0964161) * go_2(0.0, 1.0);
result += mat4(0.019746238, 0.0021332854, -0.00879096, -0.1338671, -0.0001600663, -0.29465106, 0.0867611, -0.114963025, 0.07874301, -0.012734178, -0.11124061, -0.010926616, -0.04941506, -0.07516841, 0.116663, -0.29018974) * go_2(1.0, -1.0);
result += mat4(-0.01651721, 0.05955898, 0.023618208, 0.098695934, 0.018553663, -0.054378513, 0.1436929, 0.1693743, -0.27483663, 0.029127488, 0.09619316, -0.06109113, -0.08619361, 0.09315214, -0.02478657, 0.18544984) * go_2(1.0, 0.0);
result += mat4(0.09570196, -0.016528936, -0.1559397, 0.14312246, 0.04029428, 0.08773151, -0.043646842, 0.17894371, -0.082413055, 0.0027082344, -0.100171275, 0.01547501, 0.18122818, -0.11933676, 0.26404107, -0.3169703) * go_2(1.0, 1.0);
result += mat4(-0.12073344, 0.08683522, -0.09249099, 0.058786053, -0.14480567, -0.121013954, 0.033335857, 0.009353379, -0.055087596, -0.13002734, 0.08890566, 0.05508963, -0.0075715426, -0.15936922, -0.03968994, -0.1690259) * go_3(-1.0, -1.0);
result += mat4(0.2011206, 0.23898427, 0.23656492, 0.1287573, 0.14850396, 0.40532517, -0.107408255, 0.40119782, 0.099813245, -0.03830304, 0.101520434, -0.026478073, -0.048469637, 0.106440455, 0.056632314, -0.17825997) * go_3(-1.0, 0.0);
result += mat4(-0.076735444, 0.05965795, -0.0052469415, -0.21785147, 0.11887833, 0.067560315, 0.051149055, 0.23626682, -0.1297049, -0.035512198, 0.20352256, -0.025064934, 0.04958706, 0.0454198, 0.0113334535, 0.0417486) * go_3(-1.0, 1.0);
result += mat4(-0.09055751, 0.033915352, -0.21836667, 0.22006813, -0.099022895, 0.11720966, -0.15686816, -0.13586599, -0.094427735, -0.08831514, -0.06182928, 0.09213704, -0.03642064, 0.18129414, -0.012926811, 0.12179882) * go_3(0.0, -1.0);
result += mat4(0.19389409, 0.09512252, 0.14768016, -0.16623649, -0.031052284, -0.026814984, 0.106168024, -0.2026781, -0.04581419, -0.0016849053, -0.04101923, 0.038959503, -0.011938445, 0.20096186, -0.26666564, 0.4824324) * go_3(0.0, 0.0);
result += mat4(0.17727576, 0.07309147, 0.12131863, -0.163096, 0.17225246, 0.26256254, 0.27685758, 0.09094053, 0.029605515, -0.20217367, 0.047564875, 0.043115832, 0.15089568, -0.09670934, 0.24131384, 0.03337442) * go_3(0.0, 1.0);
result += mat4(-0.34192136, 0.12063195, -0.31159517, 0.04170889, -0.30147067, -0.21330686, -0.1514457, -0.121126845, 0.04409098, 9.2206596e-05, 0.027680017, 0.03230512, -0.27993527, -0.093485355, 0.07568645, -0.23585452) * go_3(1.0, -1.0);
result += mat4(0.0537712, -0.20847629, 0.1740093, -0.013894753, -0.32719997, -0.059484575, -0.006098233, -0.10336451, -0.14706188, -0.07424865, -0.07045905, 0.17093194, -0.22147557, 0.09086218, -0.11033544, -0.05306482) * go_3(1.0, 0.0);
result += mat4(0.00489003, -0.11509064, -0.021005848, 0.16637677, -0.089347586, 0.17545725, -0.17313693, 0.13742085, -0.14577347, 0.07951095, -0.092139855, 0.017118992, -0.053472433, 0.079414465, 0.0330263, -0.11189824) * go_3(1.0, 1.0);
result += vec4(-0.034743138, 0.012946433, -0.082333155, 0.07721756);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_3_tf
//!BIND conv2d_3_tf1
//!SAVE conv2d_4_tf1
//!WIDTH conv2d_3_tf.w
//!HEIGHT conv2d_3_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_3_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_3_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.25835788, 0.050451655, -0.1845038, -0.07232528, 0.1323318, 0.26276684, 0.10842882, -0.083056524, 0.17426784, -0.3594826, 0.2728965, 0.08388844, -0.004007842, 0.020535901, -0.051425606, 0.07750436) * go_0(-1.0, -1.0);
result += mat4(-0.11410436, 0.014572361, -0.27057216, -0.023974562, 0.05234827, 0.15328228, -0.17502303, -0.3199359, 0.12188045, -0.095813684, 0.024145132, 0.0856916, -0.027453909, -0.043129764, 0.16971985, 0.021623038) * go_0(-1.0, 0.0);
result += mat4(0.06611095, 0.038625732, -0.13717118, -0.04497733, 0.15213469, 0.04770935, 0.0729271, -0.062052976, 0.004571303, 0.035141192, -0.059409596, 0.044652313, 0.17520894, 0.09665589, -0.1479193, 0.06528058) * go_0(-1.0, 1.0);
result += mat4(-0.1845968, 0.091479465, -0.09394898, -0.13545018, -0.029501775, -0.21426639, 0.09255898, 0.1257644, 0.20256902, 0.06267267, 0.10378081, 0.13494423, 0.058310498, 0.03642236, -0.16268995, -0.048100803) * go_0(0.0, -1.0);
result += mat4(0.2155119, -0.3683131, 0.049449228, -0.20559964, -0.11761922, -0.2518804, -0.020712897, 0.12895772, -0.07543782, 0.5805017, -0.11301444, -0.038493153, -0.06710986, -0.09321189, 0.108671665, -0.03259695) * go_0(0.0, 0.0);
result += mat4(0.035307787, 0.108389005, -0.27493554, 0.27029404, 0.25523573, -0.28636125, -0.20766719, -0.008661457, -0.004480811, -0.046390545, -0.16221444, 0.008979624, -0.061375532, 0.035076566, -0.018924266, 0.01380219) * go_0(0.0, 1.0);
result += mat4(-0.051922515, -0.12463486, -0.10383422, 0.02220095, -0.1573033, 0.13980615, 0.13248625, -0.16803266, -0.0692132, -0.21552645, 0.13744529, 0.23034313, 0.0052666534, 0.028977966, 0.07720251, -0.06477756) * go_0(1.0, -1.0);
result += mat4(-0.14097473, 0.2770271, -0.172289, -0.03000696, -0.028684044, 0.040578447, -0.2290285, 0.082329154, -0.042402364, -0.20926563, 0.08233207, 0.11862443, -0.07038536, -0.02273004, 0.091550544, -0.065856494) * go_0(1.0, 0.0);
result += mat4(0.14879914, -0.023923844, -0.23569296, 0.20306346, 0.17502785, 0.28776234, -0.2788995, 0.10012439, -0.05635638, -0.025840463, 0.09222198, 0.118032, 0.08057015, 0.1286071, 0.060189806, -0.052669708) * go_0(1.0, 1.0);
result += mat4(0.07076086, -0.15111323, -0.07427972, 0.008372168, -0.17791592, -0.16254742, 0.013961132, -0.0944912, -0.23380096, 0.17377278, -0.09683394, 0.019931393, -0.12042098, 0.0016406325, 0.09393333, -0.06882231) * go_1(-1.0, -1.0);
result += mat4(0.21465093, 0.04142968, 0.06840044, -0.37831602, -0.05549571, 0.044905066, -0.07873589, -0.026804, -0.34764197, 0.022487951, -0.077293746, 0.089457795, -0.110094436, 0.24233972, 0.06285107, -0.10851744) * go_1(-1.0, 0.0);
result += mat4(0.093270175, 0.084138945, 0.03938272, 0.063565865, -0.010733802, 0.13554469, -0.06650261, 0.033002816, 0.011187271, -0.12821455, 0.20785914, -0.030438649, -0.124710515, -0.022294303, 0.09732408, 0.057609864) * go_1(-1.0, 1.0);
result += mat4(-0.12833868, 0.021577539, -0.02700365, 0.11799592, -0.03655647, -0.04225167, 0.11049353, -0.16036157, 0.049277548, -0.033842396, 0.10020137, 0.095509745, 0.08060231, -0.09237418, -0.035598125, -0.035926737) * go_1(0.0, -1.0);
result += mat4(-0.32829186, 0.3492363, 0.030671779, -0.12606762, 0.010437313, 0.2757115, -0.21517593, -0.15800527, -0.12592544, -0.20578934, 0.10444053, 0.12993255, -0.046079267, 0.03834173, -0.19277227, -0.22124454) * go_1(0.0, 0.0);
result += mat4(-0.052546192, 0.026082167, 0.13831234, 0.10982424, 0.012946818, -0.12439852, 0.10134106, -0.10050398, -0.04472338, -0.14325236, -0.20579574, 0.0044005127, 0.22013672, -0.32955512, 0.12404084, -0.008160738) * go_1(0.0, 1.0);
result += mat4(-0.10774314, -0.31650826, -0.06601711, 0.19635755, -0.12622592, -0.06396423, 0.13856032, 0.16540553, 0.021387719, 0.23377723, -0.053738154, -0.1000186, -0.08338395, -0.052813534, 0.008122962, 0.13732094) * go_1(1.0, -1.0);
result += mat4(-0.18270823, 0.06966014, -0.17788303, -0.27303055, -0.077971615, 0.013978423, -0.02039098, 0.12715338, -0.11924171, 0.18900296, -0.085199654, 0.215198, 0.18587974, -0.009749325, 0.0173584, -0.12018259) * go_1(1.0, 0.0);
result += mat4(0.052129295, -0.107416354, 0.12711766, 0.03708665, -0.14369462, -0.055359814, -0.16639823, -0.045143317, -0.06925672, -0.040696755, 0.01999809, -0.016040625, -0.02484878, 0.07417094, 0.050875198, 0.2145528) * go_1(1.0, 1.0);
result += mat4(0.055696912, -0.16680926, -0.021987487, 0.024941636, -0.0927883, 0.022136632, 0.033782948, -0.10646058, -0.14944647, 0.25457275, 0.046682496, -0.022462368, -0.07886781, 0.08165927, 0.06848105, 0.0063734027) * go_2(-1.0, -1.0);
result += mat4(0.037053242, 0.033215813, 0.18291366, 0.12340375, 0.08491059, -0.28442004, -0.0127422465, -0.039834313, -0.23321372, 0.26676926, -0.05636355, -0.15672484, -0.12891728, -0.15486577, -0.032004442, -0.092745155) * go_2(-1.0, 0.0);
result += mat4(0.015779478, -0.18457565, 0.24996394, 0.036197674, 0.15694007, 0.15863103, -0.07332398, 0.0016235278, -0.15536517, -0.056062788, 0.14102836, 0.16915025, -0.08001087, 0.07073164, 0.13796777, 0.123867124) * go_2(-1.0, 1.0);
result += mat4(0.045792986, -0.15135059, -0.1354885, -0.043678258, -0.35655212, 0.51232076, -0.12816145, -0.046569496, -0.014127674, -0.06282611, -0.098873, -0.06359104, -0.0919222, 0.11822437, 0.079254694, 0.00579688) * go_2(0.0, -1.0);
result += mat4(-0.15683417, 0.61610246, -0.3024612, 0.12917964, -0.09303367, 0.23612969, -0.40842506, -0.12374661, -0.07572449, -0.2613284, -0.09970177, -0.015227848, 0.106239066, -0.21411185, 0.051998455, -0.1364518) * go_2(0.0, 0.0);
result += mat4(0.23850034, -0.14394449, -0.0031468747, -0.2380617, -0.027200876, -0.041352056, -0.01864445, 0.033848196, -0.12064239, -0.110480845, 0.08450956, -0.22328654, 0.17664163, 0.22268307, 0.050886698, -0.17475672) * go_2(0.0, 1.0);
result += mat4(-0.17808256, 0.010803805, 0.03315186, 0.033143792, -0.14205995, 0.25039625, -0.08784382, -0.13454252, 0.19576813, 0.10755282, 0.22821628, 0.019456752, -0.0422955, -0.016182603, -0.12066697, 0.0548465) * go_2(1.0, -1.0);
result += mat4(0.11563777, -0.257929, 0.0010403778, 0.080267854, -0.0025255163, 0.2855168, -0.060352214, -0.07816255, -0.00090574916, 0.049510725, 0.03720483, 0.059250016, -0.08674136, 0.20522198, -0.28694284, 0.1299507) * go_2(1.0, 0.0);
result += mat4(-0.14638457, 0.04063328, 0.03139636, -0.007934521, 0.07689684, -0.09467145, 0.10607347, 0.054510128, 0.003306194, 0.05347124, 0.062762424, -0.041480847, -0.07677865, -0.139573, 0.010972524, 0.21957156) * go_2(1.0, 1.0);
result += mat4(-0.026845628, -0.043439507, 0.034738723, 0.07281683, 0.14474197, 0.031586993, -0.22767854, -0.0707655, 0.105201736, -0.28805482, 0.008668302, -0.16329518, 0.06157049, 0.3803886, 0.26345953, -0.011096537) * go_3(-1.0, -1.0);
result += mat4(-0.23328833, 0.085731484, -0.07755016, 0.33559516, 0.07704345, 0.115106605, -0.24114038, -0.44630137, 0.2726737, -0.32170138, -0.009236524, -0.11666051, 0.0457048, 0.07876708, 0.13134004, -0.035318643) * go_3(-1.0, 0.0);
result += mat4(-0.05140272, 0.011605703, 0.13899171, -0.05071015, 0.18413687, -0.31413674, -0.13043414, -0.15118152, -0.15326938, -0.10720126, -0.23738635, 0.13481396, 0.25115076, -0.009316611, -0.2584441, -0.14389823) * go_3(-1.0, 1.0);
result += mat4(-0.039723795, -0.14869407, -0.1692942, 0.026501274, -0.10685166, -0.121267825, -0.08584318, -0.09580693, -0.10626739, -0.068417974, 0.11321909, -0.13664317, 0.061380867, -0.2587898, 0.14850819, 0.008178645) * go_3(0.0, -1.0);
result += mat4(0.06912782, 0.24230564, -0.048150286, 0.2203717, -0.17417085, 0.105546735, -0.16648416, -0.0045053074, 0.09764028, 0.37122592, -0.1939995, -0.27899942, -0.088152565, -0.53869057, 0.21676709, -0.08056594) * go_3(0.0, 0.0);
result += mat4(0.07651754, 0.03704878, -0.0197015, 0.1660726, 0.07002748, -0.11820414, -0.23360898, 0.1481592, 0.029847002, 0.054057185, 0.013176299, 0.06552942, -0.13865773, -0.20105527, -0.37550658, 0.005769631) * go_3(0.0, 1.0);
result += mat4(-0.22697811, -0.17426412, 0.10148018, 0.008134666, 0.10771455, 0.16943407, -0.016319012, -0.40176705, -0.06854668, -0.049045276, 0.20919096, 0.13240765, -0.050125647, 0.14902508, 0.052697595, -0.13817468) * go_3(1.0, -1.0);
result += mat4(0.04301619, 0.23184754, -0.023551717, 0.3768405, 0.028999053, 0.06709736, -0.05993663, -0.059861984, 0.15499207, -0.22217415, 0.111131504, -0.09082529, -0.19389243, 0.024621522, -0.15305442, 0.010799284) * go_3(1.0, 0.0);
result += mat4(-0.035496738, 0.010802548, -0.028718363, 0.19263634, 0.16900502, -0.16661702, -0.027631328, 0.18309957, -0.015860107, -0.03309961, -0.091390446, 0.14000848, -0.0036591904, 0.47659522, -0.09373507, -0.29020965) * go_3(1.0, 1.0);
result += vec4(0.08895955, -0.027667087, 0.20500831, 0.00037762933);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_4_tf
//!BIND conv2d_4_tf1
//!SAVE conv2d_5_tf
//!WIDTH conv2d_4_tf.w
//!HEIGHT conv2d_4_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_4_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_4_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.018134737, -0.2296755, -0.07276725, -0.029795367, 0.05382051, 0.092847414, -0.024469728, -0.1674685, 0.0017946451, 0.30074653, 0.0034195695, -0.04892261, 0.18229689, -0.20116119, -0.12702174, -0.08259108) * go_0(-1.0, -1.0);
result += mat4(-0.1357695, -0.08149211, 0.09314453, -0.21966846, 0.34740716, 0.043606415, 0.04225903, 0.034449834, 0.17248215, 0.39148283, -0.13868807, -0.010550686, 0.044238456, -0.09693464, -0.005044985, 0.24383289) * go_0(-1.0, 0.0);
result += mat4(0.19959371, 0.098685324, 0.058746945, 0.010580748, 0.08051514, 0.031898864, 0.017556064, 0.13004355, -0.01727653, 0.11044019, 0.040673427, -0.20064595, -0.23321067, 0.06398686, -0.19126236, -0.2430858) * go_0(-1.0, 1.0);
result += mat4(-0.12870286, -0.113455534, 0.23722827, 0.070718594, 0.19049989, -0.1927299, -0.06343845, 0.113127775, 0.082530305, -0.10972526, -0.090779535, 0.05731582, 0.11018802, -0.18049154, 0.09269507, -0.10304576) * go_0(0.0, -1.0);
result += mat4(0.15513484, 0.06659583, 0.08125296, -0.012350324, -0.09492788, 0.5048303, 0.13206847, 0.39554298, 0.28953737, -0.20913891, -0.26781562, -0.17539899, 0.023778774, 0.29716817, 0.15768486, 0.37702608) * go_0(0.0, 0.0);
result += mat4(0.0724462, 0.015571356, -0.032217246, 0.0050658924, -0.22708446, 0.03968809, 0.016753826, 0.0025668752, -0.055932112, 0.113931604, 0.19766758, -0.030027265, -0.17384295, 0.15013468, -0.0070017707, -0.09469028) * go_0(0.0, 1.0);
result += mat4(-0.078361556, -0.0954201, -0.006358101, 0.040500037, 0.4190454, -0.17622913, -0.07234791, 0.05462559, 0.18641087, 0.058313597, -0.0180785, 0.13818781, -0.14640772, 0.0699486, 0.0073663946, -0.076789856) * go_0(1.0, -1.0);
result += mat4(-0.21421191, 0.08736062, 0.09041226, 0.03608585, 0.02769972, 0.09641289, 0.11824623, 0.05653645, 0.16464607, 0.19839554, -0.13379547, 0.054417104, 0.067530684, 0.18971571, 0.13785432, -0.097639814) * go_0(1.0, 0.0);
result += mat4(-0.32658005, -0.14606023, -0.069448665, 0.032998275, -0.28331423, 0.0011900732, -0.020304207, -0.13535896, 0.08298347, 0.045509677, -0.030503955, -0.037504148, 0.049955815, 0.0925771, 0.00058534974, -0.12398032) * go_0(1.0, 1.0);
result += mat4(-0.2955836, 0.29059318, -0.018196672, -0.35866606, -0.01309431, 0.03540315, 0.010609202, 0.11956812, 0.10296229, 0.22536302, 0.015201129, -0.23797737, -0.16960852, -0.11414787, -0.034440614, 0.112644605) * go_1(-1.0, -1.0);
result += mat4(-0.14952518, 0.07024436, -0.083184876, -0.0814617, -0.13303639, 0.016159372, -0.13521518, 0.2221334, -0.056617837, 0.12958299, 0.064461656, -0.20146395, -0.16023181, 0.2640758, 0.27528805, -0.1426518) * go_1(-1.0, 0.0);
result += mat4(-0.04382363, 0.09856003, -0.08561442, -0.15699928, -0.121069774, 0.04685383, -0.009170197, -0.031489655, 0.18730178, 0.238442, 0.22497098, 0.032015145, -0.03709115, 0.1535079, 0.21674158, 0.10678019) * go_1(-1.0, 1.0);
result += mat4(-0.12200952, 0.24224263, 0.034097504, -0.028179523, -0.011962496, -0.04489487, -0.05198827, 0.22194928, -0.045400873, -0.049828544, 0.111477956, -0.098361604, 0.12788995, -0.016093334, -0.19886433, -0.011161484) * go_1(0.0, -1.0);
result += mat4(0.30563712, 0.013071727, -0.004799883, 0.12888052, -0.259498, -0.041566677, 0.07311124, 0.162324, 0.28371668, -0.004693743, -0.0019395344, 0.029358242, 0.08730285, 0.12184509, 0.05508437, 0.048439097) * go_1(0.0, 0.0);
result += mat4(0.12760857, 0.115813166, -0.217695, -0.10629871, -0.227366, 0.09030426, -0.15313712, 0.020528946, -0.20743734, 0.088583544, 0.04594053, -0.22891994, 0.18949282, -0.042186577, -0.17330512, -0.010711361) * go_1(0.0, 1.0);
result += mat4(0.029503195, 0.0063797613, -0.17004286, -0.096844055, 0.010218098, 0.04247233, 0.02362808, 0.14700809, -0.08082364, 0.11159672, -0.018505255, -0.15228583, 0.15693732, -0.025359154, 0.024829186, 0.1943192) * go_1(1.0, -1.0);
result += mat4(-0.03912932, -0.21989027, 0.12203028, 0.18702275, -0.118537985, 0.21039696, 0.09102061, 0.012288879, 0.031666897, 0.1318455, -0.04901404, -0.07516063, -0.44782668, 0.04884501, 0.047070876, 0.008728358) * go_1(1.0, 0.0);
result += mat4(-0.08669101, 0.3053463, -0.08963947, 0.0034188698, -0.070004664, 0.064788476, 0.093737036, 0.070050925, 0.12728429, -0.13179256, -0.014913502, 0.09308136, -0.027638942, 0.008638711, 0.08794172, -0.05531093) * go_1(1.0, 1.0);
result += mat4(0.0728421, 0.07872358, 0.11454748, 0.08497922, 0.071820416, -0.11789207, -0.08184197, 0.1359588, -0.2143346, -0.05876081, 0.023172129, -0.08430511, -0.19276723, 0.14283359, 0.15604696, -0.055187486) * go_2(-1.0, -1.0);
result += mat4(0.068641685, 0.2732106, -0.2809107, 0.12736696, -0.08642367, 0.023898933, -0.17859498, -0.18299665, -0.06684587, -0.12204666, 0.45898953, -0.24240111, 0.25182098, -0.04395751, 0.10637211, -0.22135144) * go_2(-1.0, 0.0);
result += mat4(0.0852072, 0.051133018, 0.03333165, -0.0008938216, 0.10251267, 0.0550774, 0.041769378, -0.21259712, 0.286912, 0.123342015, 0.282759, -0.0730124, 0.14275575, -0.15580742, -0.15224406, 0.045376908) * go_2(-1.0, 1.0);
result += mat4(0.03328225, 0.11563978, -0.07451964, 0.030546209, -0.04698351, -0.18544962, 0.037350416, 0.13969816, 0.0556746, -0.06359919, 0.06478219, -0.031694926, 0.13396506, 0.09443612, -0.01922686, -0.06290365) * go_2(0.0, -1.0);
result += mat4(0.07495407, 0.063429266, -0.106221214, -0.085107304, 0.2497817, -0.46598253, -0.18833177, -0.2731128, -0.13024822, 0.56053543, 0.055704467, -0.12331414, -0.031199086, 0.05061188, 0.22097112, -0.6611177) * go_2(0.0, 0.0);
result += mat4(0.08276988, -0.044184342, -0.03562185, -0.06159881, 0.27694225, -0.07192965, -0.08663714, 0.020221777, 0.14095962, -0.06229397, 0.051374253, -0.038158998, 0.10664802, -0.041305423, 0.051260717, -0.054698635) * go_2(0.0, 1.0);
result += mat4(0.12800686, 0.03485072, 0.039914366, 0.034041498, -0.08305794, -0.046292894, 0.22765331, 0.10904922, 0.0013937047, -0.08750301, 0.009126207, -0.065589435, 0.2837707, 0.08884436, -0.07234862, -0.093502745) * go_2(1.0, -1.0);
result += mat4(0.113439895, 0.06081726, 0.1122302, -0.022936966, 0.10329637, -0.31816107, -0.051597945, 0.23846027, -0.083913095, -0.29872265, -0.040147282, -0.08981918, -0.04329814, -0.12339693, -0.034489952, 0.013393211) * go_2(1.0, 0.0);
result += mat4(0.33091688, 0.1726297, 0.034332044, -0.091396205, 0.15434311, -0.0022870845, -0.15506189, 0.08710491, -0.16063525, 0.042252056, 0.017086457, 0.08134797, 0.08631321, 0.037843138, 0.088296555, 0.0064518084) * go_2(1.0, 1.0);
result += mat4(0.09161051, 0.114355795, -0.15304486, -0.030537153, 0.1835368, -0.3287635, 0.031197926, 0.09717476, 0.04276852, 0.113250345, 0.05949038, -0.10599563, 0.43574792, -0.060788117, 0.18409383, 0.12678055) * go_3(-1.0, -1.0);
result += mat4(-0.018356865, -0.0072578182, 0.12020777, -0.013127592, 0.20136636, -0.22984362, 0.06896224, 0.00044982752, 0.008428429, -0.123316936, -0.09989286, 0.078248784, -0.16313677, -0.003020313, -0.46285018, -0.08967125) * go_3(-1.0, 0.0);
result += mat4(-0.03451497, -0.10864502, 0.13207638, 0.17194521, 0.0037514758, -0.20222199, -0.12535086, 0.001511977, 0.056294486, -0.2112898, 0.078261316, 0.10118746, -0.044742294, 0.21793383, -0.19927903, -0.21338293) * go_3(-1.0, 1.0);
result += mat4(-0.034903776, -0.10167085, 0.031066334, 0.0379958, 0.20532596, -0.17457838, 0.16556816, -0.0021619152, 0.02682665, 0.03396325, -0.059273884, 0.1922813, -0.072151475, -0.010240544, 0.2302027, 0.12385962) * go_3(0.0, -1.0);
result += mat4(-0.20170145, -0.08203941, -0.028107846, -0.18003726, 0.44744352, -0.13190243, 0.13233365, 0.03626546, 0.085763134, -0.25613126, -0.11213388, 0.15529087, -0.271649, 0.050587676, -0.062583975, 0.057289865) * go_3(0.0, 0.0);
result += mat4(-0.040649455, -0.17949733, 0.35847965, -0.040587306, 0.24314344, -0.23811667, 0.13958354, 0.04961874, 0.09858903, -0.04202913, -0.21850993, 0.0700419, -0.09130745, -0.096835814, 0.0022782686, -0.25416258) * go_3(0.0, 1.0);
result += mat4(-0.08215545, -0.019647893, 0.055263475, 0.053733055, 0.098485716, -0.1041945, -0.06541415, -0.08868577, -0.07262986, 0.03513784, -0.110529095, -0.03369232, 0.056786604, 0.2569229, -0.05931065, -0.22081214) * go_3(1.0, -1.0);
result += mat4(0.066926084, 0.029664058, -0.10779271, 0.11026963, 0.23927264, -0.16914488, 0.022947345, 0.12303853, -0.07066212, -0.013205378, 0.15348643, 0.035568032, 0.20966691, 0.010149819, -0.08814468, -0.064854674) * go_3(1.0, 0.0);
result += mat4(0.11493852, -0.074924305, -0.14840698, -0.16956823, 0.056806292, -0.06387947, -0.06880271, -0.04637334, -0.1929893, 0.18226422, 0.064644486, -0.1594863, 0.027403917, 0.13951495, -0.06569123, -0.07700207) * go_3(1.0, 1.0);
result += vec4(-0.043347504, -0.20504741, -0.037821215, -0.014486937);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_4_tf
//!BIND conv2d_4_tf1
//!SAVE conv2d_5_tf1
//!WIDTH conv2d_4_tf.w
//!HEIGHT conv2d_4_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_4_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_4_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.047881734, -0.09396414, -0.2839081, 0.3140853, 0.052613556, 0.09940423, 0.23960467, -0.022228222, -0.12065009, 0.07898222, 0.08657881, 0.010852739, -0.050450284, 0.01683982, 0.031813968, 0.053060856) * go_0(-1.0, -1.0);
result += mat4(-0.10252411, -0.03116448, -0.30114275, -0.0316799, -0.017501019, -0.03006003, -0.2095696, 0.10134927, -0.3901916, -0.15335023, -0.11955071, 0.1337449, 0.101239376, -0.25044814, 0.2128469, 0.018979514) * go_0(-1.0, 0.0);
result += mat4(-0.13392173, 0.052036732, 0.1682114, -0.026263753, 0.027221246, -0.15121374, 0.13723798, 0.08950682, -0.1182108, -0.07294226, 0.023392374, 0.052329235, -0.05632852, -0.07036173, 0.06872573, 0.05238042) * go_0(-1.0, 1.0);
result += mat4(0.18112028, 0.18242362, -0.06812871, 0.032463413, 0.124638766, -0.26765212, -0.07678663, 0.33806562, 0.09674393, 0.15574542, 0.23634006, -0.02873782, -0.1626769, -0.14760062, -0.007274849, 0.09866139) * go_0(0.0, -1.0);
result += mat4(-0.10726673, -0.10925056, 0.19967109, -0.19936769, 0.15942842, -0.14870064, 0.15493345, -0.08489036, -0.49053356, -0.17321263, 0.28426084, 0.18721215, -0.09898434, -0.2751838, -0.11833524, 0.028445128) * go_0(0.0, 0.0);
result += mat4(-0.11788817, -0.23724948, -0.046072144, 0.035621114, 0.04527003, -0.0073492974, 0.11097195, 0.06806836, 0.04814677, -0.1408476, -0.1325629, 0.00929532, -0.16699041, -0.03034791, 0.08320368, -0.15429299) * go_0(0.0, 1.0);
result += mat4(0.2729515, 0.008244692, -0.17441982, -0.39026466, 0.17381759, 0.31194404, 0.055934936, 0.20744409, 0.20119062, 0.0734271, 0.0796807, 0.0031037466, -0.0016392237, 0.033733975, 0.07149338, 0.042083208) * go_0(1.0, -1.0);
result += mat4(0.07985744, 0.10945015, 0.018472541, 0.1397503, 0.2005682, 0.42641, 0.23022486, -0.2916921, 0.028285174, -0.31885162, -0.27070364, -0.10390779, 0.0751492, 0.12752363, -0.2279459, 0.08998453) * go_0(1.0, 0.0);
result += mat4(0.18450491, -0.140783, -0.008006845, 0.09029298, 0.12536179, 0.26949662, 0.09491545, 0.063907005, 0.11212244, 0.09778506, -0.1835966, -0.053119674, 0.0072294096, 0.25018227, 0.010868525, -0.22721334) * go_0(1.0, 1.0);
result += mat4(-0.028011927, -0.20073172, 0.5976166, -0.19494139, 0.17958745, -0.03838646, 0.058325976, -0.29409218, -0.12793432, 0.03245129, 0.35662368, -0.05048354, -0.13368197, -0.06151968, -0.012714591, -0.1763054) * go_1(-1.0, -1.0);
result += mat4(0.18468465, 0.31682113, 0.12818255, -0.117110476, 0.13709468, -0.10034022, -0.07994527, -0.1259309, 0.04067299, -0.1147398, 0.28361055, 0.27916273, 0.03696692, 0.16829546, 0.27819383, 0.08305029) * go_1(-1.0, 0.0);
result += mat4(-0.28920117, -0.033877946, 0.01586206, 0.04681198, 0.024248574, -0.045777842, -0.03342128, 0.07525412, -0.063377544, -0.016737273, 0.11235511, -0.04325238, -0.24170023, -0.09993599, -0.03205371, 0.14339828) * go_1(-1.0, 1.0);
result += mat4(-0.008357902, -0.11038377, 0.03709221, 0.26775306, 0.07963845, -0.25377446, -0.17630441, -0.10966474, 0.057311732, -0.083327, 0.044497233, 0.06903858, -0.26531395, -0.103399664, -0.14806591, 0.269314) * go_1(0.0, -1.0);
result += mat4(0.05450808, -0.041993964, -0.07217651, 0.034468375, 0.2117634, 0.0075620585, 0.05825411, -0.2252478, -0.0527787, 0.049732126, -0.032040413, -0.09361454, 0.29585132, 0.018413153, 0.18384546, -0.024226356) * go_1(0.0, 0.0);
result += mat4(-0.031109914, 0.19351351, 0.07405522, -0.06313074, -0.09983541, -0.011495182, 0.11749038, -0.16775608, 0.2790974, -0.09338754, 0.07913264, 0.103792936, -0.18679164, -0.15639925, 0.112943865, 0.07930375) * go_1(0.0, 1.0);
result += mat4(0.004106195, -0.036833283, 0.12908752, 0.12869535, -0.02472107, 0.17561707, -0.025890926, -0.18789047, 0.096218705, -0.16306408, -0.02198454, -0.010134957, -0.09710009, 0.002062143, -0.046785697, 0.0029441968) * go_1(1.0, -1.0);
result += mat4(0.19648251, -0.015663045, -0.0730215, 0.028611008, 0.13529862, -0.015256192, -0.04119306, -0.24628192, 0.02601027, -0.21184283, -0.1962902, 0.09109358, -0.06792383, 0.092336476, 0.12215351, -0.08596062) * go_1(1.0, 0.0);
result += mat4(-0.17530201, -0.0351919, -0.31872514, -0.13933206, -0.07000922, -0.049807087, 0.0010997375, -0.033573963, 0.07442056, -0.33290103, -0.40381998, 0.09435, -0.3280128, -0.09953127, -0.11283648, 0.20685865) * go_1(1.0, 1.0);
result += mat4(-0.052573867, -0.035328753, -0.11132943, -0.17515652, 0.05021051, 0.058642425, -0.046640664, 0.0799107, -0.027398815, -0.33619994, -0.22135767, 0.07894002, -0.14941697, -0.0940996, -0.11655085, 0.049795926) * go_2(-1.0, -1.0);
result += mat4(-0.039301276, 0.041062318, 0.20312686, -0.009338705, 0.013706282, -0.0245852, 0.03458311, 0.09601228, -0.18203016, -0.012260314, 0.17984508, -0.056576703, -0.102844186, 0.24047872, 0.05307189, 0.16066082) * go_2(-1.0, 0.0);
result += mat4(0.1478775, 0.0046362123, 0.05459521, 0.07162838, -0.01896149, 0.23700175, -0.14174299, 0.06988599, -0.32545477, -0.08065096, -0.061227743, -0.0010796773, 0.094327345, -0.20760082, -0.19523263, 0.19859222) * go_2(-1.0, 1.0);
result += mat4(-0.049676366, -0.10381536, 0.02546116, -0.13127093, 0.10954914, 0.0048147943, 0.06962328, -0.30456528, -0.11956627, 0.0150488885, -0.10711722, 0.1684613, -0.1939089, -0.10577047, -0.11980919, -0.036988296) * go_2(0.0, -1.0);
result += mat4(-0.054795764, 0.09491116, -0.08494948, 0.059765853, 0.0131597435, 0.20786162, 0.11999637, 0.024381055, 0.22830428, 0.027053319, -0.011646274, -0.12145409, -0.07899559, -0.012688263, 0.10684157, 0.3824219) * go_2(0.0, 0.0);
result += mat4(-0.23994572, -0.0031532666, -0.0050638164, 0.14236279, 0.05690383, -0.06259682, 0.052624144, 0.20461404, -0.19230312, -0.11072268, 0.013023965, 0.08931543, -0.21997221, 0.11760443, -0.40943825, 0.28656834) * go_2(0.0, 1.0);
result += mat4(-0.06606179, 0.26007771, 0.033754125, 0.119690455, 0.024669139, -0.06752839, 0.12688096, -0.0063201943, -0.17123021, 0.07548857, -0.14213699, 0.034093797, -0.15632647, -0.123243414, -0.42634043, 0.1715022) * go_2(1.0, -1.0);
result += mat4(-0.046503466, 0.13876389, 0.17973013, -0.25938338, -0.18824704, -0.11876702, 0.31065792, -0.041042212, -0.061369427, 0.2057992, 0.17295738, 0.3836555, -0.21109799, -0.10167118, 0.16577047, 0.113483034) * go_2(1.0, 0.0);
result += mat4(-0.24534856, -0.014482421, 0.22515748, -0.12773542, 0.12794174, -0.02528619, 0.41710484, 0.09154934, -0.17805946, -0.25428918, 0.07294183, 0.047079418, -0.30949152, -0.08919157, 0.17888431, 0.17706038) * go_2(1.0, 1.0);
result += mat4(-0.1741826, 0.046225294, -0.10761791, 0.2619953, 0.007373745, 0.05104337, -0.22309966, 0.34529984, -0.034363825, -0.022187237, -0.08609555, 0.16842419, 0.28136057, 0.17843607, -0.11307746, -0.05668021) * go_3(-1.0, -1.0);
result += mat4(-0.12310616, -0.29661375, -0.10581025, -0.049584012, 0.19651765, 0.08436489, -0.14533581, -0.029874112, -0.15422897, -0.062741704, -0.22694711, -0.15547274, -0.15181333, 0.0286061, 0.022438493, -0.062447168) * go_3(-1.0, 0.0);
result += mat4(0.3497046, -0.09455009, 0.060618952, -0.2134236, 0.054515295, 0.07451165, -0.09267233, -0.010513333, 0.13842636, 0.11563433, -0.054750167, 0.050432, 0.1514256, 0.04284002, -0.2095581, 0.07907657) * go_3(-1.0, 1.0);
result += mat4(-0.11745651, -0.04717057, 0.085377194, -0.065956995, 0.07280491, 0.2730059, 0.11088276, 0.2437957, 0.14018989, 0.1164107, -0.09516929, 0.0022427947, 0.111544006, -0.0680495, 0.09324579, -0.12482022) * go_3(0.0, -1.0);
result += mat4(-0.07995795, -0.03387884, 0.019846136, 0.10231208, -0.07017192, 0.18659039, 0.035161644, 0.101182766, -0.14901665, 0.21307294, 0.063894205, -0.27546507, -0.24792959, -0.067731075, 0.13146006, -0.19333683) * go_3(0.0, 0.0);
result += mat4(0.034206454, 0.1472648, -0.07406727, 0.014654025, 0.18703444, 0.1319857, -0.10610886, 0.08427947, -0.017536618, -0.06487879, -0.12095286, -0.050414838, 0.03260879, 0.1558894, -0.031887084, 0.11840288) * go_3(0.0, 1.0);
result += mat4(0.114811294, -0.14574333, -0.09392587, 0.042283528, 0.08919092, 0.18259068, 0.0980717, 0.21024778, -0.1280008, -0.027260462, -0.1129027, 0.18722472, 0.13733985, 0.047153983, 0.030871978, 0.1998385) * go_3(1.0, -1.0);
result += mat4(-0.06783575, 0.004612595, 0.1153467, -0.11531557, -0.048889533, 0.07673577, -0.02041786, 0.22744459, -0.13092506, 0.13484807, 0.40003043, -0.053706612, -0.16985156, -0.04791236, -0.052443005, -0.08363625) * go_3(1.0, 0.0);
result += mat4(0.18187882, 0.017893985, 0.17856054, 0.005413129, 0.014147176, 0.15102178, 0.12436294, -0.02176765, -0.16727823, -0.0364111, 0.17074408, 0.12899421, 0.31984514, -0.0072070034, 0.031895883, -0.1991405) * go_3(1.0, 1.0);
result += vec4(-0.011865144, 0.11717201, -0.13823777, -0.059450272);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_5_tf
//!BIND conv2d_5_tf1
//!SAVE conv2d_6_tf
//!WIDTH conv2d_5_tf.w
//!HEIGHT conv2d_5_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_5_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_5_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.082203194, 0.021720003, 0.03725474, -0.08048348, 0.2063248, -0.033020593, -0.17585336, 0.06476272, 0.012244563, 0.026554609, 0.014708393, 0.26606125, 0.14248778, 0.12817341, -0.039826933, -0.12751861) * go_0(-1.0, -1.0);
result += mat4(0.24573852, 0.19695967, -0.06257417, -0.04782871, 0.3511875, -0.018083302, -0.077342674, 0.15247667, 0.20321761, -0.07479984, -0.09548503, 0.08109568, -0.23808748, 0.07246303, -0.004242619, 0.16162953) * go_0(-1.0, 0.0);
result += mat4(0.13296306, 0.19495387, 0.009222276, 0.033592198, 0.20443891, 0.16063854, -0.2581601, -0.016132578, -0.2296461, -0.23647323, -0.15407176, -0.18265317, 0.2343241, -0.049697313, -0.09398783, 0.41931856) * go_0(-1.0, 1.0);
result += mat4(-0.10866088, -0.40605694, -0.0042648134, 0.07943803, 0.26914695, 0.14816476, 0.037706107, -0.123223364, -0.19962949, -0.053534556, -0.08397409, -0.04244924, -0.075791344, 0.29629225, 0.2311928, 0.099177904) * go_0(0.0, -1.0);
result += mat4(-0.1748319, -0.2003186, -0.32659066, -0.21007413, 0.20122464, 0.032196607, -0.026299698, 0.33395135, 0.11411664, 0.05971959, 0.09001304, -0.15936212, 0.012322024, 0.19936106, -0.411186, -0.08319479) * go_0(0.0, 0.0);
result += mat4(-0.07349218, 0.006184436, 0.096199185, -0.050186496, 0.064047046, -0.03813128, -0.057007037, -0.025550695, -0.2863145, -0.008512981, -0.20615962, 0.18009211, 0.008298396, 0.22452813, 0.010843521, 0.20169461) * go_0(0.0, 1.0);
result += mat4(0.2691149, 0.059546687, 0.08922005, 0.2252196, 0.30341956, -0.024489028, 0.087045394, -0.03856442, -0.14083561, -0.17683443, 0.14137806, 0.15520614, 0.2073925, -0.19525874, 0.23661858, 0.3098405) * go_0(1.0, -1.0);
result += mat4(0.006530723, 0.04180736, -0.04762067, -0.064395495, 0.02396811, -0.13332283, 0.0037775645, 0.026309434, 0.0033065109, -0.08315753, 0.02917419, 0.12330464, 0.22819455, -0.07489677, 0.12829056, -0.097994626) * go_0(1.0, 0.0);
result += mat4(-0.09983759, 0.032783493, 0.11085758, 0.08993078, -0.057110567, -0.018973934, -0.14946178, -0.03921629, 0.039757587, 0.015860094, 0.04989561, -0.19634786, 0.04351146, 0.019315343, 0.25972188, 0.17989321) * go_0(1.0, 1.0);
result += mat4(-0.04111906, -0.165601, 0.0003682197, -0.056232415, -0.32716644, -0.24015541, -0.057547837, 0.05966729, 0.06854747, 0.03599213, -0.18798864, 0.1183447, 0.014268468, -0.1310834, 0.06415977, -0.19414157) * go_1(-1.0, -1.0);
result += mat4(-0.00070661673, 0.17671427, 0.10584568, -0.060910843, -0.104282066, -0.22676118, -0.01907062, 0.24882245, -0.043454725, 0.07691623, -0.48371696, 0.013537671, -0.025488405, 0.061228953, 0.18548754, 0.028671112) * go_1(-1.0, 0.0);
result += mat4(-0.0121596735, 0.09595702, -0.08244918, -0.1176173, 0.26773354, -0.021729136, 0.075465776, -0.0928876, 0.12461298, 0.16830076, -0.15302569, 0.113850676, 0.09811088, 0.13006307, 0.24999009, 0.10261325) * go_1(-1.0, 1.0);
result += mat4(-0.032246377, 0.038265374, -0.26476422, -0.1442876, -0.19866082, 0.08649541, 0.041478764, 0.11155026, 0.21576422, -0.09572912, -0.11174068, -0.19722937, -0.15801935, 0.29604745, -0.08606268, -0.15532136) * go_1(0.0, -1.0);
result += mat4(-0.06315591, 0.16151646, -0.009230362, -0.04341246, 0.09085519, 0.21924476, 0.38044852, 0.193819, 0.16622902, 0.0025134624, -0.22688466, -0.025276015, 0.07714917, 0.16302192, -0.11767101, -0.11086476) * go_1(0.0, 0.0);
result += mat4(-0.04170153, 0.001859292, -0.26352355, 0.10982333, -0.031867817, 0.15773517, -0.060263418, 0.11117763, -0.017359972, 0.0127261225, 0.0782802, -0.16908924, 0.080516845, -0.05691526, -0.07530135, -0.14553802) * go_1(0.0, 1.0);
result += mat4(0.06112685, -0.032287434, 0.17445667, -0.044935808, -0.11449107, -0.051394563, -0.029589338, -0.14555557, 0.03440661, 0.11035615, -0.17175, -0.14851089, 0.037362, -0.18740481, 0.17278154, 0.18073405) * go_1(1.0, -1.0);
result += mat4(-0.27670652, 0.19484822, 0.2609349, 0.1455016, 0.04438468, 0.1449185, 0.11185832, -0.18598269, -0.019846648, 0.11886126, -0.098498635, 0.15737785, 0.011406795, -0.18860829, -0.13705735, 0.17535745) * go_1(1.0, 0.0);
result += mat4(-0.30244905, -0.28695273, 0.1146976, 0.21144345, -0.037980128, -0.027679864, -0.13992494, -0.04884521, -0.032023884, -0.07921183, -0.16042095, -0.06935386, -0.06570237, -0.1107404, -0.018163798, 0.22625941) * go_1(1.0, 1.0);
result += mat4(-0.07292955, -0.07321777, -0.045146503, -0.33291966, -0.096732594, -0.07203495, 0.33692798, 0.2870733, 0.122160144, -0.076574564, 0.042844944, 0.26448342, 0.07672146, -0.028775277, -0.12088313, 0.15583947) * go_2(-1.0, -1.0);
result += mat4(0.21589327, 0.05258274, 0.09705794, -0.024653846, -0.039402515, 0.28485695, 0.14711736, -0.10556087, -0.15140481, 0.09039498, 0.017308712, 0.11862922, 0.08230978, 0.21678248, -0.043815188, -0.226433) * go_2(-1.0, 0.0);
result += mat4(-0.029258793, 0.26618922, 0.02564014, -0.23189862, -0.24074338, -0.18556763, 0.25973624, 0.04746873, 0.0137007125, -0.22239363, -0.12414957, 0.048228756, -0.22406264, 0.282667, -0.021001073, -0.17465611) * go_2(-1.0, 1.0);
result += mat4(0.32401654, -0.1495363, -0.20869227, 0.04271639, -0.0087802755, 0.031325378, 0.23834595, 0.039336167, 0.17265107, 0.20947595, 0.28737286, 0.0028783784, -0.057340365, -0.050347418, -0.11915604, -0.1831807) * go_2(0.0, -1.0);
result += mat4(0.1811338, 0.07732653, 0.20975596, -0.47129005, 0.07121942, 0.08410583, 0.44170937, -0.19524159, -0.17807977, 0.12837476, 0.20816846, -0.1741958, -0.04411918, 0.06024972, 0.18159702, -0.052485272) * go_2(0.0, 0.0);
result += mat4(-0.15229738, 0.27513, 0.28150418, -0.19543962, -0.02045864, -0.07207227, 0.09589587, 0.09110817, 0.061413247, 0.0046052113, 0.11619411, -0.2988938, 0.065739445, 0.10205611, 0.12847126, -0.028355654) * go_2(0.0, 1.0);
result += mat4(0.0657154, -0.047568597, -0.16148911, 0.16392621, -0.25281775, -0.061153214, 0.017480455, -0.026288848, 0.20319715, 0.04763355, 0.010444491, -0.26671803, -0.25821987, 0.32863674, -0.30734694, -0.18190521) * go_2(1.0, -1.0);
result += mat4(-0.042703815, 0.06633036, -0.048434302, -0.17176376, -0.12699759, -0.1124558, 0.083266065, 0.03354623, -0.13468939, 0.12706263, 0.053659134, -0.06930602, 0.008196115, 0.2034998, -0.06351442, -0.039730288) * go_2(1.0, 0.0);
result += mat4(0.09614661, 0.22500272, 0.088511504, -0.16960482, 0.15364788, -0.18854137, -0.13163191, -0.07503735, -0.23177068, -0.0053305267, -0.041978605, 0.0971947, -0.049034655, 0.04486706, 0.09076307, -0.02310868) * go_2(1.0, 1.0);
result += mat4(-0.1304683, 0.17743458, -0.09817326, -0.0646786, 0.07886976, 0.20109388, -0.034114968, -0.2029261, -0.03348398, 0.029337432, -0.07302782, -0.02240758, 0.030242773, -0.30032325, 0.02085572, -0.027314361) * go_3(-1.0, -1.0);
result += mat4(-0.037377544, 0.026350772, -0.07430488, -0.114671774, -0.126935, -0.046512567, -0.033628833, -0.19018382, -0.041053895, -0.031206857, 0.08562848, -0.01875709, 0.21099389, -0.092511, 0.0073047103, -0.009811013) * go_3(-1.0, 0.0);
result += mat4(0.11358029, 0.17468451, -0.12739041, -0.14332245, -0.22230148, 0.16862972, -0.04462456, 0.2469604, -0.008622369, 0.0081848325, -0.17032363, -0.16024362, 0.21178265, 0.037127133, 0.08559072, 0.11584694) * go_3(-1.0, 1.0);
result += mat4(0.008993893, -0.08037705, 0.4426555, 0.15593371, 0.15273719, -0.03249998, 0.055109, -0.1512612, -0.037183985, 0.20825677, -0.08516227, -0.06664223, -0.10011001, -0.3505215, -0.17941694, 0.052089088) * go_3(0.0, -1.0);
result += mat4(-0.109703645, -0.13505603, 0.1336451, 0.13118869, 0.010915504, 0.12748592, 0.21201555, -0.40841985, -0.11059143, 0.033772044, -0.039282143, 0.03095394, 0.10394723, -0.21343367, -0.10699851, -0.028351074) * go_3(0.0, 0.0);
result += mat4(0.019704714, 0.06243651, 0.09896519, -0.17492259, 0.012675787, -0.004239029, 0.21319824, 0.069183126, -0.0071114586, 0.123431124, -0.24479835, 0.00723795, -0.045293927, 0.014101029, 0.15746681, 0.042405806) * go_3(0.0, 1.0);
result += mat4(0.023828225, -0.0015190929, 0.1194638, 0.082163885, 0.10532113, 0.042044062, 0.02528007, 0.015175004, 0.026613194, 0.33525538, -0.1627064, -0.29887968, -0.197707, 0.038967777, -0.15811683, -0.106895216) * go_3(1.0, -1.0);
result += mat4(0.044362027, -0.04946742, -0.14815849, -0.17660522, -0.034201477, -0.012243106, -0.050183997, 0.06407372, 0.039822515, 0.15880872, -0.0672721, -0.4081093, 0.019489579, -0.060278706, -0.015096743, -0.07799167) * go_3(1.0, 0.0);
result += mat4(0.11861756, 0.27113584, -0.14107186, -0.10246008, -0.124051, -0.1627854, 0.10698585, 0.2846401, -0.061731786, 0.1724438, -0.12428688, -0.09986041, -0.034171514, -0.07100923, 0.041739646, -0.11308375) * go_3(1.0, 1.0);
result += vec4(-0.02981662, -0.26338395, -0.011632586, 0.15063232);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_5_tf
//!BIND conv2d_5_tf1
//!SAVE conv2d_6_tf1
//!WIDTH conv2d_5_tf.w
//!HEIGHT conv2d_5_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_5_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_5_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.17082009, 0.031344634, -0.06131912, 0.00887183, -0.01528174, 0.12943709, 0.24537678, 0.008178781, -0.312396, -0.023583878, 0.07827866, -0.1231261, 0.15081584, -0.18161978, -0.25179705, -0.036934935) * go_0(-1.0, -1.0);
result += mat4(-0.05768411, 0.16785417, -0.1788644, -0.0067257965, 0.021445744, 0.10066516, -0.23864186, 0.1450302, 0.12892793, 0.19856106, -0.24444748, 0.16531628, -0.044425935, -0.02775357, 0.009059946, -0.12958384) * go_0(-1.0, 0.0);
result += mat4(-0.025798557, -0.17238182, -0.34056288, -0.20921059, -0.03576266, 0.1476854, -0.06264234, 0.14452787, -0.04130045, -0.07275762, 0.034578666, 0.2914669, 0.20879944, 0.21359251, -0.048695553, 0.2638088) * go_0(-1.0, 1.0);
result += mat4(-0.022791177, 0.4204545, 0.116855636, 0.20241925, -0.010444933, -0.14462502, 0.022550104, -0.24423064, -0.09417524, 0.045358784, -0.11405829, 0.035979558, -0.2283092, -0.06670842, -0.23852053, -0.22417003) * go_0(0.0, -1.0);
result += mat4(-0.14526704, 0.040880535, 0.14076385, 0.07795045, -0.059177604, -0.13056375, -0.3373641, -0.19344307, -0.29891858, -0.32578763, -0.29061425, 0.1562214, -0.13578376, 0.36586633, 0.24936736, 0.054629393) * go_0(0.0, 0.0);
result += mat4(-0.025790233, -0.13020341, -0.10084969, 0.15767297, -0.09738769, 0.04034404, 0.0038675873, 0.043515608, 0.16899958, -0.29117966, 0.03420067, 0.14432564, -0.10473084, 0.21014084, 0.07775908, -0.09303797) * go_0(0.0, 1.0);
result += mat4(-0.07443987, -0.16225167, 0.036251917, 0.028432872, 0.03759333, 0.004027401, -0.033941846, 0.0019474924, 0.02357054, 0.30748722, 0.1652115, -0.17361522, 0.16905582, 0.08048018, -0.23639561, -0.029408466) * go_0(1.0, -1.0);
result += mat4(0.0461233, -0.09346199, -0.07063276, -0.19447634, -0.049339604, -0.0032855074, -0.22661209, -0.0543389, 0.11924857, -0.21691081, -0.1645725, -0.0075736847, 0.018572787, -0.06552861, -0.01777661, -0.11651732) * go_0(1.0, 0.0);
result += mat4(-0.06425901, 0.123392984, -0.16395192, -0.093448035, -0.029316641, 0.0986573, -0.23135012, 0.011170849, 0.00023920486, 0.15296175, 0.35453254, -0.05189021, 0.20708887, -0.103900835, 0.081992395, -0.21829562) * go_0(1.0, 1.0);
result += mat4(-0.019074136, -0.1572586, 0.27919227, 0.09119617, 0.035954695, 0.2941489, 0.18262725, -0.055522963, -0.21364328, -0.1573611, 0.104966134, 0.08228523, 0.19945285, -0.0039229114, -0.1565048, 0.028975379) * go_1(-1.0, -1.0);
result += mat4(-0.18501253, 0.006473006, 0.06637501, 0.04295065, 0.06411007, 0.1166344, -0.10060226, 0.46296063, -0.08600344, -0.03560105, 0.012215349, 0.017885283, 0.061346993, 0.17336361, 0.01935021, 0.20198092) * go_1(-1.0, 0.0);
result += mat4(-0.04451627, -0.10372061, -0.13968691, 0.14479733, 0.1660607, 0.19334625, 0.0085214665, 0.28863636, -0.07600901, -0.014777084, 0.13209191, -0.09045013, 0.104893915, -0.04776884, -0.007936376, 0.104568765) * go_1(-1.0, 1.0);
result += mat4(0.023751335, -0.108048, -0.050531313, 0.15916029, 0.13246661, 0.04644228, -0.09586482, -0.17222965, -0.22898191, -0.033484615, 0.078883134, -0.052609313, -0.2721741, 0.045986425, 0.13972299, -0.28923607) * go_1(0.0, -1.0);
result += mat4(-0.23364568, -0.008875902, -0.40894926, 0.060443908, -0.2839635, -0.5270991, -0.2500865, 0.002020195, -0.24488612, -0.04982319, -0.009110353, -0.018023955, 0.06647274, -0.25225738, 0.26154432, -0.033934146) * go_1(0.0, 0.0);
result += mat4(-0.1535129, -0.21257545, -0.16553773, 0.17471452, -0.06203719, 0.15238857, 0.18702018, 0.18572305, 0.07740396, -0.074217625, -0.072156586, -0.2183728, 0.00403749, 0.13750519, 0.30362993, 0.06550286) * go_1(0.0, 1.0);
result += mat4(0.37164542, -0.1980723, -0.15659203, 0.19498909, 0.01748114, 0.011807152, -0.05424202, 0.11926474, 0.050406165, -0.12925303, -0.020280985, 0.08429331, 0.14769496, -0.077555746, -0.15216178, -0.27070466) * go_1(1.0, -1.0);
result += mat4(0.35804263, 0.08539285, -0.14785156, -0.13532467, 0.058254432, 0.20448379, -0.006173341, 0.058168225, -0.21714899, -0.13472849, -0.09392532, -0.12753737, -0.097461835, -0.11419082, 0.09384189, 0.06414768) * go_1(1.0, 0.0);
result += mat4(0.023494452, -0.22187226, -0.16694295, 0.0204334, -0.26720086, 0.15916729, 0.3098874, -0.10292057, 0.008854983, 0.13375004, -0.04409455, 0.09286524, 0.095829524, 0.12427317, -0.048659876, 0.18300754) * go_1(1.0, 1.0);
result += mat4(-0.119153984, 0.10163183, 0.025017537, -0.40096784, 0.026778705, 0.15821172, -0.19947284, -0.33337715, 0.2952563, 0.16820388, -0.057061996, -0.029319009, -0.12184868, 0.09031512, 0.12028806, 0.021044692) * go_2(-1.0, -1.0);
result += mat4(0.086744264, -0.046958666, 0.2130253, -0.46672252, 0.07135636, 0.0100029735, -0.13828261, -0.012365689, -0.11374441, 0.21084632, -0.059631422, -0.013799735, -0.037889663, -0.10701892, -0.09493782, 0.15516634) * go_2(-1.0, 0.0);
result += mat4(0.031181194, -0.01535001, 0.029270316, 0.13128386, 0.11838377, -0.17051528, 0.12228499, -0.04841128, 0.33350074, -0.006144013, -0.09055018, 0.27470216, -0.26665646, -0.08703671, -0.01719071, -0.23449609) * go_2(-1.0, 1.0);
result += mat4(-0.12856458, 0.005562174, -0.19517267, 0.13270985, 0.2776414, 0.032003902, -0.15778573, 0.15344355, 0.26930434, -0.13459459, 0.035019353, 0.08896612, 0.12847935, -0.122637205, 0.001815178, 0.08290523) * go_2(0.0, -1.0);
result += mat4(0.33805037, -0.15318587, -0.20955376, -0.26121393, -0.026022578, -0.1617741, 0.1336867, 0.026223289, 0.012059392, -0.17295446, -0.060811974, 0.14027825, -0.21134059, -0.08408573, -0.23773228, 0.110836074) * go_2(0.0, 0.0);
result += mat4(0.16176093, 0.15307428, -0.07711325, -0.3458805, 0.061291527, 0.023916256, 0.21370678, 0.0015756418, 0.10642374, 0.24807373, 0.11164451, 0.10780487, 0.087194376, -0.2718231, -0.008457387, 0.054078236) * go_2(0.0, 1.0);
result += mat4(-0.03259038, -0.20923306, 0.165477, 0.098864526, -0.02734457, 0.08871225, -0.01552188, 0.047712058, 0.055032052, -0.13044262, -0.2899521, 0.22230095, -0.029343741, -0.16427459, -0.005436118, -0.05111821) * go_2(1.0, -1.0);
result += mat4(0.20065974, -0.1556366, -0.12620135, 0.44572976, -0.020925352, 0.12025185, 0.20588058, 0.06391864, 0.046870507, 0.16942503, -0.049370963, 0.008779016, 0.04954915, 0.090298936, -0.16466027, 0.011152038) * go_2(1.0, 0.0);
result += mat4(0.13587528, 0.047841422, 0.19804007, -0.1672396, -0.072491, 0.04543739, 0.25287256, 0.015226213, 0.02007356, -0.049578942, -0.08796175, 0.1714897, -0.07819061, 0.1509537, 0.093094915, 0.031139288) * go_2(1.0, 1.0);
result += mat4(-0.013774682, 0.118201815, -0.009592314, -0.10837201, -0.0686881, -0.083380274, 0.107689425, 0.046642892, 0.119898744, -0.05502989, -0.19719897, 0.0005697584, -0.0921928, 0.032281205, 0.2568853, 0.2325449) * go_3(-1.0, -1.0);
result += mat4(0.02991112, -0.09898633, 0.06076172, -0.20906185, 0.0026118348, 0.06130956, 0.06760944, -0.16662054, 0.065741204, -0.13144116, 0.011419801, 0.22552124, 0.1465757, -0.07417319, -0.10788749, -0.24952699) * go_3(-1.0, 0.0);
result += mat4(-0.19238451, -0.024058497, 0.19580396, -0.067399554, -0.18832864, -0.11752747, -0.078949094, -0.23762032, -0.04141864, 0.022530237, -0.02222157, 0.0054874527, 0.057746816, -0.34854797, 0.028730657, -0.08976777) * go_3(-1.0, 1.0);
result += mat4(0.16888975, 0.19949849, -0.08456147, -0.03619044, -0.019596824, 0.11214634, 0.13971676, 0.22926724, 0.03219445, -0.04566354, -0.14948955, -0.22817011, -0.08714846, -0.19684613, 0.15479128, 0.2433362) * go_3(0.0, -1.0);
result += mat4(0.16050309, -0.102841675, 0.20855242, -0.011171905, -0.10309409, 0.22455123, 0.15892951, -0.06582373, 0.010079549, -0.2055006, -0.09385158, 0.006519388, 0.11838815, 0.37134558, -0.165772, 0.12704434) * go_3(0.0, 0.0);
result += mat4(0.11643292, 0.03294274, -0.09800525, -0.13601723, -0.081318736, -0.059975546, -0.039105035, -0.2893635, -0.13024913, -0.058016162, -0.09961072, 0.10532414, 0.24250132, -0.35546342, -0.092634924, 0.093994915) * go_3(0.0, 1.0);
result += mat4(-0.18799333, 0.25611782, 0.014645917, -0.063751906, 0.06498416, 0.16619027, -0.14411639, 0.3914421, -0.07343631, -0.116468735, -0.10941946, -0.2553544, -0.37774643, -0.0018441634, 0.06827239, -0.0122299045) * go_3(1.0, -1.0);
result += mat4(-0.11884597, -0.2477297, 0.048488285, -0.06438257, -0.124703035, 0.25932777, 0.0650111, -0.0930877, 0.06463341, -0.000544085, 0.0147504965, -0.170097, -0.13241997, 0.20983136, -0.15956205, 0.03424298) * go_3(1.0, 0.0);
result += mat4(-0.034574904, 0.06755256, 0.09508443, -0.17162292, 0.046379335, 0.2178781, 0.08699012, -0.055380464, -0.2237568, -0.07427848, -0.028395249, -0.3225617, -0.084454566, -0.24776657, 0.254169, 0.13229847) * go_3(1.0, 1.0);
result += vec4(0.18765923, -0.07697714, 0.028134674, -0.060966115);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_6_tf
//!BIND conv2d_6_tf1
//!SAVE conv2d_7_tf
//!WIDTH conv2d_6_tf.w
//!HEIGHT conv2d_6_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_6_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_6_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_6_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_6_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.21919365, 0.36627784, 0.12603314, 0.24306288, 0.06447028, 0.06472204, -0.05997039, -0.15651788, 0.017059859, -0.006497198, -0.4189735, 0.021636713, -0.23887977, -0.014220949, 0.031113686, -0.17342716) * go_0(-1.0, -1.0);
result += mat4(-0.10818789, -0.03273837, 0.33918005, -0.19290088, 0.0955361, -0.34107623, -0.054906327, -0.18083344, -0.060723677, 0.24395694, 0.112975016, -0.07254578, -0.14389384, 0.13235968, -0.15054801, -0.26216486) * go_0(-1.0, 0.0);
result += mat4(-0.23442148, -0.07857079, 0.022283873, -0.2656417, 0.037092753, -0.037313666, -0.5057047, 0.042533103, -0.120424, 0.00021930189, -0.0044566668, -0.45536995, 0.00040759926, 0.14597592, -0.094990164, -0.036161344) * go_0(-1.0, 1.0);
result += mat4(0.15024352, 0.19903262, -0.0734784, 0.092836305, -0.025753846, 0.024750374, -0.07550193, 0.035420835, 0.11084378, 0.26119822, -0.08443512, -0.0047807065, -0.042685136, 0.24889739, 0.098650105, 0.2088369) * go_0(0.0, -1.0);
result += mat4(-0.25551823, 0.14455976, 0.19886157, -0.23465924, 0.20711218, -0.20875362, -0.11320392, -0.30852005, -0.06795657, 0.008670962, 0.30601278, 0.6929064, 0.17079145, 0.15744895, 0.06441601, 0.06514001) * go_0(0.0, 0.0);
result += mat4(0.03142604, -0.006410137, -0.023654792, -0.05708553, 0.062985405, -0.077010594, 0.078804865, 0.050882503, 0.010274228, -0.15558401, 0.09490256, 0.14964707, -0.11966925, -0.36176664, 0.27809814, -0.18862294) * go_0(0.0, 1.0);
result += mat4(0.05609992, 0.0041612233, -0.08498908, 0.04479823, -0.080117956, -0.17423204, -0.22858045, 0.054569032, -0.050866384, -0.020000307, 0.027000953, -0.67724514, 0.16240878, -0.04641204, 0.0648367, -0.20613132) * go_0(1.0, -1.0);
result += mat4(0.08542306, -0.08254248, -0.11090553, -0.14140448, -0.10788511, -0.13011602, -0.29319742, -0.26007155, 0.11033401, -0.31966573, 0.32668245, 0.19542319, 0.06329418, 0.20904626, 0.2724067, -0.009155685) * go_0(1.0, 0.0);
result += mat4(-0.007403411, 0.0012836396, -0.23446666, -0.03017208, 0.062420018, -0.13611084, -0.2975928, 0.13173148, -0.03679939, 0.13743873, -0.10121899, 0.074514665, 0.1497629, -0.09523838, 0.39018926, 0.37807035) * go_0(1.0, 1.0);
result += mat4(0.11441487, -0.19565523, -0.25757137, -0.16148767, 0.15575317, -0.12657928, 0.10479676, 0.062919036, 0.010544159, 0.22931573, 0.20360178, 0.4637635, -0.3395036, -0.52467215, 0.08759308, 0.028030418) * go_1(-1.0, -1.0);
result += mat4(0.2699195, -0.34218305, 0.15259695, 0.03139074, -0.024053533, -0.029567484, 0.28480124, 0.20525953, 0.15452823, -0.217713, 0.15861876, -0.012275699, 0.21408023, 0.097508304, -0.57126766, -0.14679857) * go_1(-1.0, 0.0);
result += mat4(-0.0755847, -0.09751562, -0.29480466, -0.22285318, 0.14196442, 0.114573136, -0.22294767, 0.12463806, 0.3322209, -0.04631724, -0.11097061, -0.27986854, -0.16099304, -0.060079545, 0.00299308, 0.120776065) * go_1(-1.0, 1.0);
result += mat4(0.050933484, -0.13776319, -0.18809728, 0.24035202, -0.32528606, -0.41684148, -0.029342847, 0.28642926, -0.07963454, -0.12905268, 0.07606093, 0.24670005, -0.08815598, -0.23320907, -0.008099349, 0.21512873) * go_1(0.0, -1.0);
result += mat4(0.19247563, 0.18083979, -0.09719762, 0.15314941, -0.22350982, 0.46515045, -0.3571128, 0.35953265, 0.06921985, -0.4482386, -0.18732521, -0.5043983, 0.35159567, -0.33315298, -0.21884166, -0.16283798) * go_1(0.0, 0.0);
result += mat4(-0.021124054, -0.007966742, 0.0052493825, 0.022550896, 0.030403977, 0.3377868, -0.47602004, -0.077664234, -0.07222509, -0.07486097, -0.37971064, -0.5107857, -0.06299477, 0.04930232, -0.3330487, 0.29845512) * go_1(0.0, 1.0);
result += mat4(-0.063705474, -0.07917637, -0.02026607, -0.05142568, 0.021577014, -0.07379867, 0.033937998, 0.08148773, -0.02717838, -0.03233838, 0.098000035, 0.036476444, -0.13366953, 0.014477577, 0.24064232, 0.39313284) * go_1(1.0, -1.0);
result += mat4(-0.16046515, -0.094624564, 0.35435164, 0.09942324, -0.07137174, -0.27999225, 0.124644354, -0.0062176553, 0.015016751, -0.05500243, -0.23249559, -0.4508382, 0.1860433, 0.10671491, -0.033345353, -0.06611453) * go_1(1.0, 0.0);
result += mat4(0.21614046, -0.01307525, -0.18941112, -0.20533535, -0.14481686, -0.47801897, 0.22605121, -0.20298961, -0.06744227, -0.20377496, -0.11926173, 0.15645133, -0.31570885, -0.3495616, -0.024666889, 0.040965475) * go_1(1.0, 1.0);
result += mat4(-0.11748018, -0.039976366, -0.00084064255, -0.028653437, -0.16216733, -0.036768105, 0.018064514, -0.0928936, 0.14008482, -0.064511225, 0.24329947, -0.0268608, 0.050330248, 0.08540601, -0.07272679, -0.01187671) * go_2(-1.0, -1.0);
result += mat4(-0.09459936, -0.011723822, -0.06952858, -0.07808506, -0.065588176, 0.332501, -0.0120042395, 0.07668016, 0.14735217, -0.14856043, -0.06702449, -0.020953184, -0.023006834, 0.06135422, 0.1491448, -0.028061569) * go_2(-1.0, 0.0);
result += mat4(0.25136968, 0.25146323, -0.108277924, -0.20407207, -0.0013780294, 0.16108194, 0.25143847, 0.06672421, -0.033905584, -0.021144686, -0.019152718, 0.34619498, 0.14560962, 0.034437314, 0.024790365, -0.049976267) * go_2(-1.0, 1.0);
result += mat4(-0.24928351, 0.12637813, 0.23609994, 0.12722939, -0.036997862, -0.16554876, 0.11144095, -0.10040036, -0.020359103, -0.080701865, -0.3142192, 0.27257237, 0.13546956, -0.14416885, 0.028196262, -0.2886465) * go_2(0.0, -1.0);
result += mat4(0.28524777, -0.4236231, 0.27420738, -0.21095508, 0.23475651, 0.115876295, -0.18837357, -0.0260708, 0.030670704, -0.11516913, -0.11365572, -0.2203149, -0.018612983, -0.10719593, -0.031727783, 0.1403327) * go_2(0.0, 0.0);
result += mat4(0.07240512, 0.03139215, 0.12328737, -0.021201206, -0.13971715, 0.072742075, -0.0011289873, 0.0053133667, 0.035639685, -0.04322272, -0.19288473, -0.15812221, -0.19126481, 0.0698514, 0.17619178, -0.035605464) * go_2(0.0, 1.0);
result += mat4(-0.18552057, 0.07259671, 0.011667668, -0.15630563, 0.11414356, 0.14482655, -0.04021029, 0.18495587, -0.11386139, -0.09058561, -0.011265998, 0.23358451, 0.0521358, 0.12495261, 0.021644838, -0.048094347) * go_2(1.0, -1.0);
result += mat4(-0.09222373, 0.0533347, 0.055820454, 0.22382596, 0.18713981, 0.2668916, -0.019384036, 0.012698582, 0.13325234, 0.20361474, -0.33106443, -0.08571572, -0.21243028, -0.10996386, 0.123459645, 0.1534967) * go_2(1.0, 0.0);
result += mat4(0.18133277, 0.18108074, -0.05638664, 0.29533157, -0.2108019, -0.033636626, 0.5015888, -0.15116066, -0.041320793, -0.14764231, 0.07314567, -0.18865979, 0.10276937, 0.094240844, -0.1364283, 0.27812913) * go_2(1.0, 1.0);
result += mat4(0.06040915, 0.23753685, 0.19019844, 0.23948252, -0.07535012, 0.11848904, 0.14389765, 0.050067905, 0.16150077, -0.030053454, 0.12478255, 0.26020208, 0.111198805, 0.06787492, -0.12771018, 0.006687384) * go_3(-1.0, -1.0);
result += mat4(-0.5421617, 0.10414128, -0.21526064, -0.08883624, 0.13145073, -0.29695904, 0.57386386, 0.073361695, -0.09538372, 0.27593842, 0.070922814, 0.21769938, 0.06214975, 0.11847816, 0.10033405, 0.29360098) * go_3(-1.0, 0.0);
result += mat4(-0.16294672, -0.014815565, 0.22046989, 0.16858687, 0.058917344, 0.21384977, 0.18803519, 0.105688855, 0.0355118, 0.20571202, -0.07341922, 0.26624045, -0.0415102, 0.050942056, 0.19727907, 0.20122413) * go_3(-1.0, 1.0);
result += mat4(-0.020470422, 0.15815964, -0.13437317, -0.1967045, 0.074902646, 0.08356444, 0.055913117, -0.12837863, -0.18647918, 0.07002247, 0.038864706, -0.07288784, 0.04135125, -0.016055549, -0.1340297, -0.15578008) * go_3(0.0, -1.0);
result += mat4(-0.07685624, 0.00079105416, -0.068755336, 0.110282525, -0.014170752, 0.041282844, -0.17035173, 0.19439398, -0.3036256, 0.024148455, -0.19566648, -0.06736254, 0.14203559, -0.13016985, -0.32845357, -0.14266774) * go_3(0.0, 0.0);
result += mat4(0.0087252045, 0.098839566, -0.08770506, -0.08499465, 0.015245115, -0.110854514, 0.054458305, -0.018121868, -0.09666134, -0.08316006, 0.24617113, -0.17195955, 0.2574254, 0.06734342, -0.13792352, -0.07306126) * go_3(0.0, 1.0);
result += mat4(-0.0073954533, -0.20126835, -0.22545357, -0.29462856, 0.057408337, 0.11939119, -0.01846476, 0.12534486, 0.15751605, -0.14282645, -0.14219986, 0.14283386, 0.14090413, 0.10500912, 0.03039335, 0.17448832) * go_3(1.0, -1.0);
result += mat4(0.043910783, -0.09140025, -0.21666165, 0.07616939, 0.104454786, 0.309926, -0.12906921, 0.1140117, 0.09372434, 0.049547072, -0.086615674, -0.034449168, 0.096705064, 0.26001686, 0.027063297, 0.12422948) * go_3(1.0, 0.0);
result += mat4(0.1365422, 0.2679611, 0.12037257, 0.43346113, 0.08223084, -0.016788265, 0.13570398, -0.017974345, -0.17922844, -0.09475725, 0.073539585, -0.106947675, 0.08998511, 0.04133868, 0.16586913, -0.26291734) * go_3(1.0, 1.0);
result += vec4(-0.19233678, 0.016725872, -0.008011114, -0.1977463);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_6_tf
//!BIND conv2d_6_tf1
//!SAVE conv2d_7_tf1
//!WIDTH conv2d_6_tf.w
//!HEIGHT conv2d_6_tf.h
//!COMPONENTS 4
#define go_0(x_off, y_off) (max((conv2d_6_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_6_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_6_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_6_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.36016628, 0.019064043, 0.3073228, 0.16891135, 0.026739368, 0.31136194, 0.11260383, -0.26918694, 0.0419928, -0.3365078, 0.20189743, -0.04136312, 0.039564647, 0.033199426, 0.18768296, -0.017119858) * go_0(-1.0, -1.0);
result += mat4(0.28663483, -0.41716507, 0.059281543, 0.043736435, 0.0028875466, 0.13817391, -0.12543318, -0.2794053, -0.023528943, 0.10610115, 0.09100278, 0.040132936, -0.21949205, -0.027810011, -0.0301218, 0.084047124) * go_0(-1.0, 0.0);
result += mat4(0.39674807, -0.0040878756, -0.038235947, 0.11880838, 0.009898328, 0.19107847, -0.009313831, -0.1554276, -0.047341663, 0.18049581, -0.029317195, 0.0708909, 0.0708316, -0.110617444, 0.14584038, -0.022261223) * go_0(-1.0, 1.0);
result += mat4(-0.20400241, 0.0896492, -0.010386381, -0.052133385, 0.005023956, -0.06628705, -0.16436209, -0.25345984, -0.05285192, 0.09706557, -0.03778914, -0.152546, 0.17023252, 0.063713826, 0.00743037, 0.056634087) * go_0(0.0, -1.0);
result += mat4(-0.080793336, 0.4204207, 0.19098237, 0.20028038, -0.054076545, 0.22064368, -0.25853387, -0.3643562, 0.2085573, -0.023731, -0.06727709, -0.18683033, -0.18032159, -0.06388348, 0.304463, -0.2517781) * go_0(0.0, 0.0);
result += mat4(0.11940941, 0.10624008, 0.16120581, 0.2369602, 0.3321827, 0.4272075, -0.10403669, -0.31388018, -0.006372124, -0.00653671, 0.109810196, 0.2277172, 0.005771998, 0.086026914, -0.08934813, -0.094941735) * go_0(0.0, 1.0);
result += mat4(-0.13233568, 0.24112508, -0.0068006413, 0.12466225, 0.11396591, -0.07249253, -0.29090378, -0.12828146, -0.22001141, -0.08532405, -0.11932601, 0.29452974, 0.09572195, 0.017603843, 0.12454017, 0.16321751) * go_0(1.0, -1.0);
result += mat4(0.042107448, -0.00807216, 0.06580674, -0.1289527, 0.13977426, -0.037159685, -0.21001346, -0.08698161, 0.22370502, -0.29170328, 0.2179206, 0.36621302, 0.0825477, -0.016513655, -0.11157249, 0.12861598) * go_0(1.0, 0.0);
result += mat4(0.2246826, -0.13262233, 0.12131653, -0.15522355, 0.38104856, 0.030237729, 0.1286289, -0.19770473, -0.16175011, -0.13688888, 0.23505463, 0.21333031, 0.76352316, -0.17949077, -0.13124311, 0.1613879) * go_0(1.0, 1.0);
result += mat4(-0.050607495, 0.0846705, -0.06136092, -0.033436477, 0.41138348, 0.037043408, -0.02676336, -0.37771952, 0.22147503, 0.06490757, -0.04266158, -0.22606373, 0.045775007, -0.054498192, -0.21495876, -0.036050417) * go_1(-1.0, -1.0);
result += mat4(-0.06242522, 0.2700824, -0.05602621, -0.12361551, 0.14477442, 0.19403581, 0.23505251, -0.072234035, -0.15831544, 0.4640447, -0.104754634, -0.004539681, -0.20246096, 0.23216484, -0.35886365, 0.11360777) * go_1(-1.0, 0.0);
result += mat4(0.14777757, 0.18951412, 0.027219458, 0.11216015, 0.02997997, -0.13466355, -0.0010830094, 0.021302953, 0.23441231, -0.14529245, 0.08068729, 0.10044398, 0.3972878, 0.26570204, 0.0046810666, -0.2863261) * go_1(-1.0, 1.0);
result += mat4(-0.10385485, 0.1053724, 0.16961229, 0.20727012, -0.025148917, -0.011365095, 0.03899919, -0.030950211, 0.079080455, -0.32767853, 0.064670205, -0.035771385, 0.16833797, -0.21567492, 0.30871257, -0.19965471) * go_1(0.0, -1.0);
result += mat4(-0.23420888, -0.004894698, -0.18162623, -0.31107524, 0.11976508, 0.14924951, -0.08723316, 0.21401922, -0.58200324, -0.01177345, -0.049033508, 0.19593577, -0.21139073, 0.13016601, 0.08734843, 0.4158892) * go_1(0.0, 0.0);
result += mat4(0.0009789813, 0.33274913, 0.017405733, -0.042906318, -0.26410276, -0.09291333, 0.019387102, 0.105381854, -0.009176527, 0.09483514, -0.28462934, -0.03644404, 0.285194, -0.4260311, 0.14902237, -0.115670316) * go_1(0.0, 1.0);
result += mat4(-0.09344311, 0.4463103, 0.19984834, -0.09733857, -0.118717775, -0.0708026, 0.24919955, -0.11234634, 0.1246395, -0.052909933, 0.1525815, 0.07724016, 0.0070534665, -0.06404165, -0.18149726, -0.014058336) * go_1(1.0, -1.0);
result += mat4(-0.17353044, 0.15376104, 0.004588994, -0.13554202, -0.19920237, -0.18918681, 0.11327512, -0.117296435, -0.0785251, 0.013677155, -0.2103214, 0.06843426, -0.27790928, 0.09837545, -0.00019213746, 0.09132539) * go_1(1.0, 0.0);
result += mat4(-0.01586651, 0.014929441, 0.2426186, -0.1889374, -0.0865462, -0.07454513, -0.20797268, -0.22366855, 0.19704159, 0.0048206006, -0.16707218, -0.14162683, 0.036798395, -0.1663155, -0.12009389, 0.09603803) * go_1(1.0, 1.0);
result += mat4(-0.041532192, 0.05753804, 0.17927068, -0.042112097, 0.12080969, -0.15052572, -0.34855765, -0.07356988, -0.28199884, -0.18958664, 0.15879883, 0.08511588, 0.0034213227, -0.05338495, -0.37285298, 0.06626709) * go_2(-1.0, -1.0);
result += mat4(-0.20219134, 0.22150375, -0.29405454, 0.06597703, -0.018885285, -0.010551704, -0.010774283, 0.08758955, -0.2015349, -0.17006227, -0.24321876, -0.06864207, -0.118437864, -0.043977212, -0.029736811, 0.14040919) * go_2(-1.0, 0.0);
result += mat4(-0.18709077, -0.09723938, 0.12783436, -0.15167634, 0.29039705, -0.11009911, 0.018371418, -0.060096707, -0.07256923, -0.25799567, -0.06276934, -0.035992302, -0.06729111, -0.059956793, -0.024079734, 0.011838878) * go_2(-1.0, 1.0);
result += mat4(0.010449175, -0.08212451, 0.1409803, 0.11861122, -0.18035835, 0.051930565, 0.01049551, -0.09447962, 0.12029649, 0.040604513, -0.059971705, -0.0044667358, -0.22080486, -0.11187681, 0.124374695, -0.004155485) * go_2(0.0, -1.0);
result += mat4(-0.28584236, -0.38480133, -0.13987814, -0.4463469, -0.3890419, -0.022498172, 0.17334452, 0.21895568, -0.15450422, -0.10905497, 0.15111905, -0.22554915, 0.106121585, -0.029144369, 0.36059046, 0.22140682) * go_2(0.0, 0.0);
result += mat4(-0.23780307, -0.023033705, 0.068205886, -0.110635854, -0.26720005, -0.1608183, 0.19523881, 0.07972837, -0.018495852, -0.2793956, 0.17668398, -0.12020479, -0.079556085, -0.02284952, 0.031480275, 0.31818348) * go_2(0.0, 1.0);
result += mat4(0.22501226, -0.00829407, 0.059581667, 0.16512989, 0.18711442, 0.1200968, 0.11812652, -0.16091056, 0.15733972, 0.045156084, 0.20640492, -0.16852027, -0.11217177, 0.06746273, -0.050218176, 0.08643783) * go_2(1.0, -1.0);
result += mat4(0.20715691, -0.1082907, 0.027892975, 0.19515261, -0.17838904, 0.1532257, -0.108409844, -0.06632365, -0.13805026, 0.23020233, 0.12416581, -0.14861803, 0.16650471, 0.08158386, -0.09051303, -0.06981649) * go_2(1.0, 0.0);
result += mat4(-0.04617126, 0.06579221, 0.25964734, 0.28500968, 0.07641255, -0.090885855, -0.0972522, 0.18298368, -0.06393334, 0.103463, -0.23062052, -0.15270731, 0.13633437, 0.074707486, 0.15065335, -0.024602572) * go_2(1.0, 1.0);
result += mat4(0.118319295, 0.010410938, 0.044655934, -0.104725905, 0.030477569, 0.12867387, 0.039075315, 0.18922117, 0.13301082, -0.1601557, 0.038168408, -0.07372259, -0.09522213, -0.095107146, -0.16679631, 0.044673234) * go_3(-1.0, -1.0);
result += mat4(0.46229, -0.30780822, -0.09081465, 0.1433387, -0.0315039, 0.059409115, -0.24948491, -0.17146957, 0.060843736, -0.041989822, 0.054005735, 0.22835566, 0.12036598, -0.0070898845, 0.17276852, -0.17754094) * go_3(-1.0, 0.0);
result += mat4(-0.35119572, 0.020034311, 0.08751943, 0.08193488, 0.041884877, 0.22649358, -0.07447533, 0.20845473, -0.04859846, -0.16206735, 0.06819576, -0.053000778, 0.18146423, 0.04694148, 0.045293212, 0.06783575) * go_3(-1.0, 1.0);
result += mat4(0.280914, -0.14998704, -0.23485807, -0.015608296, 0.1549556, -0.11992663, -0.094974115, 0.05887284, 0.053392075, 0.10322464, -0.075066686, 0.068358354, -0.18663338, 0.009901499, -0.123370335, -0.12502703) * go_3(0.0, -1.0);
result += mat4(0.7748568, -0.17870626, -0.20770052, 0.024692526, -0.056430295, -0.06324113, -0.03660047, 0.29629672, -0.51896983, -0.027231261, 0.05903762, 0.077677645, -0.061675485, -0.20277846, 0.10352223, -0.08198446) * go_3(0.0, 0.0);
result += mat4(-0.06347568, 0.21643166, -0.09718546, 0.0372257, -0.029537952, -0.0357135, -0.09548363, 0.18225233, -0.29609334, -0.3496132, 0.18245913, -0.10162589, -0.18189451, -0.09077887, 0.117313184, -0.06863874) * go_3(0.0, 1.0);
result += mat4(-0.047373574, -0.020289376, -0.25748715, -0.13568166, 0.15656634, -0.06841899, 0.012100781, -0.13611819, 0.0016357322, -0.23870537, 0.14035743, -0.14700134, 0.2535575, -0.13697346, -0.13693139, -0.10365287) * go_3(1.0, -1.0);
result += mat4(0.4283934, -0.316192, -0.012617617, 0.018468965, 0.21436644, 0.18408814, -0.42651537, 0.12504087, -0.13894933, 0.091662176, -0.20096369, -0.080727175, -0.005487846, 0.17046383, 0.1383948, -0.0054956395) * go_3(1.0, 0.0);
result += mat4(0.20014295, -0.027282396, -0.06317007, 0.04452042, 0.064600386, 0.072222926, -0.33409226, 0.08063831, -0.022607977, 0.1308856, -0.39691743, -0.094889864, -0.1810531, 0.011367248, -0.2531222, -0.22468317) * go_3(1.0, 1.0);
result += vec4(0.26886886, 0.05874665, 0.10268232, 0.05833081);
return result;
}
//!DESC Anime4K-v4.0-Restore-CNN-(VL)-Conv-3x1x1x112
//!HOOK MAIN
//!BIND MAIN
//!BIND conv2d_1_tf
//!BIND conv2d_1_tf1
//!BIND conv2d_2_tf
//!BIND conv2d_2_tf1
//!BIND conv2d_3_tf
//!BIND conv2d_3_tf1
//!BIND conv2d_4_tf
//!BIND conv2d_4_tf1
//!BIND conv2d_5_tf
//!BIND conv2d_5_tf1
//!BIND conv2d_6_tf
//!BIND conv2d_6_tf1
//!BIND conv2d_7_tf
//!BIND conv2d_7_tf1
//!SAVE MAIN
//!WIDTH conv2d_1_tf.w
//!HEIGHT conv2d_1_tf.h
#define g_0 (max((conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_1 (max((conv2d_1_tf1_tex(conv2d_1_tf1_pos)), 0.0))
#define g_2 (max(-(conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_3 (max(-(conv2d_1_tf1_tex(conv2d_1_tf1_pos)), 0.0))
#define g_4 (max((conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_5 (max((conv2d_2_tf1_tex(conv2d_2_tf1_pos)), 0.0))
#define g_6 (max(-(conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_7 (max(-(conv2d_2_tf1_tex(conv2d_2_tf1_pos)), 0.0))
#define g_8 (max((conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_9 (max((conv2d_3_tf1_tex(conv2d_3_tf1_pos)), 0.0))
#define g_10 (max(-(conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_11 (max(-(conv2d_3_tf1_tex(conv2d_3_tf1_pos)), 0.0))
#define g_12 (max((conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_13 (max((conv2d_4_tf1_tex(conv2d_4_tf1_pos)), 0.0))
#define g_14 (max(-(conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_15 (max(-(conv2d_4_tf1_tex(conv2d_4_tf1_pos)), 0.0))
#define g_16 (max((conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_17 (max((conv2d_5_tf1_tex(conv2d_5_tf1_pos)), 0.0))
#define g_18 (max(-(conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_19 (max(-(conv2d_5_tf1_tex(conv2d_5_tf1_pos)), 0.0))
#define g_20 (max((conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_21 (max((conv2d_6_tf1_tex(conv2d_6_tf1_pos)), 0.0))
#define g_22 (max(-(conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_23 (max(-(conv2d_6_tf1_tex(conv2d_6_tf1_pos)), 0.0))
#define g_24 (max((conv2d_7_tf_tex(conv2d_7_tf_pos)), 0.0))
#define g_25 (max((conv2d_7_tf1_tex(conv2d_7_tf1_pos)), 0.0))
#define g_26 (max(-(conv2d_7_tf_tex(conv2d_7_tf_pos)), 0.0))
#define g_27 (max(-(conv2d_7_tf1_tex(conv2d_7_tf1_pos)), 0.0))
vec4 hook() {
vec4 result = mat4(0.09689336, 0.06046458, 0.072598994, 0.0, 0.11994565, 0.104477674, 0.09302802, 0.0, -0.05718302, 0.050438102, 0.08814741, 0.0, 0.0308889, 0.0033925986, -0.01715605, 0.0) * g_0;
result += mat4(-0.028314235, 0.06597744, 0.0966897, 0.0, 0.035656154, 0.07770106, 0.075551905, 0.0, 0.0001793458, -0.000479495, -0.00297406, 0.0, -0.053916585, -0.016807461, -0.0057141334, 0.0) * g_1;
result += mat4(-0.047189303, -0.0207, -0.020910334, 0.0, -0.07933196, -0.06961211, -0.086069845, 0.0, 0.0943727, 0.008463375, 0.010755166, 0.0, 0.062410597, 0.022625161, 0.04068433, 0.0) * g_2;
result += mat4(0.10270994, -0.019080428, 0.0050091282, 0.0, -0.004672948, -0.013966742, -0.0063746064, 0.0, -2.5856789e-05, 0.03151499, -0.0023983798, 0.0, 0.113539025, 0.12381699, 0.100360274, 0.0) * g_3;
result += mat4(0.07868885, -0.030913834, -0.009213676, 0.0, 0.04870991, 0.021467991, 0.038739506, 0.0, -0.042969644, -0.07122453, -0.08798675, 0.0, -0.09784122, 0.021434791, 0.02510374, 0.0) * g_4;
result += mat4(0.050420716, 0.0729716, 0.076532185, 0.0, -0.019112485, -0.01037939, -0.026948035, 0.0, -0.02591423, 0.008927897, -0.00042541025, 0.0, 0.1043701, -0.0071186824, -0.041817162, 0.0) * g_5;
result += mat4(-0.16143242, -0.0009298223, -0.01228508, 0.0, 0.07744052, -0.018313263, -0.0488145, 0.0, 0.09241393, 0.07128674, 0.055164956, 0.0, 0.054884013, -0.04834418, -0.06281626, 0.0) * g_6;
result += mat4(-0.049036566, -0.05979936, -0.05594288, 0.0, -0.014564307, 0.031926468, 0.037857566, 0.0, 0.015474487, -0.11385003, -0.11527764, 0.0, -0.07076006, 0.057038613, 0.095983796, 0.0) * g_7;
result += mat4(0.03094887, -0.008734403, 0.00042712069, 0.0, 0.053891554, 0.05837673, 0.06200635, 0.0, 0.09071558, -0.04202184, -0.046172567, 0.0, -0.0425916, 0.04905093, 0.020835675, 0.0) * g_8;
result += mat4(0.096628904, -0.037792254, -0.043241944, 0.0, -0.011923947, -0.025950424, -0.031381752, 0.0, -0.060941868, -0.07859433, -0.07535451, 0.0, -0.026777223, 0.08604982, 0.07829908, 0.0) * g_9;
result += mat4(-0.06435972, 0.0036599538, 0.00786578, 0.0, -0.061972067, -0.05681472, -0.06667608, 0.0, -0.106890626, 0.007406496, 0.029977169, 0.0, -0.20519382, -0.044860814, 0.0021225857, 0.0) * g_10;
result += mat4(-0.16876474, 0.012789643, 0.026692612, 0.0, 0.017817136, 0.026935097, 0.02227043, 0.0, 0.01690181, 0.07716103, 0.086527, 0.0, 0.07923805, -0.10443151, -0.10859543, 0.0) * g_11;
result += mat4(0.003730466, -0.024648283, -0.022169832, 0.0, -0.0062762927, 0.022062732, 0.032966793, 0.0, 0.016349113, 0.017197203, 0.020952817, 0.0, -0.1763789, 0.035497356, 0.053835396, 0.0) * g_12;
result += mat4(0.020886675, -0.07054202, -0.079142675, 0.0, 0.06664387, 0.044960167, 0.042230908, 0.0, -0.095019594, 0.012421141, 0.0142890485, 0.0, 0.056814816, -0.012751135, -0.014684506, 0.0) * g_13;
result += mat4(0.011765893, 0.0008920681, -0.0018258415, 0.0, -0.010473814, -0.023085753, -0.028783914, 0.0, -0.023034256, -0.0024786016, -0.0052162083, 0.0, 0.1643386, -0.06132718, -0.09289065, 0.0) * g_14;
result += mat4(0.016597198, 0.09389637, 0.10833379, 0.0, -0.043163072, -0.04714812, -0.035274632, 0.0, 0.09634976, -0.009292612, -0.022424143, 0.0, -0.08765172, 0.0051558353, 0.010900356, 0.0) * g_15;
result += mat4(0.030815786, 0.021069322, 0.01812191, 0.0, 0.084839165, -0.0080813095, -0.029270556, 0.0, -0.10456346, 0.062386703, 0.0665605, 0.0, 0.11926609, -0.1104228, -0.13291118, 0.0) * g_16;
result += mat4(-0.07159541, -0.007267032, -0.010134558, 0.0, 0.008234213, 0.045609634, 0.040295456, 0.0, 0.018416971, 0.01308482, 0.014649557, 0.0, 0.035107512, -0.02140815, -0.030279048, 0.0) * g_17;
result += mat4(0.01918586, 0.03875863, 0.03229402, 0.0, -0.07917104, 0.041135103, 0.057182517, 0.0, 0.08609541, 0.0079662455, 0.004327576, 0.0, -0.14332893, 0.03120354, 0.056732506, 0.0) * g_18;
result += mat4(0.03200192, -0.0035752193, -0.0031064528, 0.0, -0.010902813, 0.014607456, 0.019431474, 0.0, -0.016461229, -0.004938204, -0.004655488, 0.0, -0.033470232, 0.0026075812, 0.005896968, 0.0) * g_19;
result += mat4(0.037410006, 0.048742272, 0.04348088, 0.0, 0.037719514, 0.030768529, 0.03127472, 0.0, 0.056426726, 0.03066893, 0.016440205, 0.0, -0.010599352, 0.022832409, 0.023211194, 0.0) * g_20;
result += mat4(-0.005733291, 0.06365659, 0.06663611, 0.0, -0.041917093, -0.016493445, -0.020438088, 0.0, -0.0014357592, -0.0022506563, -0.0045095007, 0.0, 0.029893145, -0.009129354, -0.015173116, 0.0) * g_21;
result += mat4(0.013052085, 0.005108175, 0.0025906067, 0.0, -0.021950055, -0.036447693, -0.036141638, 0.0, -0.036296472, 0.0068928464, 0.013102313, 0.0, 0.0060471976, -0.024798103, -0.023548538, 0.0) * g_22;
result += mat4(0.0067743887, -0.06191211, -0.062355213, 0.0, 0.0016080744, -0.020445071, -0.016840393, 0.0, 0.028264903, 0.01852915, 0.015891539, 0.0, -0.023877412, -0.013271666, -0.008158679, 0.0) * g_23;
result += mat4(-0.04317466, -0.018953001, -0.020452993, 0.0, -0.009322576, -0.03022352, -0.030970376, 0.0, 0.05653658, 0.05430553, 0.046692245, 0.0, 0.05615359, 0.059338935, 0.056018773, 0.0) * g_24;
result += mat4(0.022878079, 0.03392234, 0.033057988, 0.0, -0.017554542, -0.0141542535, -0.014122613, 0.0, -0.048634093, -0.05316463, -0.047988772, 0.0, -0.058002178, -0.040221967, -0.034025013, 0.0) * g_25;
result += mat4(-0.018253656, -0.04197674, -0.040467236, 0.0, -0.04358929, -0.028309818, -0.025425073, 0.0, -0.008488672, -0.001727991, 0.00035808363, 0.0, -0.0011709273, 0.0052514165, 0.0059479307, 0.0) * g_26;
result += mat4(-0.08333935, -0.09818201, -0.09476284, 0.0, -0.033692095, -0.046259012, -0.045797516, 0.0, -0.007577072, 0.0022402718, 0.0016200038, 0.0, 0.0029786075, -0.020728534, -0.018938033, 0.0) * g_27;
result += vec4(0.047567394, -0.02504617, -0.028163986, 0.0);
return result + MAIN_tex(MAIN_pos);
}
@@ -0,0 +1,300 @@
// MIT License
// Copyright (c) 2019-2021 bloc97
// All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(M)-Conv-4x3x3x3
//!HOOK MAIN
//!BIND MAIN
//!SAVE conv2d_tf
//!WIDTH MAIN.w
//!HEIGHT MAIN.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (MAIN_texOff(vec2(x_off, y_off)))
vec4 hook() {
vec4 result = mat4(-0.010995803, 0.077095956, -0.043992598, 0.06048717, 0.1164834, -0.11689607, 0.072985925, -0.078805886, 0.01182932, 0.054985743, -0.09018186, 0.044907484, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, -1.0);
result += mat4(0.1813623, -0.14752422, 0.025720436, -0.17639883, 0.15697388, 0.10445984, -0.1843076, 0.5264643, 0.047516696, -0.097305484, 0.09740847, -0.29619336, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 0.0);
result += mat4(-0.014534763, 0.09486465, 0.046173926, 0.039391946, 0.09609376, -0.060574662, 0.042200956, -0.3269777, 0.051006425, 0.059818447, 0.04366627, 0.17699827, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 1.0);
result += mat4(0.04268535, -0.08152529, 0.10577459, -0.036936995, -0.051562306, 0.054872766, 0.09194519, 0.0025066638, -0.01073954, 0.00064474024, 0.10038221, 0.02131141, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, -1.0);
result += mat4(-0.51751363, -0.40028602, 0.3469574, 0.5933738, -0.91357684, -0.67692596, 0.57815677, 0.39809322, -0.16341521, -0.27169713, 0.12232366, 0.4318641, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 0.0);
result += mat4(0.12601124, -0.06263236, -0.45907676, -0.41514075, 0.3330334, -0.1929565, -0.6333532, -0.6552794, -0.045809917, 0.046351526, -0.26173338, -0.30252662, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 1.0);
result += mat4(0.0030332592, 0.012103107, 0.010537323, -0.02038607, 0.095558085, 0.097704545, 0.083433494, 0.026790185, 0.01943357, -0.061712462, -0.00015703632, -0.032268334, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, -1.0);
result += mat4(0.016870102, 0.5215812, -0.11525501, 0.027527615, -0.09045733, 0.61310345, -0.1575268, 0.1905386, 0.020172214, 0.3503187, -0.08209157, -0.051328037, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 0.0);
result += mat4(0.005494087, -0.010656317, 0.07682753, -0.08116042, -0.03934524, 0.16589017, 0.101483546, -0.066603065, 0.03494657, -0.07885597, 0.074227594, 0.0016264897, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 1.0);
result += vec4(0.014463938, -0.0031906287, 0.007015422, -0.003888468);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_tf
//!SAVE conv2d_1_tf
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.08532478, -0.14302494, -0.017921071, -0.0032664281, -0.09841952, 0.024187077, 0.10701477, 0.14110753, -0.05714981, -0.10897174, 0.073803626, 0.103992954, 0.07914382, 0.032193683, -0.18346278, -0.09723936) * go_0(-1.0, -1.0);
result += mat4(-0.034482613, -0.10742312, -0.047286414, -0.08641124, -0.33896688, -0.036533825, -0.48337597, 0.034040943, -0.13598205, -0.080917805, 0.08540263, -0.012667689, -0.009171425, -0.120026454, -0.20536867, -0.032149274) * go_0(-1.0, 0.0);
result += mat4(0.18687321, 0.066278316, 0.024327392, 0.08816582, -0.08017908, 0.09488853, 0.26018232, -0.101504356, 0.17487666, 0.31057635, 0.14785016, -0.09622089, -0.07537452, -0.13844088, -0.05810814, 0.09907489) * go_0(-1.0, 1.0);
result += mat4(-0.04183032, 0.15207712, 0.005002397, 0.32277516, -0.16169126, -0.119836345, -0.04068436, -0.096728764, 0.11943901, 0.1789597, -0.20412198, 0.19009817, 0.36630696, 0.06946421, -0.5254373, -0.11896399) * go_0(0.0, -1.0);
result += mat4(-0.31916487, -0.98911583, 1.0728644, -0.39280394, 0.33458877, -0.17325239, -0.645045, -0.28524077, -0.14512783, 0.24996442, -0.09837877, 0.05468934, 0.31559715, -0.020504637, -0.026724018, 0.24507573) * go_0(0.0, 0.0);
result += mat4(-0.23759829, -0.08530173, -0.16665787, -0.22463752, 0.109896734, 0.13446991, -0.049552456, -0.02385489, -0.01245375, 0.3833208, 0.05758832, 0.1528937, 0.0501858, -0.19651426, 0.0076587177, -0.03297025) * go_0(0.0, 1.0);
result += mat4(0.14554465, -0.01826686, 0.10284085, -0.19152659, -0.017585073, -0.05511482, 0.06362406, 0.023924058, -0.0018977845, -0.103172876, 0.03287086, -0.20085956, 0.36062446, 0.10749464, -0.20984372, 0.018256644) * go_0(1.0, -1.0);
result += mat4(-0.005534592, 0.3709197, -0.18287498, 0.1720451, 0.030155553, -0.023265475, 0.0058617783, -0.031765483, 0.037328955, -0.2730994, 0.35090837, -0.3269043, -0.028477207, 0.32756507, -0.15989502, 0.12158258) * go_0(1.0, 0.0);
result += mat4(0.10873739, 0.19583772, 0.060394943, 0.09410379, -0.04739245, 0.026561242, 0.022990001, 0.1093272, -0.01071349, -0.022938967, -0.046423864, 0.2385325, -0.0319821, 0.046962265, 0.09081178, -0.11001857) * go_0(1.0, 1.0);
result += mat4(0.13012704, 0.112289295, 0.030790284, -0.050499484, 0.11784853, 0.08107028, -0.07556717, -0.15643, 0.015249331, 0.015299608, 0.07748125, 0.054485757, 0.044857923, 0.12161275, -0.048292994, -0.033995003) * go_1(-1.0, -1.0);
result += mat4(0.12931514, 0.15114146, 0.070513315, 0.11246343, 0.4142387, 0.213479, -0.5439916, 0.07776645, 0.13109331, 0.2021147, 0.25932786, -0.22157331, 0.02377734, -0.014970623, -0.1943276, 0.18440372) * go_1(-1.0, 0.0);
result += mat4(-0.22365458, -0.19829084, -0.06881161, -0.06468993, 0.17202774, 0.0048758537, -0.09235021, 0.18941896, 0.064125344, -0.09067088, 0.09748182, 0.13561936, -0.05876288, -0.0122420965, -0.054380875, -0.17743628) * go_1(-1.0, 1.0);
result += mat4(0.18582906, -0.09263032, -0.08210888, -0.20515606, 0.11484005, 0.08557595, 0.0009253741, -0.051202174, -0.18535301, -0.1529345, -0.13092944, 0.03770747, -0.020947013, 0.19187425, -0.15494856, -0.048979875) * go_1(0.0, -1.0);
result += mat4(-0.38131633, 0.4278787, 0.19763695, 0.27655518, -0.08711912, 0.07374453, -0.064803004, 0.5983854, 0.2361923, -0.057221692, -0.37138999, -0.24259573, 0.13890724, 0.25706333, -0.54021406, 0.08095518) * go_1(0.0, 0.0);
result += mat4(0.0991328, -0.022651536, -0.029148921, -0.009812537, -0.09523686, -0.15704902, 0.052389514, 0.21561539, 0.1950314, -0.08572602, 0.0016523858, 0.14125621, -0.030999828, 0.12009709, 0.0373512, -0.105043754) * go_1(0.0, 1.0);
result += mat4(-0.11251988, 0.12106985, 0.011923068, 0.3662747, 0.004800994, 0.017972551, 0.004761366, -0.07934206, -0.13755941, -0.022852683, 0.1502225, 0.009758547, -0.16964264, 0.00984782, 0.07855833, 0.035730787) * go_1(1.0, -1.0);
result += mat4(0.01964957, -0.27226487, 0.033933397, -0.117632054, -0.009058229, 0.047830686, -0.01125145, 0.136628, 0.0056388285, 0.3028781, -0.12286517, 0.23498532, -0.009319075, -0.444048, 0.16174883, -0.06367683) * go_1(1.0, 0.0);
result += mat4(0.02343933, -0.010915871, -0.058680378, -0.21886891, -0.010750894, -0.06671997, 0.0602906, -0.07903071, 0.066891186, 0.06650588, 0.14362891, -0.101870626, 0.02264628, -0.06940821, -0.077616625, 0.110911585) * go_1(1.0, 1.0);
result += vec4(0.032014452, -0.020821465, 0.0826416, -0.002838458);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_1_tf
//!SAVE conv2d_2_tf
//!WIDTH conv2d_1_tf.w
//!HEIGHT conv2d_1_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.06963679, -0.07560548, -0.069522075, 0.0038078027, -0.08002613, 0.13671301, 0.084461786, -0.039376218, 0.19136548, -0.123174496, 0.26566333, -0.16583005, -0.18664864, -0.023539122, -0.21928434, -0.026818147) * go_0(-1.0, -1.0);
result += mat4(0.16660932, -0.18558703, 0.37230486, 0.118128106, -0.14098641, 0.14659132, -0.22217897, 0.12952235, -0.4139033, -0.04308319, 0.12885277, -0.17986743, -0.23556231, -0.08351981, -0.43240538, 0.019033253) * go_0(-1.0, 0.0);
result += mat4(-0.18008037, -0.04448665, 0.011906908, -0.023056917, 0.18136618, -0.04723555, -0.0050158803, -0.14823224, -0.2105281, 0.023047728, -0.14040631, -0.03178526, -0.13477588, -0.01820428, 0.058358394, 0.23792502) * go_0(-1.0, 1.0);
result += mat4(0.07363309, -0.061728477, 0.03573137, -0.0050971056, -0.012813505, -0.17236637, 0.1697835, 0.055788577, -0.22263195, 0.10324512, 0.58971673, -0.4872246, -0.1555681, 0.032747746, -0.096495196, 0.070196226) * go_0(0.0, -1.0);
result += mat4(0.14174286, 0.099460006, -0.088765986, 0.58350676, -0.025177564, -0.46004987, 0.37007022, -0.11437029, -0.5164534, -0.60465246, 0.38859612, -0.32846406, 0.050266482, -0.20334712, 0.18316261, -0.19327633) * go_0(0.0, 0.0);
result += mat4(-0.09377763, -0.0012762006, -0.028991895, -0.26523829, 0.20173682, 0.037923716, -0.03174243, 0.07103378, -0.10764164, -0.30752546, 0.20556998, -0.1892279, 0.08115748, -0.023550175, -0.07627362, 0.11746628) * go_0(0.0, 1.0);
result += mat4(-0.06998859, -0.017997518, 0.069938794, -0.14943017, -0.14179112, 0.16643842, -0.110231474, 0.08895815, -0.24074875, 0.3277253, -0.07435203, -0.23452802, 0.039962552, -0.07145652, -0.022511544, -0.04571222) * go_0(1.0, -1.0);
result += mat4(-0.059785757, -0.23771374, -0.030571314, 0.25222278, 0.106601834, 0.34398326, 0.14511436, -0.03867526, -0.38982397, -0.11944689, 0.12997924, -0.13079585, 0.005729482, 0.012653905, -0.063693404, 0.09632285) * go_0(1.0, 0.0);
result += mat4(-0.04933823, 0.0547175, 0.050636575, -0.10060694, 0.1344485, 0.19752938, -0.100068115, -0.028829506, -0.14096203, -0.079092234, 0.092109434, 0.011606209, -0.04052607, -0.008347507, 0.06956573, -0.028109524) * go_0(1.0, 1.0);
result += mat4(0.21918017, -0.11115073, 0.2262453, -0.06889667, -0.11256312, -0.07438075, -0.088454485, 0.13672407, -0.06905764, 0.08128395, 0.016103368, 0.050190717, 0.09691516, 0.05845721, 0.4886816, 0.041121427) * go_1(-1.0, -1.0);
result += mat4(-0.3449472, 0.09711974, -0.13881907, -0.018265123, 0.27855873, -0.07030004, 0.29545054, 0.37216932, 0.08657718, 0.099066615, -0.10574013, -0.17667885, -0.14855732, -0.11351448, 0.66945946, 0.11312157) * go_1(-1.0, 0.0);
result += mat4(0.2526151, -0.04594331, -0.06606611, 0.09104881, 0.06857995, -0.075284235, -0.17664689, 0.21578754, 0.0696524, 0.09142951, 0.080997564, -0.0682772, -0.0011445724, -0.11736295, 0.2519232, -0.101926275) * go_1(-1.0, 1.0);
result += mat4(-0.12913518, 0.058357026, 0.195421, -0.15651494, 0.2877076, 0.0033844314, -0.07831594, 0.052855384, -0.031295884, 0.03301088, -0.18408822, 0.06732994, 0.23742151, -0.12568143, 0.22810535, -0.11545694) * go_1(0.0, -1.0);
result += mat4(-0.49203303, -0.22656603, 0.1723193, -0.51250046, -0.09742038, 0.758559, -0.3387505, -0.6193586, 0.14136684, 0.27679884, -0.050113205, 0.31041816, -0.36475047, -0.48746544, 0.3233227, 0.4579754) * go_1(0.0, 0.0);
result += mat4(0.46636763, 0.1507748, -0.2581362, 0.15413165, -0.17160143, 0.14256273, -0.074575804, -0.099299066, -0.0017214464, 0.13778336, -0.07378213, -0.15489665, -0.10533715, -0.0011083825, 0.39584312, 0.0023906573) * go_1(0.0, 1.0);
result += mat4(0.026959421, -0.06391859, 0.0034752619, 0.14521928, -0.0010877338, -0.032619733, 0.005375293, -0.018952755, 0.03381545, -0.007652831, 0.034141563, 0.046016496, 0.11219674, 0.030913852, 0.077403754, 0.17192438) * go_1(1.0, -1.0);
result += mat4(0.040326044, 0.17290725, -0.1220239, -0.09594783, -0.025229257, 0.17913155, -0.26623353, -0.033396784, -0.03075146, 0.009143897, -0.0136083895, -0.13886899, 0.075683735, -0.11584183, 0.22182357, 0.19350322) * go_1(1.0, 0.0);
result += mat4(0.15726025, -0.10215694, -0.060057458, 0.26487043, -0.04075552, -0.016496127, 0.0015382086, 0.108562306, 0.026795091, 0.0441233, -0.08754318, -0.0460157, 0.048422016, 0.14107347, 0.07986661, 0.1047697) * go_1(1.0, 1.0);
result += vec4(0.0766796, 0.08115133, -0.05703058, 0.14025708);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_2_tf
//!SAVE conv2d_3_tf
//!WIDTH conv2d_2_tf.w
//!HEIGHT conv2d_2_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.18038331, 0.21830973, -0.10019419, -0.022745568, -0.14944611, -0.15669158, 0.46361133, -0.07289843, 0.02976627, -0.09000817, 0.113060996, 0.05635241, 0.012762965, -0.022688959, 0.01629751, 0.061114635) * go_0(-1.0, -1.0);
result += mat4(0.024338024, -0.10004009, -0.13709056, -0.0851965, 0.23927099, -0.024349794, -0.16574804, 0.084686354, -0.047885604, 0.09688507, -0.12733915, 0.06980246, 0.11480734, 0.014669346, -0.07505829, 0.04676309) * go_0(-1.0, 0.0);
result += mat4(0.054203495, 0.011881634, -0.036115017, -0.0686298, -0.13682245, -0.15678032, 0.057050128, -0.03368558, 0.13011025, 0.033391044, -0.09841339, -0.027057761, -0.18701133, 0.20852546, -0.13660902, 0.0005817616) * go_0(-1.0, 1.0);
result += mat4(-0.08077834, 0.35952288, -0.07647382, -0.0033230998, 0.13929126, -0.09155619, 0.14128102, 0.16005981, 0.18161216, -0.09485738, 0.0029118075, 0.052682754, 0.03242074, 0.08299826, 0.073796146, -0.06446532) * go_0(0.0, -1.0);
result += mat4(-0.36655015, 0.4606936, 0.19073649, 0.31655258, -0.006838053, -0.579939, 0.089126326, -0.14021218, -0.3437716, 0.16714323, 0.17705944, -0.22418492, -0.3883696, -0.2302651, 0.2581861, 0.21983066) * go_0(0.0, 0.0);
result += mat4(0.0992383, -0.014257871, -0.023896435, 0.19868234, 0.0408007, 0.07995299, 0.16102871, -0.11668251, 0.22458278, -0.05587917, 0.19373615, -0.016202094, -0.25106144, 0.15634494, 0.11624891, -0.2930768) * go_0(0.0, 1.0);
result += mat4(0.024616942, 0.36248252, -0.14779098, -0.019894283, -0.007111256, 0.010641561, -0.09541178, 0.21236233, 0.009501827, 0.08132797, -0.13983901, 0.027207611, 0.038444366, -0.013995817, -0.16242191, 0.03294123) * go_0(1.0, -1.0);
result += mat4(0.0131698875, -0.18124102, -0.13503514, -0.06099072, 0.07422735, -0.20906176, -0.049005672, 0.08739405, -0.031758767, -0.1978915, 0.23094437, 0.54512614, 0.21338555, -0.011205669, -0.23727885, -0.29533875) * go_0(1.0, 0.0);
result += mat4(-0.0010255767, -0.07168225, -0.033568826, 0.22161655, -0.087293416, 0.11350447, 0.13653576, 0.061226424, -0.13074352, 0.058425818, 0.038460605, 0.2749964, -0.012814839, 0.085885845, -0.038151987, -0.17960808) * go_0(1.0, 1.0);
result += mat4(0.19728905, -0.040724937, -0.18270236, 0.046735186, 0.03507326, 0.119867206, -0.12691991, 0.18119748, -0.052895024, 0.11348764, -0.043787055, 0.004703516, 0.006752757, -0.06939761, -0.009801806, -0.075640485) * go_1(-1.0, -1.0);
result += mat4(0.051735226, 0.1732299, -0.10672899, 0.0320877, -0.4913656, 0.2102274, 0.43920282, 0.059108034, 0.08349019, -0.16517872, 0.15436842, -0.1075667, 0.022741623, -0.26693836, 0.3645307, 0.017874828) * go_1(-1.0, 0.0);
result += mat4(0.034464058, 0.014929155, 0.054227423, 0.14167373, -0.0023630706, -0.08904212, 0.11918041, -0.034539603, 0.06048089, -0.06807333, 0.14447778, 0.035260547, 0.09979546, -0.1924939, 0.14596114, -0.12069667) * go_1(-1.0, 1.0);
result += mat4(-0.04427228, -0.23673469, 0.010357103, -0.2907043, -0.06845721, -0.078984015, 0.06867713, -0.058163825, -0.12154615, 0.08430951, 0.1922373, 0.030108064, -0.43081748, -0.38715646, -0.022240646, -0.15403675) * go_1(0.0, -1.0);
result += mat4(0.46885306, -0.33421394, -0.6695223, -0.41841158, 0.30317923, 0.24244753, -0.1047785, -0.18656285, 0.06261881, -0.4405616, 0.24233986, 0.40070608, 0.81440526, 0.11305212, -0.8826317, -0.023478031) * go_1(0.0, 0.0);
result += mat4(-0.07879348, -0.024378026, -0.041883785, -0.17030984, 0.23229122, -0.011237109, 0.12058088, 0.20766267, -0.36519575, 0.09599417, -0.1271098, 0.06990154, 0.21161246, 0.041002538, -0.36046275, 0.007304667) * go_1(0.0, 1.0);
result += mat4(0.10873893, 0.003872542, -0.13476561, -0.036068805, -0.054637462, 0.02304618, 0.04707738, -0.2856381, 0.07124422, 0.010866545, 0.20484549, -0.008342406, -0.43660247, -0.041055538, 0.33536008, -0.060022205) * go_1(1.0, -1.0);
result += mat4(0.1966458, 0.0016302796, -0.25712642, -0.09639119, -0.006955351, 0.10882133, 0.1107341, 0.062697805, -0.1074494, 0.17361663, 0.6429869, -0.39846307, -0.26302996, 0.048710946, 0.40387508, 0.4299715) * go_1(1.0, 0.0);
result += mat4(0.18948616, 0.24086732, -0.064474985, -0.11069709, 0.1279659, -0.13438123, -0.028438117, 0.125883, 0.018153818, -0.21942288, 0.020390838, -0.22797634, -0.10821287, -0.17175092, 0.122016855, 0.20699544) * go_1(1.0, 1.0);
result += vec4(-0.05101961, -0.060740646, -0.024465766, 0.058471628);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_3_tf
//!SAVE conv2d_4_tf
//!WIDTH conv2d_3_tf.w
//!HEIGHT conv2d_3_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.14533128, 0.07266841, 0.13238011, -0.23328504, 0.031516243, 0.058471266, -0.06394412, 0.090752736, -0.0042359144, 0.12357294, -0.04377495, 0.0011743477, 0.05412243, -0.08146249, 0.04002749, -0.032876283) * go_0(-1.0, -1.0);
result += mat4(-0.036972385, -0.15238069, -0.3453321, -0.36025128, 0.07597202, -0.02368151, -0.3889606, 0.34607083, 0.3133179, -0.21712309, -0.4210954, 0.21450534, 0.15226828, 0.25326282, 0.45327064, -0.3350824) * go_0(-1.0, 0.0);
result += mat4(0.019018406, -0.33060563, -0.092601225, 0.14970545, 0.1441509, -0.19228427, -0.032771986, 0.26331595, 0.052981265, -0.06627376, -0.08634131, 0.038706224, 0.13403937, -4.4842476e-05, 0.049002815, -0.12719193) * go_0(-1.0, 1.0);
result += mat4(0.17527401, -0.0035254909, -0.047959115, -0.4526988, -0.07510284, 0.0013256798, -0.07539148, 0.24220634, -0.08708839, -0.14494033, -0.17085724, -0.099797316, 0.0068515535, -0.08918779, 0.27164719, -0.1702649) * go_0(0.0, -1.0);
result += mat4(0.31848368, 0.48983255, -0.44140294, -0.65174145, -0.004199057, 0.19494705, 0.5196497, -0.027118586, 0.032509074, -0.23900363, -0.14489244, 0.36314297, -0.23168536, -0.20960593, 0.61471456, 0.12401275) * go_0(0.0, 0.0);
result += mat4(-0.24317405, 0.21560913, 0.15564032, 0.11606844, -0.15039803, -0.59578896, 0.14100945, -0.026194477, 0.37237462, -0.49472088, -0.15215331, -0.38820064, -0.25089455, -0.29643852, -0.09513793, 0.019779462) * go_0(0.0, 1.0);
result += mat4(0.12498539, 0.0710632, -0.25012368, -0.2272255, -0.08647026, 0.12277892, 0.011025097, -0.12168395, -0.13489573, 0.016708186, -0.15583871, -0.057124946, 0.1216943, 0.019803725, 0.06952334, -0.032985855) * go_0(1.0, -1.0);
result += mat4(0.28794885, 0.33783793, -0.14469545, -0.081780486, -0.50320613, -0.067601606, -0.06847453, -0.021648854, -0.34295765, 0.15071863, -0.06619896, -0.084465064, 0.31909832, 0.015414661, 0.14930317, -0.11295768) * go_0(1.0, 0.0);
result += mat4(0.24530606, 0.25526014, 0.09971985, -0.07749641, -0.2361951, -0.07997673, 0.03617294, 0.02959561, -0.4498983, -0.014073485, -0.20587012, 0.06396779, 0.1262825, 0.027433183, 0.14469334, 0.011538011) * go_0(1.0, 1.0);
result += mat4(-0.038572453, -0.023108613, -0.039481267, -0.012160024, -0.004521989, -0.028665857, 0.04295255, 0.10580258, 0.05439479, -0.072261885, 0.11030243, 0.08934696, 0.09133867, 0.017547369, 0.097613186, 0.05491059) * go_1(-1.0, -1.0);
result += mat4(-0.09972817, 0.057730395, 0.12665828, 0.32861367, -0.16186063, 0.0745509, 0.2394045, -0.08687853, -0.034404907, -0.05843572, 0.0684561, -0.1355754, 0.19248672, -0.60372186, 0.12583947, 0.4388962) * go_1(-1.0, 0.0);
result += mat4(0.10341107, 0.061113223, 0.08773817, -0.082504354, -0.16612078, 0.2681751, 0.019737698, -0.17122322, -0.135949, 0.3048101, 0.087803006, 0.11373851, 0.013192192, -0.27022064, 0.35529897, -0.15321451) * go_1(-1.0, 1.0);
result += mat4(-0.032835662, 0.11123062, -0.11322452, -0.17300649, 0.04680824, 0.12849288, 0.17269878, -0.048671383, 0.05189037, -0.009078046, 0.22105052, 0.013008137, -0.009738674, 0.15391739, 0.20969556, 0.14189166) * go_1(0.0, -1.0);
result += mat4(-0.47377753, 0.3038031, 0.18604809, 0.1931698, -0.2964668, -0.12287907, -0.7107761, 0.26619422, -0.33923018, 0.19200724, 0.013786281, -0.17496964, 0.079325035, -0.3694445, 0.0054486147, -0.33018264) * go_1(0.0, 0.0);
result += mat4(0.14903802, -0.028043179, 1.5238678e-05, 0.021232028, 0.16025065, 0.14746875, -0.22831628, -0.12177345, 0.038778774, 0.32188168, -0.042017702, 0.27155936, 0.17920609, 0.04099755, 0.28527525, 0.074623376) * go_1(0.0, 1.0);
result += mat4(0.057019282, -0.112741895, 0.030361209, 0.14567861, 0.056265317, -0.01573537, -0.06707608, 0.016657263, 0.09829025, -0.026795063, 0.023042196, 0.09438241, -0.025483066, -0.052787006, 0.19730279, 0.021218104) * go_1(1.0, -1.0);
result += mat4(0.19868211, -0.01531125, 0.108596824, -0.035456363, 0.0033609823, 0.057961613, -0.013726211, 0.101742364, 0.33357215, 0.14468077, 0.29711527, -0.24662566, -0.119014986, -0.1899639, 0.11246697, -0.0035374009) * go_1(1.0, 0.0);
result += mat4(-0.05602109, -0.15539522, 0.010730943, 0.057116497, -0.02037749, 0.084210664, -0.028235348, 0.10574697, 0.056925274, 0.07922333, -0.090088, 0.1615985, -0.0044301567, -0.089945644, 0.024176618, -0.041844133) * go_1(1.0, 1.0);
result += vec4(0.0015292584, -0.043625206, -0.09429898, -0.06280405);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_4_tf
//!SAVE conv2d_5_tf
//!WIDTH conv2d_4_tf.w
//!HEIGHT conv2d_4_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.06051604, -0.028152643, -0.21418124, 0.13032125, 0.42565975, -0.09571944, -0.34494513, 0.30004, -0.073245734, -0.028659137, 0.0032105136, -0.05009555, -0.048971225, 0.04814533, 0.002843805, -0.046224426) * go_0(-1.0, -1.0);
result += mat4(-0.07495975, 0.018714864, 0.21229684, -0.13614887, 0.79988647, -0.0697328, 0.38232988, 0.24165109, 0.25947478, -0.0009418982, -0.17369923, 0.10007766, 0.024117598, 0.028611807, 0.15090801, -0.06344829) * go_0(-1.0, 0.0);
result += mat4(-0.07982219, 0.0900347, 0.007609254, -0.0034791247, 0.013611781, -0.13560618, 0.09685799, 0.06276075, 0.134693, -0.14370437, -0.25175703, -0.0016138123, -0.0075672898, -0.13325731, -0.061100446, 0.0059743375) * go_0(-1.0, 1.0);
result += mat4(-0.039018434, -0.19668463, -0.43018532, 0.31886247, 0.4965479, 0.114569925, 0.19110382, 0.27343535, 0.0707728, -0.11877004, -0.25827697, 0.37012872, 0.1474777, 0.07056952, -0.14965728, 0.061595406) * go_0(0.0, -1.0);
result += mat4(0.506543, -0.16268773, 0.455319, -0.0702646, 0.70102173, -0.14041683, 0.70184857, 0.4817842, -0.3389246, -0.14463086, 0.13763213, -1.1259074, 0.47722015, 0.38352612, -0.04293366, -0.5604627) * go_0(0.0, 0.0);
result += mat4(0.17606944, 0.15897374, 0.13499324, 0.29241478, -0.032824475, 0.11128662, -0.22204424, -0.051803727, 0.013195331, -0.42040786, -0.3950585, 0.70745844, 0.38646924, -0.19080774, -0.15171832, -0.10742828) * go_0(0.0, 1.0);
result += mat4(-0.039278325, 0.18421806, -0.044948544, 0.07902063, -0.2149251, 0.09913459, -0.09743655, -0.26899317, -0.002695496, -0.07554527, -0.22373366, 0.17830558, -0.047994815, -0.06789183, -0.06755918, -0.104452066) * go_0(1.0, -1.0);
result += mat4(-0.0493473, -0.30411786, -0.056439694, -0.06582185, -0.21309847, 0.100670904, -0.22966193, -0.045954112, 0.12728062, -0.25081897, -0.094699375, -0.4036555, 0.060854495, -0.64373237, -0.21522263, -0.6683476) * go_0(1.0, 0.0);
result += mat4(0.063481025, 0.11744312, -0.043330096, 0.33817932, -0.06679828, -0.23207302, -0.10188898, -0.10590511, 0.058780864, 0.047292337, -0.11834696, 0.10076128, -0.036641665, 0.30200714, -0.0002892557, -0.10303763) * go_0(1.0, 1.0);
result += mat4(-0.10842604, 0.042055763, 0.29702973, -0.07409644, -0.030164458, -0.012098744, -0.06396587, -0.08787527, 0.051854923, 0.12997511, 0.11468497, 0.15022379, 0.007814715, 0.014517445, 0.025484756, 0.01078619) * go_1(-1.0, -1.0);
result += mat4(-0.29229385, 0.040265664, -0.15376821, 0.075579196, -0.05593569, -0.045405343, 0.12099204, 0.1571252, 0.17841713, 0.04673325, 0.14550509, 0.08603346, -0.049786013, 0.06121843, -0.16273825, -0.13857752) * go_1(-1.0, 0.0);
result += mat4(0.06903744, 0.2628764, -0.13582836, -0.35678583, -0.13821034, -0.019381443, -0.19570538, -0.09298511, 0.08965436, 0.09745909, 0.20055099, 0.024967568, 0.08144204, 0.004633625, 0.12809834, -0.009431525) * go_1(-1.0, 1.0);
result += mat4(0.09784006, 0.010729353, 0.046643205, -0.110926524, -0.21556224, 0.00016300633, 0.122175336, 0.15004392, 0.013864355, 0.24767809, 0.13865592, 0.0155424485, -0.1450483, -0.15688781, -0.06195043, -0.13745981) * go_1(0.0, -1.0);
result += mat4(0.018991318, 0.55401963, 0.11709872, -0.028442185, -0.46035343, -0.10215539, -0.60193926, 0.47882316, -0.23346989, 0.037200127, 0.22814943, -0.08231696, -0.36430013, -0.011152757, 0.48752213, 0.29796222) * go_1(0.0, 0.0);
result += mat4(-0.07258066, -0.023222538, 0.23230423, -0.30317304, 0.03942911, -0.06899803, 0.23778579, 0.07418621, -0.17443737, 0.33387753, 0.007354842, -0.123447575, -0.1745315, 0.11071779, -0.11949625, -0.22832453) * go_1(0.0, 1.0);
result += mat4(-0.024909232, -0.0308135, 0.12170621, -0.13298757, 0.045828197, -0.1532345, -0.06633672, 0.23591088, 0.04964077, 0.14091493, 0.038343724, -0.029780807, 0.05762822, -0.048930667, -0.02434709, 0.07109019) * go_1(1.0, -1.0);
result += mat4(-0.16039175, 0.3004474, -0.17278233, 0.13677922, 0.18838613, 0.15054552, 0.32901475, -0.1288333, 0.26378244, -0.05119892, 0.34533516, 0.25180495, 0.19452183, 0.0843233, -0.08029368, 0.39877903) * go_1(1.0, 0.0);
result += mat4(-0.07097129, -0.26492423, -0.055032317, -0.093516104, -0.11795062, 0.04086253, -0.07989471, 0.059686553, 0.09378249, 0.45851848, 0.2510942, 0.19599153, 0.019765077, -0.02920918, -0.04125142, -0.13859107) * go_1(1.0, 1.0);
result += vec4(0.04400571, -0.04015565, 0.0140529545, 0.05474095);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_5_tf
//!SAVE conv2d_6_tf
//!WIDTH conv2d_5_tf.w
//!HEIGHT conv2d_5_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.014236042, -0.0031431736, -0.1551387, 0.12515116, -0.28528872, 0.36161992, 0.15750743, -0.17111474, 0.13792591, -0.0657419, -0.17471549, 0.14650472, 0.034169197, -0.019157575, 0.23520657, -0.20358163) * go_0(-1.0, -1.0);
result += mat4(0.02015035, 0.12993371, 0.11199667, -0.09854378, 0.5001741, 0.03462961, 0.24919736, 0.08505297, -0.20902094, -0.24141377, -0.15360375, 0.049974803, -0.037157424, -0.048510186, 0.20106035, -0.118480384) * go_0(-1.0, 0.0);
result += mat4(0.086798504, -0.009607818, 0.034812123, -0.005187592, 0.0351509, 0.021755, -0.04996161, -0.041231696, 0.0020545553, 0.015730752, -0.07507172, 0.018597523, -0.02393343, 0.07624775, 0.03892451, -0.0025574185) * go_0(-1.0, 1.0);
result += mat4(0.035725456, 0.06809103, 0.51926994, -0.39983147, -0.16402833, -0.1243394, -0.25922915, 0.28285915, 0.15959994, -0.2351732, 0.2650535, -0.30193794, -0.11468332, 0.050777763, -0.51894253, 0.4408367) * go_0(0.0, -1.0);
result += mat4(-0.27042082, 0.22243942, 0.14902467, 0.38428563, 0.46612173, 0.5169912, -0.22330502, -0.11300288, -0.36141354, 0.0668681, 0.2984152, 0.1275798, -0.24121419, 0.2952039, -0.45109174, -0.3822957) * go_0(0.0, 0.0);
result += mat4(0.26543504, -0.05742226, -0.052103903, -0.013124308, -0.14358385, -0.04024543, 0.07665455, -0.012301872, -0.18752757, -0.03913891, 0.038205814, -0.006583095, -0.25550908, -0.25725332, -0.12454206, -0.0058936924) * go_0(0.0, 1.0);
result += mat4(-0.0018946569, 0.019746022, -0.13080788, 0.11450627, -0.013743845, -0.027179785, -0.14425103, 0.07109661, 0.023703793, 0.086905524, 0.03151253, 0.0132474145, 0.041018624, 0.04548913, 0.2718715, -0.20008296) * go_0(1.0, -1.0);
result += mat4(-0.076830454, 0.11652955, 0.5068201, -0.3082819, 0.058615055, -0.006765798, -0.057522714, 0.049981344, -0.006897243, -0.21763432, 0.16896053, -0.21176189, -0.061227098, 0.03566485, 0.08901554, -0.050980624) * go_0(1.0, 0.0);
result += mat4(0.02327798, 0.07662976, 0.034811985, -0.03238033, -0.0021881019, -0.030997375, -0.069672935, 0.04040273, -0.1217442, 0.104173124, 0.09862539, 0.020557549, -0.022286594, 0.10287763, -0.021694934, 0.07542515) * go_0(1.0, 1.0);
result += mat4(0.124069154, -0.08579466, -0.07816314, 0.11332851, -0.034682628, -0.11038275, 0.04750615, -0.096100725, 0.039588403, -0.15149672, -0.05529172, 0.034304325, -0.022520235, -0.05023852, -0.2674731, 0.21886522) * go_1(-1.0, -1.0);
result += mat4(-0.1948599, -0.14946899, -0.39548838, 0.18042913, -0.007919619, 0.19826505, 0.23789087, 0.009140256, 0.11857748, 0.18215668, 0.13606293, -0.09209675, -0.080678545, -0.020431137, -0.07728839, -0.051353537) * go_1(-1.0, 0.0);
result += mat4(-0.07616472, -0.0032800382, -0.045657665, -0.039144326, -0.37786487, -0.08877774, 0.053579114, -0.070886396, 0.011311804, 0.107276045, 0.013236154, 0.009832061, 0.08292063, 0.12258811, 0.0005569043, -0.009806432) * go_1(-1.0, 1.0);
result += mat4(-0.28062925, 0.15946878, -0.1021801, -0.06471589, -0.26999477, 0.21230288, -0.14243907, 0.2555922, -0.09608517, 0.26339412, 0.20891234, -0.23538485, 0.33958244, -0.12569186, 0.43289876, -0.33462036) * go_1(0.0, -1.0);
result += mat4(0.16265294, 0.2625464, -0.34452894, 0.2233622, 0.13850005, -0.42999864, -0.5385177, -0.11035979, 0.51662, -0.78238726, -0.09422375, 0.83759475, 0.44468537, 0.14301361, 0.108906105, 1.1596143) * go_1(0.0, 0.0);
result += mat4(-0.73757625, -0.12369605, 0.23523071, 0.006587637, -0.15445381, 0.22757277, 0.052819528, 0.10183905, -0.07912228, -0.16998893, -0.13360223, 0.014348178, -0.17778571, -0.41047302, 0.10241381, -0.08526306) * go_1(0.0, 1.0);
result += mat4(0.14712952, 0.048995696, 0.05299946, -0.06817572, 0.1498064, -0.079825334, 0.40354064, -0.31789717, -0.1998377, 0.00955295, -0.32318407, 0.30898204, -0.039571725, -0.026203401, -0.16292085, 0.08574385) * go_1(1.0, -1.0);
result += mat4(-0.6353329, -0.56000775, -0.17279743, 0.18198174, -0.19555812, 0.056538377, 0.34365895, -0.07799055, 0.19011354, -0.13952748, 0.029196098, -0.19596763, -0.069196045, -0.17402656, 0.07948411, -0.016226962) * go_1(1.0, 0.0);
result += mat4(0.25592864, 0.083498634, -0.28515807, 0.10789751, 0.0043962947, 0.07085363, 0.048724182, -0.025131436, -0.0049440865, -0.033094388, -0.032935806, 0.04266025, 0.20026933, 0.0927841, -0.006839351, -0.013012285) * go_1(1.0, 1.0);
result += vec4(0.02021373, 0.0014037411, 0.0012718709, 0.017278494);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(M)-Conv-4x1x1x56
//!HOOK MAIN
//!BIND conv2d_tf
//!BIND conv2d_1_tf
//!BIND conv2d_2_tf
//!BIND conv2d_3_tf
//!BIND conv2d_4_tf
//!BIND conv2d_5_tf
//!BIND conv2d_6_tf
//!SAVE conv2d_last_tf
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define g_0 (max((conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_1 (max(-(conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_2 (max((conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_3 (max(-(conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_4 (max((conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_5 (max(-(conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_6 (max((conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_7 (max(-(conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_8 (max((conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_9 (max(-(conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_10 (max((conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_11 (max(-(conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_12 (max((conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_13 (max(-(conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
vec4 hook() {
vec4 result = mat4(-0.0067711817, 0.08160003, 0.0247279, 0.03084815, -0.026977416, -0.02120602, -0.025078611, -0.029852165, -0.011627478, -0.012742972, 0.022736797, -0.0028815821, -0.007515677, 0.0172887, -0.023259213, 0.009608947) * g_0;
result += mat4(-0.028660107, -0.014015208, -0.027838672, -0.013171922, 0.0029435428, 0.027047642, -0.017478354, 0.022834882, -0.037572853, -0.0034044068, -0.0149029335, -0.013362301, 0.009827443, -0.015742151, -0.0074795415, -0.0022266617) * g_1;
result += mat4(-0.07579662, -0.039754186, -0.066026606, -0.046816852, 0.1099032, 0.043956704, 0.073109835, 0.04680284, -0.06896613, -0.008838632, -0.044584926, -0.01319039, -0.0021152915, -0.04503326, 0.027061926, -0.028334105) * g_2;
result += mat4(0.15458213, 0.059769996, 0.09327123, -0.028782733, 0.023459995, -0.15390377, -0.13432898, -0.1127775, 0.072764635, -0.0020463336, 0.034736466, -0.0012086042, -0.05847183, -0.029952323, 0.052969377, 0.09590908) * g_3;
result += mat4(-0.07476772, -0.016574614, 0.04131183, 0.017335678, 0.009654406, 0.072183535, -0.002266456, 0.086873695, 9.310129e-05, 0.0056416965, -0.004188391, 0.023132093, -0.05183336, -0.025825873, -0.03684392, -0.0075729224) * g_4;
result += mat4(0.00878842, 0.03869637, -0.035759524, 0.003345386, -0.064184256, -0.034568302, -0.06672922, -0.0686381, -0.06794392, -0.10685906, 0.04679947, -0.012535639, 0.006932529, -0.007783515, 0.109123886, 0.13804391) * g_5;
result += mat4(-0.03160699, 0.050473, -0.09030729, 0.0649397, 0.11466501, 0.17912874, -0.0081851315, 0.052244574, 0.051632743, 0.061941486, 0.06546816, 0.12174249, -0.05104755, -0.018193979, -0.032196652, -0.035292786) * g_6;
result += mat4(0.013612735, -0.0024100312, -0.068611205, -0.07369285, -0.019647537, -0.066944756, -0.010012875, -0.06785739, -0.062246565, -0.087313406, -0.044278186, -0.09368995, 0.052555013, 0.13604961, 0.05645059, 0.08763303) * g_7;
result += mat4(0.04218486, -0.05028401, 0.059086576, -0.03545452, 0.027737848, 0.0043074046, 0.0011001764, -0.073026665, -0.04094988, 0.044061556, -0.009812515, 0.06841999, -0.06612581, 0.037223976, -0.07759491, -0.04356598) * g_8;
result += mat4(-0.027558247, 0.014248466, -0.019813016, -0.058107473, -0.016717663, -0.020424338, 0.0053625097, -0.009917319, 0.013678771, 0.0113340765, 0.0061787106, -0.036083996, -0.020179711, -0.011310535, 0.054827053, -0.0008278952) * g_9;
result += mat4(0.028690035, -0.012079616, 0.11931408, -0.048533775, 0.069336995, 0.0049852817, 0.013774468, 0.035233382, -0.07384821, 0.0003354423, -0.0059171803, -0.04503906, 0.08727279, 0.005138857, -0.17724465, 0.055782065) * g_10;
result += mat4(-0.20744391, 0.24348328, -0.3145766, 0.17026486, -0.022870807, -0.01648648, -0.05912279, -0.012555373, -0.066004686, 0.03182394, 0.16285324, -0.1221846, -0.31816196, 0.007928748, 0.43180224, -0.015949022) * g_11;
result += mat4(0.16363169, 0.14781676, -0.2377973, -0.1571377, -0.09038187, 0.0046504294, 0.033955004, -0.051421452, 0.046735536, 0.006827522, -0.121338, 0.12671822, 0.15833299, -0.1858712, -0.1942371, 0.17336044) * g_12;
result += mat4(-0.018145572, -0.015550516, 0.044410378, 0.046016492, 0.084021375, 0.05327457, -0.008270992, -0.045435544, 0.07185879, -0.131923, 0.26721445, -0.26745328, -0.07093472, 0.042701527, 0.13793674, -0.095621444) * g_13;
result += vec4(0.016836504, 0.010161949, 0.021351453, 0.01278978);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(M)-Depth-to-Space
//!HOOK MAIN
//!BIND MAIN
//!BIND conv2d_last_tf
//!SAVE MAIN
//!WIDTH conv2d_last_tf.w 2 *
//!HEIGHT conv2d_last_tf.h 2 *
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
vec4 hook() {
vec2 f0 = fract(conv2d_last_tf_pos * conv2d_last_tf_size);
ivec2 i0 = ivec2(f0 * vec2(2.0));
float c0 = conv2d_last_tf_tex((vec2(0.5) - f0) * conv2d_last_tf_pt + conv2d_last_tf_pos)[i0.y * 2 + i0.x];
float c1 = c0;
float c2 = c1;
float c3 = c2;
return vec4(c0, c1, c2, c3) + MAIN_tex(MAIN_pos);
}
@@ -0,0 +1,158 @@
// MIT License
// Copyright (c) 2019-2021 bloc97
// All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(S)-Conv-4x3x3x3
//!HOOK MAIN
//!BIND MAIN
//!SAVE conv2d_tf
//!WIDTH MAIN.w
//!HEIGHT MAIN.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (MAIN_texOff(vec2(x_off, y_off)))
vec4 hook() {
vec4 result = mat4(-0.0057322932, 0.12928207, -0.056848746, 0.18680117, -0.0306273, 0.25602463, 0.053723164, 0.20419341, 0.0018709862, 0.022848232, -0.04105527, 0.10169034, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, -1.0);
result += mat4(0.009471417, -0.12957802, 0.096014425, 0.21836184, 0.00021601951, -0.22997683, 0.23666254, 0.41192335, 0.021762101, 0.0047863554, 0.008233427, 0.108514786, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 0.0);
result += mat4(-0.01156376, -0.18988979, 0.04614705, -0.044767227, 0.01050636, -0.26426336, 0.23741047, 0.0027636609, -0.027718676, -0.14202335, -0.016650287, -0.06637125, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 1.0);
result += mat4(0.057809234, -0.11033858, 0.056533534, -0.06292466, 0.13880666, -0.18710336, 0.2441031, -0.25326246, 0.0032683122, -0.026437074, 0.0023248852, 7.640766e-05, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, -1.0);
result += mat4(-0.49110603, 0.4429004, -0.44015464, -0.41174838, -0.87738293, 0.7808468, -1.0929365, -0.59699076, -0.18409836, 0.185138, -0.11773224, -0.17097276, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 0.0);
result += mat4(0.10580959, -0.055947904, -0.03431237, -0.080236495, 0.14862584, -0.15393938, -0.18872876, -0.3170681, 0.03559387, -0.003990826, 0.021298569, 0.012844483, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 1.0);
result += mat4(-0.040715586, -0.25781113, 0.08896714, -0.1225879, -0.15790503, -0.54010904, 0.29588607, 0.10401059, 0.003413123, -0.108357325, 0.0112870345, -0.11888622, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, -1.0);
result += mat4(0.0049315444, 0.02376202, -0.08224771, 0.121118225, -0.041512914, -0.027994309, -0.585988, -0.069672115, -0.017247835, 0.0056576864, 0.04319012, 0.055003505, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 0.0);
result += mat4(0.37521392, 0.15916082, 0.059708964, 0.19046007, 0.8120325, 0.38343868, 0.3436578, 0.5287958, 0.16570656, 0.06957687, 0.014022592, 0.074799836, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 1.0);
result += vec4(-0.01050964, -0.00939481, 0.17684458, 0.027366742);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(S)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_tf
//!SAVE conv2d_1_tf
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.011029496, 0.05866063, -0.09460646, -0.017664742, -0.022488879, 0.18384217, -0.00397663, -0.064733066, 0.08466802, 0.10667488, 8.0212536e-05, 0.0908869, 0.13580276, 0.00097438256, 0.12176522, -0.08218466) * go_0(-1.0, -1.0);
result += mat4(0.16062798, -0.10190268, 0.03280682, 0.05621916, -0.009684231, -0.08464307, 0.17058301, -0.096469186, 0.1967505, -0.1450099, 0.093607284, -0.28240147, -0.21377413, 0.10079291, -0.1741522, 0.17330575) * go_0(-1.0, 0.0);
result += mat4(-0.060160473, 0.06316997, 0.0046929033, -0.049405966, 0.13851729, 0.06830702, -0.0586872, -0.040827133, 0.007052838, -0.03576886, -0.111261636, 0.039155316, -0.07380389, -0.09369825, 0.04471156, 0.09678487) * go_0(-1.0, 1.0);
result += mat4(-0.36683616, -0.035950605, -0.24414362, -0.009159744, 0.19335322, -0.099253505, 0.075083904, -0.00076695543, 0.65291303, -0.25599423, 0.19827642, 0.065899536, -0.07423247, -0.068967685, 0.0050554527, -0.060272824) * go_0(0.0, -1.0);
result += mat4(-0.020688485, -0.83178276, 0.11104878, 0.26454413, 0.13655476, 0.37675047, -0.22219229, -0.01751935, 0.44552696, 0.92510307, 0.16063261, -0.62011045, 0.19366647, -0.06996067, -0.2504841, 0.00803723) * go_0(0.0, 0.0);
result += mat4(0.0051537007, -0.057168536, -0.16110587, 0.25232598, -0.04447099, 0.11997351, 0.14808103, -0.34443566, -0.26212573, -0.21970181, 0.2724405, 0.21050811, -0.07949061, -0.064808235, -0.21208277, -0.0042361654) * go_0(0.0, 1.0);
result += mat4(-0.0888952, -0.20169449, 0.19144905, -0.016882861, -0.013283103, 0.07552998, -0.24686803, 0.012453213, -0.065454446, -0.016123284, -0.47316182, 0.070926026, 0.09219782, 0.13118166, 0.074736096, 0.0077910526) * go_0(1.0, -1.0);
result += mat4(0.5832154, 0.1138069, -0.039765622, 0.3182784, -0.25497997, 0.0013993139, 0.39285088, -0.48511526, -0.39891505, -0.19094779, -0.082146175, -0.20826934, 0.020590555, -0.0012490178, -0.4398621, 0.14377014) * go_0(1.0, 0.0);
result += mat4(0.21917395, 3.4314657e-05, 0.25734863, -0.3433305, 0.015720673, 0.2676127, -0.06807297, 0.15040149, -0.23638041, -0.0050233034, -0.13666134, 0.4542111, -0.033572577, -0.08450588, -0.23341487, 0.053490847) * go_0(1.0, 1.0);
result += mat4(-0.17482175, 0.057647135, 0.33135444, 0.0850751, -0.1718849, -0.0854123, 0.036795795, -0.13874969, -0.10903869, -0.19007301, -0.06064334, -0.03786032, -0.036696054, 0.07844446, 0.012523185, -0.01562906) * go_1(-1.0, -1.0);
result += mat4(-0.04411997, -0.10331819, 0.10050193, 0.12406485, 0.07431592, 0.30109692, -0.17511666, -0.13263564, -0.10192587, 0.07821255, -0.22415096, 0.25552443, 0.17881326, -0.13914281, 0.109979235, -0.0016463579) * go_1(-1.0, 0.0);
result += mat4(-0.01911644, -0.15412527, 0.028903123, 0.20831817, 0.00375175, 0.08110953, 0.074919395, -0.17581624, -0.015677985, 0.06504228, 0.08817818, -0.12518327, -0.09537373, 0.028905088, -0.051288474, 0.054334078) * go_1(-1.0, 1.0);
result += mat4(0.2852779, -0.28924024, 0.36805123, 0.21079305, -0.28336474, 0.1679663, -0.08641141, -0.10699407, -0.16090055, 0.1287612, -0.15910125, 0.05734755, 0.15883245, 0.0053026294, 0.080674745, 0.0505137) * go_1(0.0, -1.0);
result += mat4(0.17639062, 0.3790122, -0.19588692, -0.020314282, 0.26197383, 0.09014768, 0.19696823, -0.41025418, -0.08308115, -0.33279485, -0.22528782, 0.06172439, -0.1365661, -0.13094363, -0.005086559, 0.089024484) * go_1(0.0, 0.0);
result += mat4(0.05262993, 0.0006296959, 0.1657725, -0.32591924, 0.12126701, 0.061543245, -0.10526848, 0.041583937, 0.094976954, 0.09416157, -0.22019257, -0.058390073, -0.2073888, 0.057273377, 0.19558284, 0.004208022) * go_1(0.0, 1.0);
result += mat4(0.30005738, 0.18478931, -0.23342943, 0.22455733, -0.016488122, 0.099634305, 0.31620836, -0.15731157, 0.09595808, 0.0013774688, 0.48273298, -0.07027936, -0.18764344, -0.26194447, -0.11794225, -0.012173601) * go_1(1.0, -1.0);
result += mat4(0.117986746, -0.13846518, -0.019614812, -0.3011192, 0.5501164, 0.3408611, -0.40090847, 0.15706886, 0.13050972, 0.051776595, 0.20792943, 0.23389706, -0.22965533, -0.053367328, 0.3911586, -0.032988597) * go_1(1.0, 0.0);
result += mat4(0.054753624, -0.008485731, -0.2451672, 0.17528129, 0.13657846, 0.010480436, 0.07651423, -0.43316832, 0.12736236, 0.13804524, 0.12529011, -0.30946237, -0.14423579, 0.08403089, 0.24335162, 0.057288036) * go_1(1.0, 1.0);
result += vec4(0.012077211, 0.013045883, 0.0380778, -0.02908858);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(S)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_1_tf
//!SAVE conv2d_2_tf
//!WIDTH conv2d_1_tf.w
//!HEIGHT conv2d_1_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.036115196, -0.06971895, -0.07508942, 0.016036168, 0.12120111, 0.24536026, 0.044755507, -0.20663576, 0.029635755, -0.15427187, 0.027148994, -0.20795093, 0.10170582, 0.077919215, 0.66063017, -0.4632968) * go_0(-1.0, -1.0);
result += mat4(-0.0052889925, -0.019060908, -0.08660142, -0.022095207, -0.08097976, -0.015142803, -0.18552722, -0.078493506, -0.16293915, -0.20099808, -0.08370822, 0.3701389, 0.09094984, 0.2487225, 0.24338846, 0.044003833) * go_0(-1.0, 0.0);
result += mat4(-0.061406493, -0.017232792, -0.10917424, 0.11203319, 0.040699825, -0.019294346, 0.084953666, -0.018133596, 0.07209552, 0.016069936, 0.17805555, -0.089537814, 0.15809004, 0.1027023, 0.15044671, -0.15530108) * go_0(-1.0, 1.0);
result += mat4(0.0948676, -0.040305693, -0.005591629, -0.048048403, -0.07547777, 0.056606572, 0.021390207, 0.32600567, -0.20805131, -0.099587254, 0.029613169, 0.0092129605, -0.29429698, -0.09898621, 0.44470885, -0.89487344) * go_0(0.0, -1.0);
result += mat4(-0.122259885, 0.11445877, 0.06666907, 0.1869428, -0.1553992, -0.1658741, 0.2988138, -0.57746625, -0.34609964, 0.11169158, -0.41877756, 0.38075635, 0.21293911, 0.09640372, -0.12754214, -0.08026104) * go_0(0.0, 0.0);
result += mat4(0.15128808, 0.050087795, 0.09219755, -0.18080945, 0.0044571217, -0.046019405, -0.1289922, 0.20305426, 0.19601224, 0.04667917, 0.17465587, 0.027672665, 0.18441725, 0.06845396, 0.11288585, -0.23283863) * go_0(0.0, 1.0);
result += mat4(-0.072962, -0.06639447, 0.049347494, -0.1386401, 0.10396071, 0.08187777, -0.04280746, 0.07390891, 0.06628344, 0.037797406, 0.021885803, -0.013147403, 0.22376558, 0.36243078, 0.12874891, -0.0023783944) * go_0(1.0, -1.0);
result += mat4(0.074945286, 0.16045591, -0.11798349, 0.12910712, 0.054760084, -0.095626175, -0.047832094, 0.03493912, 0.11817307, 0.037452437, -0.14301221, -0.027356789, -0.052390423, 0.11373512, 0.07686775, 0.010008694) * go_0(1.0, 0.0);
result += mat4(-0.023999173, -0.091900624, 0.02388157, 0.03173873, 0.0065633506, -0.033716757, -0.1198324, 0.12057766, 0.026465805, -0.07517131, -0.07760598, 0.060463097, 0.07345541, 0.046037503, 0.21101558, -0.26785463) * go_0(1.0, 1.0);
result += mat4(0.15544604, -0.03902825, 0.04630384, -0.25173616, -0.0691359, 0.07476507, 0.009071253, 0.089964196, -0.26539803, -0.3958477, -0.22155671, 0.20735882, -0.105860494, -0.003996804, -0.044815883, 0.39544627) * go_1(-1.0, -1.0);
result += mat4(0.6169709, 0.23717614, -0.37884676, -0.7484867, 0.020169826, -0.30718836, 1.0965588, -0.20711036, -0.39149985, -0.06843563, -0.06522909, 0.103805855, 0.03265825, -0.15137726, 0.12837899, -0.01294922) * go_1(-1.0, 0.0);
result += mat4(-0.23638196, -0.4560866, -0.11948684, -0.1464144, 0.10690008, 0.007835961, 0.11864342, -0.13101323, -0.16509797, 0.075027354, 0.08122998, 0.13451207, 0.0011890623, 0.052157886, 0.08372405, -0.07085038) * go_1(-1.0, 1.0);
result += mat4(-0.21997726, -0.16488647, -0.0291317, 0.17997476, 0.1493211, 0.027494298, 0.0034613227, -0.3207727, 0.18699001, 0.14728633, -0.042895135, -0.07612043, 0.125076, -0.14714554, -0.03480009, -0.22753975) * go_1(0.0, -1.0);
result += mat4(-0.5342686, -0.7426105, -0.38294584, 0.42549992, 0.46053204, 0.7867879, 0.106234804, -0.041163098, 0.5198579, -0.5219404, 0.14809476, -0.41802374, 0.06810794, -0.15122683, -0.047409, 0.13178343) * go_1(0.0, 0.0);
result += mat4(-0.50428164, 0.18220626, 0.35510704, -0.081787474, 0.03155813, 0.019284263, 0.0032388573, -0.20513348, -0.05385551, 0.17803182, -0.26206362, 0.2870375, 0.008557827, 0.08401449, -0.027598893, -0.010791235) * go_1(0.0, 1.0);
result += mat4(0.16657415, 0.067647465, 0.093076974, -0.14438486, -0.10017002, 0.0022367141, 0.03250936, -0.052794546, -0.009178676, -0.019673595, -0.0016697067, -0.15424626, -0.112123474, -0.11079971, 0.011987111, -0.11747758) * go_1(1.0, -1.0);
result += mat4(-0.023021797, -0.058703423, -0.037978355, -0.062433913, -0.13130441, 0.048656322, 0.056839373, 0.109036915, -0.07823158, 0.14785293, 0.058555078, -0.11679035, -0.14002073, 0.07395252, 0.098268874, -0.06710464) * go_1(1.0, 0.0);
result += mat4(0.14906375, 0.030001195, -0.10338215, 0.0662968, -0.161953, -0.13682815, 0.09563142, 0.009514228, -0.009491218, 0.06737101, -0.1393389, 0.15231515, -0.073147796, 0.00767062, 0.028675212, 0.014213088) * go_1(1.0, 1.0);
result += vec4(0.018736731, -0.0026039074, 0.050130025, -0.055364225);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(S)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_2_tf
//!SAVE conv2d_last_tf
//!WIDTH conv2d_2_tf.w
//!HEIGHT conv2d_2_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.019100675, -0.014241565, 0.004667036, -0.03865062, 0.106731094, 0.026099661, 0.014594411, -0.011881356, 0.0040967264, -0.004626336, 0.006469508, 0.010875305, -0.033909045, -0.085905954, 0.07861378, 0.019452631) * go_0(-1.0, -1.0);
result += mat4(0.20777655, -0.060354974, 0.0023840065, -0.064121604, -0.17397617, 0.019293457, -0.09707183, 0.080641985, 0.01025124, -0.017382381, 0.008661793, -0.010995665, 0.21943407, -0.115574986, 0.14471593, -0.068836235) * go_0(-1.0, 0.0);
result += mat4(0.057942886, -0.06311754, 0.2253396, -0.04159292, -0.020731755, 0.007877151, 0.041525815, 0.025278691, 0.03041967, -0.025137542, 0.024364179, -0.024543528, 0.029438615, -0.015506873, 0.081686, -0.07812221) * go_0(-1.0, 1.0);
result += mat4(0.054237515, 0.0676094, -0.0047708177, 0.0043467237, -0.10032304, -0.020498628, 0.04240586, 0.07272254, 0.0784221, 0.017945962, -0.022310399, -0.013134622, 0.015638694, -0.10001543, 0.1043031, 0.05898838) * go_0(0.0, -1.0);
result += mat4(-0.021652509, 0.35796642, 0.059497777, 0.23948468, 0.15454951, -0.10017235, -0.19072174, -0.44812536, -0.03974552, 0.04529369, 0.22207436, 0.026222564, -0.09705454, 0.5623026, -0.3354105, -0.017278556) * go_0(0.0, 0.0);
result += mat4(-0.053682446, -0.03411237, -0.09399936, 0.15128824, -0.07463, -0.042020727, 0.0031783928, 0.13481957, -0.07731454, 0.044114403, -0.23085599, 0.060444202, -0.15015422, 0.0018040676, -0.18684982, 0.2812511) * go_0(0.0, 1.0);
result += mat4(0.0029329916, 0.001596018, 0.0007512241, 0.016544111, -0.04876942, -0.05272409, 0.037884697, 0.049948208, 0.015518177, 0.11368592, -0.03815777, -0.013149978, -0.027638039, 0.107719295, -0.04115787, 0.02745414) * go_0(1.0, -1.0);
result += mat4(0.016691081, 0.010204119, 0.04078854, 0.01613337, 0.03325829, 0.0114824055, -0.017286912, -0.07284126, -0.110984206, -0.21041764, 0.0089543555, 0.18986733, 0.01537506, -0.2059135, 0.029074017, 0.013117443) * go_0(1.0, 0.0);
result += mat4(0.013965926, 0.029871881, 0.0034499036, -0.011343668, 0.022120327, -0.0068748263, 0.009324342, -0.039081004, 0.08032371, 0.050809264, 0.035050742, -0.2032847, 0.06305391, -0.021958945, 0.038569167, -0.22465245) * go_0(1.0, 1.0);
result += mat4(0.046307724, -0.012419472, 0.007673863, -0.042344846, 0.011042414, 0.016994251, -0.018166406, -0.016955731, -0.13240299, 0.01768431, -0.027607648, 0.0699927, -0.02840628, 0.004414203, 0.0049618417, 0.011084679) * go_1(-1.0, -1.0);
result += mat4(-0.119954154, -0.007455482, -0.031108133, -0.009946449, 0.0077065965, 0.01660345, 0.032943666, 0.016376585, 0.10273124, 0.1556573, -0.24643841, 0.107307844, -0.068235755, 0.0561896, -0.0104672015, 0.042693343) * go_1(-1.0, 0.0);
result += mat4(-0.01634601, 0.04195375, -0.10401894, 0.047641944, -0.034602515, -0.0034419263, -0.010457858, 0.015194475, -0.03962551, -0.030031368, 0.16036317, 0.019283568, -0.05877721, 0.016504882, -0.15523468, 0.018161612) * go_1(-1.0, 1.0);
result += mat4(-0.08083991, 0.0024665035, -0.049373373, 0.030371357, 0.0113322195, -0.014676956, 0.011646689, -0.01142667, 0.124930486, 0.06625774, -0.045840867, -0.009693036, -0.012649251, -0.07388084, 0.008790075, 0.0013844534) * go_1(0.0, -1.0);
result += mat4(-0.33941835, -0.2763476, -0.118311435, -0.063535266, 0.20936015, 0.13731301, 0.13443594, 0.07464433, 0.059650812, -0.36973104, 0.16444235, -0.37082872, 0.06432777, -0.18283032, -0.044489607, -0.13895285) * go_1(0.0, 0.0);
result += mat4(0.13533665, 0.08268915, -0.03675727, -0.14348659, 0.0186255, -0.05051692, 0.056702953, 0.0061717895, 0.047663026, -0.088188455, 0.23254345, -0.014015464, 0.08400204, -0.0073777726, 0.2202068, -0.12366078) * go_1(0.0, 1.0);
result += mat4(0.04361004, 0.046543695, 0.0064863074, -0.03358146, -0.022602187, 0.018138997, -0.011071864, 0.010244091, -0.019814799, -0.17250171, 0.040823266, -0.040131986, 0.010125854, 0.020660749, 0.0020435036, -0.010819304) * go_1(1.0, -1.0);
result += mat4(-0.004810193, -0.11286074, 0.051985834, 0.04788631, -0.023950428, 0.036145125, -0.038203828, 0.052401308, 0.022986965, 0.26420745, -0.06076917, -0.09252999, 0.03164547, 0.15652153, -0.037934, -0.0035418556) * go_1(1.0, 0.0);
result += mat4(0.03358366, -0.005219482, 0.007060882, -0.06569114, -0.02941682, 0.00966056, -0.0153679885, 0.019905418, -0.107232265, -0.03405676, -0.044340115, 0.26892832, -0.04723829, -0.02589829, 0.004563232, 0.19318114) * go_1(1.0, 1.0);
result += vec4(-0.00346731, -0.0046263863, -0.004627155, -0.0057769152);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(S)-Depth-to-Space
//!HOOK MAIN
//!BIND MAIN
//!BIND conv2d_last_tf
//!SAVE MAIN
//!WIDTH conv2d_last_tf.w 2 *
//!HEIGHT conv2d_last_tf.h 2 *
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
vec4 hook() {
vec2 f0 = fract(conv2d_last_tf_pos * conv2d_last_tf_size);
ivec2 i0 = ivec2(f0 * vec2(2.0));
float c0 = conv2d_last_tf_tex((vec2(0.5) - f0) * conv2d_last_tf_pt + conv2d_last_tf_pos)[i0.y * 2 + i0.x];
float c1 = c0;
float c2 = c1;
float c3 = c2;
return vec4(c0, c1, c2, c3) + MAIN_tex(MAIN_pos);
}
@@ -0,0 +1,969 @@
// MIT License
// Copyright (c) 2019-2021 bloc97
// All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(VL)-Conv-4x3x3x3
//!HOOK MAIN
//!BIND MAIN
//!SAVE conv2d_tf
//!WIDTH MAIN.w
//!HEIGHT MAIN.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (MAIN_texOff(vec2(x_off, y_off)))
vec4 hook() {
vec4 result = mat4(0.3053028, -0.037464816, 0.113983095, 0.12537485, -0.18630321, 0.084269725, -0.01351514, -0.20190673, -0.12298384, -0.037622184, -0.070214555, -0.19367279, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, -1.0);
result += mat4(-0.41849324, 0.099702746, -0.04276645, -0.047299717, 0.20074473, 0.14217933, 0.15571699, 0.19553481, 0.21868695, -0.053848714, 0.016413521, 0.14117444, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 0.0);
result += mat4(0.030540446, -0.052293833, 0.0715466, -0.31160545, 0.07808315, -0.16860045, 0.032828577, -0.2955024, -0.110374965, 0.04043687, -0.014024628, 0.058699366, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 1.0);
result += mat4(-0.10727635, 0.054200135, 0.20853694, 0.21086875, 0.122690216, -0.091823794, 0.310609, -0.01738923, -0.0013488946, 0.10835534, -0.077265196, 0.086751856, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, -1.0);
result += mat4(-0.77150255, 0.40530515, -0.41257596, -0.14367618, 0.46888494, 0.2650122, -0.934199, 0.40476102, 0.32293493, 0.20251967, 0.19891106, -0.29698747, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 0.0);
result += mat4(-0.12505147, -0.41904053, -0.065798186, 0.34075752, 0.026240354, -0.2977496, 0.032647505, -0.003566783, 0.10290523, -0.23417123, -0.06014203, 0.094735645, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 1.0);
result += mat4(0.11207838, -0.04062474, 0.023897955, 0.08605987, -0.020888371, 0.045541205, -0.07231824, -0.25884083, -0.11796847, -0.002691391, 0.0050435597, 0.02756291, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, -1.0);
result += mat4(0.4615728, 0.041790638, 0.08971143, 0.20213957, -0.38537467, 0.19938901, 0.08594364, -0.08621994, -0.08163473, -0.133266, -0.09561729, -0.014209637, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 0.0);
result += mat4(0.0787417, -0.0483673, 0.07621572, -0.060169693, -0.013465177, -0.17152289, 0.02515561, 0.17675288, -0.05173998, 0.10768042, -0.029858522, -0.013957215, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 1.0);
result += vec4(0.0072128535, -0.05658625, 0.052939568, -0.1760861);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(VL)-Conv-4x3x3x3
//!HOOK MAIN
//!BIND MAIN
//!SAVE conv2d_tf1
//!WIDTH MAIN.w
//!HEIGHT MAIN.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (MAIN_texOff(vec2(x_off, y_off)))
vec4 hook() {
vec4 result = mat4(-0.112743355, 0.0422517, 0.21350034, -0.0967133, 0.16265953, 0.0022497, 0.015078242, 0.08204187, 0.035236806, -0.0468228, -0.09464228, -0.001864949, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, -1.0);
result += mat4(0.25631642, -0.41485596, -0.16662048, 0.13201024, 0.057921384, 0.2240005, -0.30038536, -0.08305622, 0.2228756, 0.32263795, 0.10608189, -0.18616734, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 0.0);
result += mat4(0.08997524, 0.11516871, 0.19212262, -0.035154644, 0.11612274, -0.04056247, 0.14974374, 0.029173585, -0.07629641, -0.14353512, 0.041081246, 0.20230265, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 1.0);
result += mat4(0.2262286, 0.055954933, -0.14499907, 0.17314723, 0.16590612, -0.06688698, -0.11118816, -0.012938116, -0.043101817, 0.026133137, 0.2958395, 0.06543993, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, -1.0);
result += mat4(-0.07311521, -0.3041244, -0.47978505, -0.6350967, -0.17432262, 0.34965977, 0.25399777, -0.16590433, -0.49957857, 0.0549526, -0.40869385, -0.08780993, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 0.0);
result += mat4(-0.3014447, -0.00021343959, -0.14953177, 0.028001398, -0.14931908, -0.14910097, -0.13287953, -0.45026535, 0.17378895, 0.024704922, -0.027308129, -0.10292025, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 1.0);
result += mat4(-0.06732655, -0.13119644, 0.066014715, 0.081011154, -0.15154321, 0.2407805, 0.07733481, 0.12312706, 0.1741804, 0.008495716, -0.14125362, -0.043644864, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, -1.0);
result += mat4(0.11465958, 0.42001364, 0.011069392, 0.3203028, -0.058801666, -0.37830314, -0.030540617, 0.2245139, -0.11310525, -0.14845212, 0.19957744, 0.25789997, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 0.0);
result += mat4(-0.16037206, 0.21326372, 0.020099448, 0.018666709, 0.122083254, -0.16033986, -0.10725163, 0.2556128, 0.1650688, -0.10475823, 0.048623525, -0.103755645, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 1.0);
result += vec4(0.007717166, -0.027800834, 0.0795002, 0.0053199283);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_tf
//!BIND conv2d_tf1
//!SAVE conv2d_1_tf
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.0056740534, -0.21186607, -0.18014967, 0.118979976, -0.0015611284, -0.07708486, 0.060131397, 0.11653345, 0.027150517, 0.10837246, 0.08583816, -0.14032431, 0.017552888, 0.0035846964, 0.03980114, 0.064649396) * go_0(-1.0, -1.0);
result += mat4(-0.03289318, -0.12004539, 0.26514888, -0.15079662, 0.04214227, -0.027273783, -0.027950313, 0.19614808, 0.18510003, -0.10346252, -0.029836183, 0.09174428, -0.0088710375, -0.18273513, 0.06601674, 0.009983851) * go_0(-1.0, 0.0);
result += mat4(0.08476211, 0.043996535, 0.056711517, 0.009976895, 0.07039107, -0.024862664, -0.059921104, 0.046850603, 0.04983447, 0.04863198, 0.21777405, -0.0576961, 0.045321796, -0.0060038245, 0.096396215, -0.10842004) * go_0(-1.0, 1.0);
result += mat4(-0.15746164, 0.041757874, 0.035169285, -0.1734288, -0.24219254, -0.13318908, 0.2272079, -0.02902605, 0.07750601, -0.1467191, -0.12296749, -0.07533314, -0.07073083, 0.17909113, 0.04789308, 0.17245363) * go_0(0.0, -1.0);
result += mat4(0.057547905, 0.1464685, -0.33115456, -0.26956198, -0.26298407, -0.059824817, 0.022509675, -0.09251868, 0.36277944, -0.2072429, 0.21095088, -0.45492023, 0.07428653, 0.1593302, -0.2945834, 0.12825087) * go_0(0.0, 0.0);
result += mat4(-0.1318458, 0.27804148, 0.037600737, 0.12047866, 0.0065036337, 0.0017241207, 0.060497303, -0.14786585, -0.15149063, 0.02731698, 0.048886403, -0.0025970868, -0.026979815, 0.07348884, 0.015636757, -0.107966796) * go_0(0.0, 1.0);
result += mat4(-0.079988025, -0.01626299, 0.06517438, 0.086406484, -0.1484504, 0.070595, 0.20620634, 0.09713373, -0.13620836, 0.012067949, -0.00068703433, -0.038030174, 0.22300471, -0.0012400965, -0.014827909, -0.08927486) * go_0(1.0, -1.0);
result += mat4(0.15634936, 0.052028038, 0.038081627, 0.12720168, 0.07342066, -0.04318368, -0.0065998454, 0.12109317, -0.45398173, 0.03666754, -0.17773737, 0.038516667, -0.13009632, -0.007457001, -0.013938809, 0.09776142) * go_0(1.0, 0.0);
result += mat4(0.029636936, 0.12864171, 0.11347291, -0.11812842, -0.0870342, 0.035678383, 0.050338242, 0.045754932, -0.07072752, 0.010447726, 0.039642975, -0.08795004, -0.1191525, 0.00967509, 0.13485421, -0.053204738) * go_0(1.0, 1.0);
result += mat4(-0.011072695, -0.09613245, -0.09094804, 0.028029291, -0.04031162, 0.15690295, 0.25094184, -0.21776834, 0.06524669, 0.06412185, -0.052852992, -0.08097702, -0.039127756, 0.036357917, 0.104585476, 0.25095442) * go_1(-1.0, -1.0);
result += mat4(-0.08328618, -0.006246033, 0.099708706, -0.014916097, 0.17727195, 0.4369228, 0.14760216, 0.06707674, 0.025167737, -0.022487842, -0.038962565, 0.15380669, 0.08125089, 0.09844594, 0.33538374, -0.003161368) * go_1(-1.0, 0.0);
result += mat4(-0.0128195705, -0.05475118, -0.037705053, -0.0012077648, -0.17425515, 0.091487505, -0.12909423, 0.0074876705, 0.13438368, 5.778033e-05, 0.04563314, -0.12185897, -0.053612474, -0.049824294, -0.12851205, 0.12856449) * go_1(-1.0, 1.0);
result += mat4(-0.025741795, 0.01867236, -0.00027440622, 0.10502768, 0.27042285, -0.14947751, 0.11143123, 0.2575913, -0.07414089, -0.33919522, -0.13194235, -0.20088726, 0.23121537, -0.08197353, 0.06693911, 0.015411386) * go_1(0.0, -1.0);
result += mat4(0.09143717, 0.22842278, 0.06501074, -0.20009698, -0.042117566, -0.23452093, -0.074082755, -0.10612558, 0.077631965, 0.08343657, -0.07657599, -0.43297377, 0.7092466, -0.16272525, 0.17222248, -0.056038965) * go_1(0.0, 0.0);
result += mat4(0.081200436, 0.046752565, 0.028254949, 0.18820632, 0.096592255, 0.05896745, 0.14845169, 0.034777895, 0.07195204, -0.1908046, -0.015341971, 0.02606145, -0.010377239, 0.0755547, -0.15285216, 0.047916733) * go_1(0.0, 1.0);
result += mat4(-0.06825636, -0.049540907, -0.024328846, 0.03506251, 0.2060094, 0.054119263, -0.06671269, 0.052428722, 0.055792283, -0.14336903, -0.03180757, 0.013760968, -0.037398104, -0.06880077, -0.023608573, 0.0360965) * go_1(1.0, -1.0);
result += mat4(-0.16937497, -0.30156836, 0.0021435453, 0.025772978, -0.17990975, 0.046133514, -0.32447076, -0.083382785, -0.081322014, -0.022132374, -0.05319431, 0.11794733, 0.08943906, 0.12927428, 0.105764806, -0.051034793) * go_1(1.0, 0.0);
result += mat4(-0.011012306, 0.047636557, 0.050260928, 0.051847618, 0.010985655, -0.13752967, 0.023869954, 0.07011459, -0.18244945, 0.07239806, -0.013638856, -0.026982805, 0.11395993, -0.031304818, -0.08714153, 0.077115685) * go_1(1.0, 1.0);
result += mat4(0.08707592, 0.2265186, 0.13363098, -0.039588258, -0.029561255, 0.019238092, 0.024606103, -0.0019022018, -0.062285982, -0.0629511, -0.03753033, 0.109805316, 0.016018672, -0.08284564, -0.04092752, -0.030386891) * go_2(-1.0, -1.0);
result += mat4(0.0016500859, 0.01616536, -0.099148355, 0.24161765, 0.028064307, -0.028680569, 0.054400917, -0.1978921, -0.08584302, -0.096797146, -0.06546965, -0.09342837, 0.030265866, 0.07057579, -0.02080932, 0.053178705) * go_2(-1.0, 0.0);
result += mat4(-0.030304352, 0.047440585, -0.04248429, 0.08568772, -0.051317703, 0.036739342, 0.00865767, -0.018183297, -0.07335176, 0.025001721, -0.068509035, 0.1814819, -0.09756565, -0.024179723, -0.05959287, 0.0352454) * go_2(-1.0, 1.0);
result += mat4(0.023015196, -0.022870664, -0.12028372, -0.111095205, 0.11065281, -0.19900022, -0.24012049, -0.017028643, -0.13484617, 0.050107025, 0.10741765, 0.037951697, 0.013090438, -0.0010045726, -0.029447839, -0.1859787) * go_2(0.0, -1.0);
result += mat4(0.17922719, -0.24138594, -0.44595388, -0.032014426, 0.06897096, 0.07125395, 0.1944457, -0.035794795, -0.24022278, -0.13230884, -0.1277025, 0.21229011, -0.12249393, 0.06141907, 0.2687936, -0.26896995) * go_2(0.0, 0.0);
result += mat4(0.0397242, -0.30710965, 0.28815824, -0.06642567, -0.07588877, -0.019552408, 0.0057806037, 0.11465521, 0.03560534, -0.10640553, 0.023589289, -0.16667193, 0.02066607, -0.01026633, -0.02655378, 0.082493655) * go_2(0.0, 1.0);
result += mat4(-0.007902949, -0.08501038, -0.029395591, -0.07072227, -0.01800967, -0.14564751, -0.08372804, -0.049974415, 0.1756957, -0.02042449, -0.04413007, -0.016873527, -0.2385717, -0.001741017, 0.08298281, -0.019873247) * go_2(1.0, -1.0);
result += mat4(-0.01803727, 0.0642893, 0.21513617, 0.066888265, -0.042107955, -0.123470366, 0.045296013, -0.11958806, 0.48208967, -0.027188249, 0.12136116, 0.05246265, 0.13522038, -0.016297493, 0.028486907, -0.059840377) * go_2(1.0, 0.0);
result += mat4(-0.1373251, -0.11281026, -0.06418318, 0.08444032, 0.062874556, -0.009133875, -0.049571835, -0.042995855, 0.12483249, -0.025967957, -0.11202483, 0.09862257, 0.099986054, 0.009230306, -0.09042664, 0.046612263) * go_2(1.0, 1.0);
result += mat4(0.03203309, 0.106030256, 0.045741174, -0.020529225, -0.028610658, -0.055219248, -0.21404657, 0.07746393, -0.059359375, 0.0033258004, -0.0054513607, 0.06856653, 0.18043655, -0.119936846, -0.05639265, -0.10240379) * go_3(-1.0, -1.0);
result += mat4(-0.0004331875, 0.10426754, -0.008130048, 0.012795991, -0.14372933, -0.40797862, 0.105197415, -0.0041354536, -0.079792455, 0.0914027, 0.012418237, -0.11449173, 0.020261409, -0.14681602, -0.13355242, 0.18290488) * go_3(-1.0, 0.0);
result += mat4(0.052306626, 0.010864275, -0.072627716, -0.009773121, 0.09484167, -0.09631301, 0.14896165, -0.21220942, -0.11994051, -0.002957136, -0.118194886, 0.08661347, 0.10005298, -0.029620873, 0.101668894, 0.0242806) * go_3(-1.0, 1.0);
result += mat4(-0.055188183, -0.06322889, 0.12994595, 0.03140751, -0.092755616, 0.04239107, 0.18460171, 0.08471877, 0.014203371, 0.13608724, 0.035351243, -0.07883493, -0.10067456, 0.14417742, 0.0054235114, 0.100745104) * go_3(0.0, -1.0);
result += mat4(-0.043811034, -0.16055201, -0.11927185, 0.20517266, 0.16734722, 0.27720267, 0.1205665, 0.045803893, -0.07874647, 0.06764307, -0.11157022, 0.080770165, -0.044105835, -0.03276538, -0.10945451, 0.100562036) * go_3(0.0, 0.0);
result += mat4(-0.044731796, -0.12854387, -0.061937924, -0.21604767, -0.036132332, -0.024353411, -0.16718283, 0.14903957, -0.11620588, 0.14563644, 0.23363836, 0.08400659, 0.15248756, -0.1424437, 0.112882614, -0.04096889) * go_3(0.0, 1.0);
result += mat4(-0.0486021, -0.05714939, 0.042517707, -0.06106919, -0.12970918, -0.071898215, -0.044727243, -0.026308542, 0.05687118, -0.0394057, -0.109454155, -0.0021216893, 0.018588595, 0.08061093, 0.0500373, -0.0034918839) * go_3(1.0, -1.0);
result += mat4(0.11269324, -0.17924047, -0.12965205, -0.07287767, -0.015830642, -0.044497102, 0.20014328, -0.14054494, 0.1232692, 0.2395109, 0.14093149, 0.03518561, -0.14088139, -0.09045081, -0.07283352, 0.053434785) * go_3(1.0, 0.0);
result += mat4(0.020512339, 0.026349569, -0.06666101, 0.05554806, -0.03044066, 0.26656216, 0.019155584, -0.12118906, 0.087923005, -0.1716557, 0.050843164, 0.037432503, -0.030232614, 0.030457936, 0.04232163, -0.066400655) * go_3(1.0, 1.0);
result += vec4(-0.0216415, 0.09015036, -0.030761974, -0.26541537);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_tf
//!BIND conv2d_tf1
//!SAVE conv2d_1_tf1
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.04688368, 0.13853125, 0.1714716, -0.03034447, -0.08090605, 0.1225867, 0.17535992, 0.012508419, -0.0010665918, -0.07481546, -0.15541986, 0.0671128, -0.029307734, -0.076674186, 0.03925896, -0.07140553) * go_0(-1.0, -1.0);
result += mat4(-0.13273083, 0.062933214, 0.04200143, -0.0080243945, -0.120439716, -0.090192355, -0.022639645, 0.00020024918, -0.11211478, -0.12949537, 0.025783822, 0.009155746, 0.01004339, -0.0661901, 0.10630156, 0.053137038) * go_0(-1.0, 0.0);
result += mat4(0.07113487, -0.16011865, -0.10838903, -0.0034704183, 0.110606894, -0.14915739, 0.036511585, -0.003103608, -0.0551775, -0.13140677, 0.05270299, 0.12139221, 0.02226174, 0.008415268, -0.06647426, 0.118130066) * go_0(-1.0, 1.0);
result += mat4(-0.045172617, -0.0020388453, -0.27287582, 0.002428232, -0.2833772, 0.13788106, 0.073339015, 0.10666715, 0.08455194, 0.16499293, 0.089058325, 0.008815447, 0.034657538, -0.109856166, -0.11499077, -0.02918854) * go_0(0.0, -1.0);
result += mat4(0.07910854, -0.26334837, -0.3246593, -0.08246522, 0.09211476, 0.40793833, -0.09658794, -0.14430091, -0.50632644, 0.087234974, 0.26298127, 0.3687086, 0.06492316, 0.23082961, 0.18233871, -0.09283792) * go_0(0.0, 0.0);
result += mat4(-0.022744032, 0.21690565, 0.2694824, -0.12230013, -0.07969618, 0.21595429, -0.034979805, 0.008938489, 0.21289209, -0.446482, -0.042927746, -0.13587558, -0.032581557, -0.07182814, -0.054092336, -0.009542036) * go_0(0.0, 1.0);
result += mat4(-0.0034912943, -0.080354184, -0.08577375, -0.1521193, 0.09809233, 0.034529503, -0.100664355, 0.008191219, -0.014303411, -0.02862216, -0.18669915, -0.12384598, 0.046499267, 0.093707144, 0.10661308, 0.15079576) * go_0(1.0, -1.0);
result += mat4(-0.031025652, -0.0384342, 0.14258307, 0.25531343, 0.0075049917, -0.03966595, 0.062381975, 0.19593526, -0.2868182, 0.03162008, -0.4391041, -0.524017, -0.034463473, -0.0066741486, -0.24586639, 0.10521736) * go_0(1.0, 0.0);
result += mat4(-0.07452321, -0.0227877, -0.025402244, 0.115727395, -0.039511252, -0.07785703, -0.013689458, 0.0066024344, -0.052957747, 0.011206241, -0.0021671024, 0.077190824, -0.11709912, 0.046635598, 0.123751156, -0.03712064) * go_0(1.0, 1.0);
result += mat4(0.055411004, -0.0020031065, 0.06685547, -0.018829947, -0.06378933, -0.18389674, -0.0023551763, 0.0670314, 0.13038594, 0.0601923, -0.03035789, -0.019537423, -0.014483204, -0.056800704, 0.08663347, -0.106859975) * go_1(-1.0, -1.0);
result += mat4(-0.06603686, 0.07360526, -0.0072026253, -0.06778907, -0.039178446, 0.012397263, -0.13482279, 0.05745685, -0.055182382, -0.10545766, 0.003857615, 0.041947857, -0.15239377, 0.041826613, 0.058879383, -0.0042669442) * go_1(-1.0, 0.0);
result += mat4(-0.0697229, -0.010702144, -0.032265816, 0.013317131, 0.105028264, 0.21032134, 0.06845646, -0.018358687, 0.064568676, 0.08437135, -0.000723181, 0.1324007, 0.05527932, -0.049871888, -0.10125047, -0.005040889) * go_1(-1.0, 1.0);
result += mat4(-0.006467578, -0.05120533, -0.011780779, -0.011742203, -0.34242442, -0.020819988, 0.17381702, -0.059836414, -0.028882682, 0.23210457, 0.16579404, -0.03708216, -0.23541835, -0.03290251, 0.029319672, 0.26189178) * go_1(0.0, -1.0);
result += mat4(-0.30955994, -0.06408282, -0.16872866, 0.10767772, -0.041430887, 0.051697977, 0.12523535, -0.060389146, 0.026289431, 0.06359533, 0.13526368, 0.2479901, -0.3263977, 0.10216362, -0.0030894123, 0.046437826) * go_1(0.0, 0.0);
result += mat4(0.10061438, -0.17047118, -0.21593021, -0.023389054, -0.17507865, -0.30822313, -0.22044766, 0.16078933, 0.07099252, -0.11573018, 0.24712858, -0.0659458, -0.037504572, -0.12297423, 0.03342632, -0.058119852) * go_1(0.0, 1.0);
result += mat4(-0.020957774, -0.0224927, 0.04069268, -0.07911167, 0.074009344, 0.065916434, 0.008222278, 0.11625076, -0.25299504, 0.03357169, -0.021988, 0.015821831, -0.0021187372, -0.030700417, -0.004374924, 0.027358979) * go_1(1.0, -1.0);
result += mat4(0.06549052, -0.048067164, 0.05489091, -0.28851983, 0.13378961, 0.026875904, -0.09877994, -0.19947459, -0.1274035, -0.022928834, -0.26344195, -0.025870804, 0.022505255, 0.0070861108, 0.121051334, -0.025964163) * go_1(1.0, 0.0);
result += mat4(0.059426542, -0.0327433, 0.2313695, -0.07046268, 0.20479666, 0.027021704, 0.2564928, -0.11689885, -0.07407976, -0.019611249, 0.093463086, -0.121553615, 0.035009407, -0.008135333, -0.075931996, 0.047803063) * go_1(1.0, 1.0);
result += mat4(-0.059434246, -0.1652242, -0.124611154, 0.04743711, 0.10530296, -0.13869187, -0.036534663, -0.035206333, 0.06067593, 0.06126907, 0.120151915, -0.06722673, 0.008103894, 0.037225723, -0.007520425, 0.065720856) * go_2(-1.0, -1.0);
result += mat4(-3.6759695e-05, -0.036789574, 0.013370567, -0.037871476, -0.013454664, 0.15086569, 0.10164699, 0.057703357, -0.12871023, 0.12827681, -0.055057358, -0.040753044, -0.0142621, 0.08563361, -0.04615499, -0.03130452) * go_2(-1.0, 0.0);
result += mat4(-0.117965914, 0.09056485, 0.07272314, 0.009695964, -0.11331058, 0.07467256, -0.08291521, 0.00937355, -0.04097737, 0.07752905, -0.017335521, -0.12539999, 0.039462104, -0.0007037007, 0.06034812, -0.09497377) * go_2(-1.0, 1.0);
result += mat4(0.20828065, 0.0400099, 0.047638226, -0.046423353, -0.026133502, 0.098207295, 0.056742374, 0.017029466, -0.058164768, -0.046973787, -0.17328712, -0.0012984811, 0.050085854, 0.11296557, 0.12639083, 0.058543045) * go_2(0.0, -1.0);
result += mat4(-0.098907426, 0.22031747, 0.101559944, 0.06616554, 0.026110496, 0.56487054, 0.23754556, -0.07540935, 0.31768414, -0.47653618, 0.015073956, -0.33731326, 0.087285936, -0.24593173, -0.26141426, 0.15003823) * go_2(0.0, 0.0);
result += mat4(0.046026446, -0.13767281, 0.064847544, 0.07717139, 0.08544123, -0.11092969, 0.072325274, 0.010849038, -0.3055905, 0.66436774, 0.1434729, 0.0494463, 0.07115603, 0.083811216, 0.020431712, 0.06537088) * go_2(0.0, 1.0);
result += mat4(-0.15532711, 0.030139687, 0.040853374, 0.11089222, -0.08150315, -0.015851755, -0.06787692, 0.096075505, -0.011956207, -0.0017758606, 0.1277494, 0.16156575, -0.038588695, -0.0626418, -0.041797023, -0.19467135) * go_2(1.0, -1.0);
result += mat4(0.12917455, 0.017410474, -0.20125067, -0.08040003, -0.13494664, 0.17789102, -0.19909395, 0.08441434, 0.078570575, -0.06330619, 0.23767303, 0.5442659, -0.009227878, -0.021818208, 0.14318731, -0.09042824) * go_2(1.0, 0.0);
result += mat4(0.097801, 0.09345441, 0.17846581, -0.14773296, 0.06536365, 0.07642184, -0.011880635, 0.02086135, 0.013336972, -0.053295113, -0.13410404, 0.027241753, 0.087728985, -0.044033397, -0.13098569, 0.009423933) * go_2(1.0, 1.0);
result += mat4(-0.02488427, 0.0134966355, -0.0075000813, 0.07272353, 0.015842725, 0.13765687, 0.028079558, -0.08384948, -0.06666623, -0.023220664, 0.025091043, -0.055167805, -0.18826278, 0.04423603, 0.13499942, 0.059128854) * go_3(-1.0, -1.0);
result += mat4(0.01935146, -0.030980906, -0.031569187, -0.0036869382, 0.036753897, 0.118464164, 0.15871695, -0.09842428, 0.023324292, 0.071796335, -0.07869346, -0.10751301, -0.2588698, 0.064011686, 0.17386378, -0.039197855) * go_3(-1.0, 0.0);
result += mat4(0.08590827, 0.005497696, -0.026512025, 0.015661815, 0.1102415, -0.08268483, -0.0032903247, 0.10049029, -0.008157236, -0.035823178, -0.017570151, -0.081716835, -0.3531045, 0.010005245, 0.017141227, -0.016376914) * go_3(-1.0, 1.0);
result += mat4(-0.16617337, -0.007689783, 0.00954665, 0.07117733, -0.001669262, -0.012331606, 0.051613946, 0.062780835, 0.06123557, -0.20243123, -0.19181818, 0.032895602, 0.19760677, 0.004464939, 0.12754539, -0.27360034) * go_3(0.0, -1.0);
result += mat4(0.15006685, -0.083587274, -0.03215495, -0.16992462, -0.011944293, 0.058361508, -0.088097006, 0.023880545, -0.04168166, -0.06960282, -0.092672385, -0.057278465, 0.23540072, -0.1721208, -0.018213503, -0.23494521) * go_3(0.0, 0.0);
result += mat4(-0.124885194, 0.1905868, 0.11108704, 0.03163991, 0.11383064, 0.101223364, 0.069428995, -0.14298953, -0.07609092, 0.13704266, -0.07749446, -0.0005389336, -0.04617235, 0.18011934, 0.08350316, 0.09416366) * go_3(0.0, 1.0);
result += mat4(0.073356606, 0.067966126, -0.21285574, 0.0782625, -0.0034364646, -0.032581426, -0.05538558, -0.1317288, 0.14552782, -0.1132393, 0.13063973, -0.00833602, 0.0026844777, 0.028135289, -0.02536825, -0.028372496) * go_3(1.0, -1.0);
result += mat4(-0.318728, 0.07862527, -0.12176221, 0.35010242, -0.029198067, 0.016302662, 0.17667587, 0.12605923, 0.1556697, -0.06061443, 0.05843511, 0.10891248, 0.01267106, -0.018492714, -0.15945031, -0.050723754) * go_3(1.0, 0.0);
result += mat4(-0.21555941, -0.016813517, -0.084676236, -0.07545412, -0.14518794, -0.014592766, -0.2446481, 0.0530632, 0.0847341, 0.12342537, -0.028644923, 0.083479315, -0.04179012, 0.0025225023, 0.16006976, -0.026940256) * go_3(1.0, 1.0);
result += vec4(-0.060742114, -0.037577342, 0.055704296, 0.03134311);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_1_tf
//!BIND conv2d_1_tf1
//!SAVE conv2d_2_tf
//!WIDTH conv2d_1_tf.w
//!HEIGHT conv2d_1_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_1_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_1_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.13129333, -0.022117995, -0.009753253, 0.020439912, 0.044090994, -0.0916335, 0.0036765633, -0.11719207, -0.06413809, 0.04079378, -0.00085516454, -0.06306388, -0.12660664, -0.054126263, -0.005513979, 0.06364538) * go_0(-1.0, -1.0);
result += mat4(-0.028422508, 0.23270117, -0.28674677, -0.10820166, 0.024321957, -0.0811145, -0.07290707, -0.02125165, -0.064260505, 0.052076746, -0.009654081, 0.08363882, -0.02037171, 0.15006389, 0.121593125, -0.011237004) * go_0(-1.0, 0.0);
result += mat4(-0.14672333, 0.015381624, 0.1028172, -0.041823238, 0.0072677187, -0.042953942, 0.06426537, -0.0938381, -0.05990813, -0.04599802, -0.11264726, -0.027826328, -0.058160868, 0.10747306, -0.07327458, 0.07998872) * go_0(-1.0, 1.0);
result += mat4(-0.08702181, -0.03750975, -0.045659006, 0.04488332, 0.09102003, 0.066556975, -0.04353586, 0.08994567, -0.13561495, -0.10653702, 0.006989605, 0.028230097, 0.07177144, 0.2938447, -0.00943923, 0.022120917) * go_0(0.0, -1.0);
result += mat4(-0.1801194, -0.11119162, 0.1977298, -0.247902, -0.16654298, -0.07423158, 0.114130594, 0.0014401592, 0.006954727, -0.09810646, -0.051310766, 0.19487657, 0.2545855, -0.06328558, -0.04617056, 0.09444692) * go_0(0.0, 0.0);
result += mat4(0.011378825, 0.16044368, 0.017211074, 0.14472178, 0.032992378, -0.008925819, 0.035120245, -0.012409223, 0.074333005, 0.1178002, -0.128956, -0.13624239, -0.2791275, 0.21457297, -0.1476131, 0.04874687) * go_0(0.0, 1.0);
result += mat4(-0.03491764, -0.061763793, 0.05779039, 0.0054837577, -0.023937583, 0.08281698, 0.032306053, -0.014566218, 0.12738499, -0.0132100545, -0.051833414, 0.0057818824, 0.012158851, -0.20231532, -0.0043795826, 0.10285843) * go_0(1.0, -1.0);
result += mat4(-0.22269921, -0.15135509, -0.039143335, 0.033390045, 0.06770212, -0.14538582, -0.08011057, 0.03796648, -0.025913516, 0.13925864, 0.18309896, 0.012709204, -0.24912506, 0.3217706, 0.0394195, 0.017977878) * go_0(1.0, 0.0);
result += mat4(0.00080196525, 0.059145816, 0.05720508, 0.0056548906, 0.005168018, 0.09938438, 0.0200503, -0.05516137, 0.061309986, -0.019621318, -0.1541441, 0.019540716, 0.030571707, -0.09054893, 0.032851614, -0.27210873) * go_0(1.0, 1.0);
result += mat4(0.27061436, -0.114008114, -0.0020118617, -0.1656827, 0.09770587, 0.029897455, -0.03307522, -0.04661818, 0.033011347, 0.18498488, -0.05162084, 0.087471776, -0.24665618, -0.12538423, -0.08123797, -0.010210389) * go_1(-1.0, -1.0);
result += mat4(0.075188264, 0.0020608555, 0.18558815, 0.041179713, 0.11232638, 0.05507779, -0.19599183, 0.027942855, 0.06199144, 0.22141005, -0.06121163, 0.014993597, 0.24105869, -0.019737717, -0.112485714, 0.0157406) * go_1(-1.0, 0.0);
result += mat4(0.09425698, 0.0207658, 0.12074599, 0.009430481, 0.11889248, -0.025782838, 0.0034711843, 0.05113582, 0.012531833, -0.0018606635, -0.09137569, 0.018120576, 0.4051155, 0.02222076, -0.16001017, 0.10981527) * go_1(-1.0, 1.0);
result += mat4(-0.03582557, 0.014994796, -6.4688604e-05, 0.24618183, -0.11697727, 0.24388117, 0.038502026, -0.3511993, 0.101741396, -0.10748137, 0.035059888, -0.017535849, 0.09450039, 0.06541661, 0.12149035, 0.28798738) * go_1(0.0, -1.0);
result += mat4(-0.27143848, 0.017990451, -0.69144464, 0.037944376, -0.04551905, 0.09263134, 0.4259611, -0.14107811, -0.10641847, 0.23065196, 0.040813655, -0.07789163, 0.3087666, 0.08190437, 0.16409059, -0.06455426) * go_1(0.0, 0.0);
result += mat4(-0.08290655, -0.35286915, -0.18082355, -0.32229406, 0.1608227, 0.030915622, 0.09207708, 0.02655054, 0.039464593, 0.026095424, 0.052584656, 0.033881903, -0.01751319, -0.0011676399, 0.04002607, 0.1630013) * go_1(0.0, 1.0);
result += mat4(-0.012021132, 0.12163766, -0.07410629, -0.06879096, 0.017859738, -0.039261997, -0.028677614, -0.23610398, -0.15963873, -0.0006119958, 0.11275506, 0.0082659265, 0.05677582, 0.08676638, -0.08669759, -0.10475464) * go_1(1.0, -1.0);
result += mat4(0.12792721, 0.06888765, 0.31803077, 0.26002547, -0.067599155, -0.011822328, -0.2589909, -0.30024147, 0.11076704, 0.15200609, -0.018180368, -0.19146141, 0.22298847, 0.059484895, 0.034478076, 0.15610938) * go_1(1.0, 0.0);
result += mat4(0.0870121, -0.016420847, -0.011579898, 0.097182855, -0.120095566, -0.06843338, -0.043460473, -0.060684606, -0.027540063, -0.008499213, 0.033570655, -0.06866259, 0.01429712, -0.07424434, 0.0009466247, 0.09142678) * go_1(1.0, 1.0);
result += mat4(-0.03781424, 0.04587032, 0.03744051, 0.02712279, -0.051038064, 0.0669144, -0.02640278, 0.12384894, -0.0022533627, -0.010022036, 0.07536463, -0.030489929, 0.09418577, 0.155089, -0.011290433, -0.02102941) * go_2(-1.0, -1.0);
result += mat4(-0.0053278613, -0.07160643, 0.039028414, 0.04123311, -0.10693177, -0.1170874, 0.07230816, -0.033255517, -0.119176835, 0.0786526, -0.11880206, -0.11354601, -0.037539184, 0.14404313, 0.069760695, 0.024738638) * go_2(-1.0, 0.0);
result += mat4(0.03413808, -0.006487654, 0.10006853, 0.22228058, -0.13796462, -0.14042488, 0.04017443, -0.031790894, -0.06673143, 0.009888688, 0.08831443, -0.0045771743, -0.028375361, -0.04704813, 0.07128581, -0.07012518) * go_2(-1.0, 1.0);
result += mat4(-0.06954315, -0.23728988, -0.14192343, -0.08236467, -0.2552115, 0.04102959, -0.06355397, -0.08340241, 0.17617856, 0.20281969, -0.16249381, 0.10843737, -0.04392261, -0.08587206, 0.053069845, -0.15482199) * go_2(0.0, -1.0);
result += mat4(0.124981806, 0.12828638, -0.061472785, -0.20108232, -0.14905351, -0.40766275, -0.35427195, -0.13183996, 0.09307428, -0.07697028, 0.06702549, -0.22656697, 0.019868268, -0.19361132, 0.08784669, 0.20249842) * go_2(0.0, 0.0);
result += mat4(-0.004661343, -0.09333453, -0.24876262, -0.07906779, 0.110697776, -0.37069768, -0.042212646, -0.0046135853, -0.2254257, -0.023392014, 0.031476703, -0.045574382, -0.12675518, -0.076056994, -0.08228006, -0.040303517) * go_2(0.0, 1.0);
result += mat4(0.16182694, 0.0512523, 0.051189836, 0.048962783, -0.05156489, -0.17987493, -0.012037288, 0.06953726, -0.09458492, 0.1610021, -0.004063283, -0.032922342, 0.08995396, 0.1939926, -0.018710036, -0.08153231) * go_2(1.0, -1.0);
result += mat4(-0.064830944, 0.06121252, -0.18886387, -0.12976822, -0.031117212, 0.12219633, 0.19070715, 0.12495262, -0.11994464, -0.24687837, -0.08425294, -0.016920334, -0.13286817, -0.3260188, -0.11776061, 0.1651019) * go_2(1.0, 0.0);
result += mat4(-0.17652592, 0.002499805, -0.030541016, -0.01393431, 0.031418208, 0.08209422, 0.12430871, 0.4387016, -0.108871914, -0.09041422, 0.031226631, -0.1638517, 0.20756467, 0.014476537, -0.012701195, -0.03440563) * go_2(1.0, 1.0);
result += mat4(0.005320072, -0.0032291536, -0.017209187, 0.031944863, -0.2479921, -0.24433962, -0.13832912, 0.07835928, -0.17707248, 0.028202811, -0.19121435, 0.164587, 0.123152815, 0.0050288937, 0.084104605, -0.0380019) * go_3(-1.0, -1.0);
result += mat4(0.16008669, -0.018608516, -0.013778938, 0.033447385, -0.01242472, -0.070916265, 0.026909694, -0.07318777, 0.15158044, 0.12047607, -0.1709358, 0.2031767, 0.0025611701, -0.21457459, 0.2791286, 0.10159932) * go_3(-1.0, 0.0);
result += mat4(0.14320926, 0.020023825, -0.0484187, 0.011563084, -0.2640472, -0.013056275, 0.004234292, -0.095376395, 0.28363484, -0.0058227647, -0.0777649, 0.05238444, 0.41757923, -0.07081097, 0.012567031, -0.13029522) * go_3(-1.0, 1.0);
result += mat4(0.07266207, 0.042793367, -0.08212271, -0.23401663, -0.19457819, 0.4191269, -0.03095442, 0.15339781, -0.28451788, 0.09316364, 0.10231693, -0.22844811, 0.111623526, 0.120017685, 0.18777381, 0.014420896) * go_3(0.0, -1.0);
result += mat4(0.15037206, -0.29763284, 0.2601235, 0.0193363, 0.13686465, 0.009907918, -0.37781665, 0.04916627, 0.14114739, 0.5043813, 0.0447959, -0.029427614, 0.041768756, 0.27211213, 0.14163221, 0.086162075) * go_3(0.0, 0.0);
result += mat4(0.19159287, 0.21363218, 0.15053211, 0.08992885, 0.100828275, 0.09379921, 0.030783929, 0.11664482, -0.059145752, -0.19400764, -0.09351283, -0.016430443, -0.12910964, -0.067078374, 0.11760082, 0.121194765) * go_3(0.0, 1.0);
result += mat4(-0.055059325, 0.09299572, 0.06848913, 0.06334532, -0.1476285, 0.111801244, -0.033960916, 0.06474366, -0.04952303, 0.27885208, -0.052447475, 0.09226763, -0.15024844, -0.0033919013, 0.013498364, 0.09135676) * go_3(1.0, -1.0);
result += mat4(-0.017010042, -0.122343406, -0.19097193, -0.27957183, -0.18206005, 0.102321096, 0.22794476, 0.0439245, -0.23710132, -0.08070259, 0.17377135, 0.23811814, 0.17799385, 0.049567625, 0.1470908, 0.07329385) * go_3(1.0, 0.0);
result += mat4(0.0038071256, 0.19454515, -0.01222965, -0.07390379, -0.0532754, 0.03942833, 0.123840906, 0.023459576, -0.0658742, -0.023957543, -0.14682837, 0.1221027, -0.010986398, -0.066184506, 0.03026491, -0.0638446) * go_3(1.0, 1.0);
result += vec4(-0.06427697, -0.00039365015, 0.011889719, 0.060232002);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_1_tf
//!BIND conv2d_1_tf1
//!SAVE conv2d_2_tf1
//!WIDTH conv2d_1_tf.w
//!HEIGHT conv2d_1_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_1_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_1_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.012110923, 0.07818654, 0.07964548, 0.11885079, -0.07694473, -0.01378252, 0.006632789, -0.12876098, 0.0069211307, 0.022278586, 0.069553085, 0.16569804, -0.11123615, 0.06125189, -0.11232848, 0.1559266) * go_0(-1.0, -1.0);
result += mat4(-0.3261174, -0.25586754, 0.21129315, 0.3135101, 0.1509055, 0.0044283345, 0.024674175, -0.08000473, 0.01213029, 0.09093019, 0.04942677, 0.09806723, -0.16454464, -0.14433062, -0.058094524, -0.060819894) * go_0(-1.0, 0.0);
result += mat4(0.023174008, 0.02858724, 0.07685972, 0.036857616, -0.10415571, 0.10241035, -0.01893166, 0.02065923, 0.058356714, 0.096426114, -0.03772327, -0.1529002, 0.13740575, -0.048291504, -0.06152548, -0.15199897) * go_0(-1.0, 1.0);
result += mat4(0.029300174, -0.13222043, 0.0139825605, -0.02274408, 0.062944874, 0.028447356, 0.05960515, 0.034447193, 0.03133432, -0.019283533, -0.024591971, -0.0043914663, 0.15245225, 0.006851478, -0.051783554, 0.17453748) * go_0(0.0, -1.0);
result += mat4(-0.09125915, 0.081739366, 0.01196335, 0.23130219, -0.22557035, -0.13537665, 0.0022028848, -0.043430023, 0.22759882, 0.07920754, -0.027986467, -0.14051494, -0.19557038, -0.03585936, -0.4258294, -0.03856216) * go_0(0.0, 0.0);
result += mat4(0.18511422, -0.09368415, 0.1551229, 0.04322566, -0.023400841, -0.02261204, 0.15129441, -0.007954805, -0.10739125, 0.019459398, 0.013128325, 0.018073296, 0.20886365, -0.20662378, -0.03814699, -0.09272838) * go_0(0.0, 1.0);
result += mat4(-0.027352437, -0.039882626, 0.12598103, -0.093930446, 0.030846786, -0.09325075, -0.009084744, -0.024584265, 0.07159868, 0.14162529, 0.19019091, 0.058855128, -0.09880401, -0.01843218, 0.14753596, -0.2449532) * go_0(1.0, -1.0);
result += mat4(0.06565521, 0.09150168, -0.08654865, 0.0829788, -0.07596146, -0.01815166, -0.08786775, -0.03477514, 0.20538878, -0.012766377, 0.020719538, 0.088188395, -0.034300096, 0.29972988, -0.20005241, 0.018425167) * go_0(1.0, 0.0);
result += mat4(0.11713916, 0.024167519, 0.05167596, -0.0027117804, -0.016994188, 0.048177514, -0.012556207, 0.010979094, 0.09098878, 0.028514355, 0.06063336, -0.06624107, 0.012754856, 0.013208708, -0.061374772, -0.0025992664) * go_0(1.0, 1.0);
result += mat4(-0.09053513, 0.03183455, 0.017340872, 0.12934409, -0.022161964, -0.0015361432, -0.049972344, -0.12763855, 0.12779881, -0.04697911, 0.018968226, -0.119873665, 0.05462772, -0.13919477, -0.10226718, -0.2540179) * go_1(-1.0, -1.0);
result += mat4(-0.29912186, -0.09291771, 0.050926663, 0.49361777, 0.21372582, 0.076717265, -0.058968987, -0.1572678, 0.3194591, -0.120582424, 0.03942037, 0.023128232, 0.24321598, 0.07046334, -0.21204855, -0.648296) * go_1(-1.0, 0.0);
result += mat4(0.05366883, -0.020366706, 0.020979457, -0.06893884, 0.04837168, 0.017253762, 0.008874203, -0.020785445, -0.20425391, 0.060179923, 0.046167206, 0.09863377, -0.14381303, 0.038928367, -0.06590863, -0.18408588) * go_1(-1.0, 1.0);
result += mat4(0.07099762, 0.2029403, -0.033945918, 0.15202214, 0.0901113, -0.27336198, -0.17693861, -0.16206753, -0.17642029, 0.09400492, -0.11165698, -0.07863893, -0.16306102, -0.056210615, 0.22173557, 0.013508989) * go_1(0.0, -1.0);
result += mat4(0.08541511, -0.27093616, -0.35273993, -0.48919773, 0.038383547, -0.16013749, 0.012996215, -0.03434873, 0.07024113, -0.28971404, 0.10623425, -0.0019642068, -0.062374946, 0.3291145, 0.22468035, -0.42971882) * go_1(0.0, 0.0);
result += mat4(0.020427933, 0.15062793, 0.08308975, -0.025095072, 0.030093266, -0.09649862, -0.03382388, -0.0016017791, 0.105402954, 0.020693144, -0.051065, 0.07704679, 0.02864139, -0.00135146, 0.03762216, 0.029277142) * go_1(0.0, 1.0);
result += mat4(0.01700994, 0.12214317, 0.06749582, 0.07354159, -0.093085855, -0.065021954, 0.010773045, -0.00095128635, -0.045384295, -0.072611265, -0.043900184, 0.049471326, 0.029131187, 0.03180158, -0.13313527, 0.05280797) * go_1(1.0, -1.0);
result += mat4(0.14751251, -0.15087761, 0.09932281, -0.099232934, -0.062390897, 0.112391844, -0.09159478, 0.15856399, 0.034708973, 0.01819943, -0.02730164, -0.13562973, -0.05687333, -0.0114601655, 0.07025971, 0.02496533) * go_1(1.0, 0.0);
result += mat4(-0.0117268525, -0.026162883, 0.07481553, 0.13420302, 0.029870516, 0.07405776, -0.06379041, 0.09631234, -0.07754842, 0.035888605, 0.0034764851, -0.040771756, -0.092022054, -0.034230903, -0.02281844, -0.0028173258) * go_1(1.0, 1.0);
result += mat4(-0.059846643, 0.016772347, -0.02287152, 0.07036337, -0.024946844, 0.09826078, -0.068491876, 0.20852126, 0.073890835, -0.058288682, 0.013093785, -0.05776076, 0.0516503, 0.052794468, 0.10837015, 0.038539834) * go_2(-1.0, -1.0);
result += mat4(-0.16391893, -0.008062687, -0.35022175, 0.2510062, -0.15820411, 0.048403125, 0.024878092, 0.037888516, -0.035924178, -0.068953894, -0.025386479, 0.24405715, -0.018495679, -0.051277515, 0.14754932, -0.031538483) * go_2(-1.0, 0.0);
result += mat4(-0.038429607, -0.047140498, -0.018157095, -0.029318782, -0.04094171, -0.11870087, 0.11214255, 0.07142628, 0.021007229, -0.005681072, 0.1662777, 0.10829575, 0.112268396, 0.03567479, -0.06738845, 0.0032037434) * go_2(-1.0, 1.0);
result += mat4(-0.032217573, 0.2102397, -0.20617546, -0.07920811, 0.12918773, 0.054486286, -0.13656865, 0.05806265, 0.01963165, 0.049910642, 0.15538268, 0.10724465, -0.09697837, -0.03070673, -0.0071386313, -0.11899626) * go_2(0.0, -1.0);
result += mat4(0.130827, 0.0051715383, -0.07212691, 0.45726067, 0.2773031, 0.2973666, 0.3951691, 0.01333662, -0.14561643, 0.04508669, 0.121690124, 0.13326228, -0.22579186, 0.058161184, 0.09281702, -0.00079749606) * go_2(0.0, 0.0);
result += mat4(-0.00771113, 0.09912341, -0.41895548, -0.06705759, 0.029148718, 0.052991726, 0.18665347, -0.031787418, 0.23053595, 0.09444956, 0.10691037, -0.06325714, -0.05335701, 0.1917427, -0.0065284846, 0.032622546) * go_2(0.0, 1.0);
result += mat4(-0.056801565, -0.019131258, -0.0939022, -0.08130343, -0.11051993, 0.0035269214, -0.047361933, -0.0543875, 0.10854369, 0.06445185, 0.016828364, -0.022595318, 0.1450623, 0.033027507, -0.020425137, 0.16169788) * go_2(1.0, -1.0);
result += mat4(-0.08747717, 0.07770065, 0.018155783, 0.07160794, 0.09860347, -0.04329888, -0.0043579484, -0.2014418, -0.060260013, 0.0036374568, -0.17566042, -0.2268221, 0.001273691, -0.2609373, -0.19417606, -0.04102927) * go_2(1.0, 0.0);
result += mat4(-0.086845055, -0.114253804, -0.13433142, -0.025941795, -0.0155711295, -0.13578776, 0.12059696, -0.08760523, -0.0057348222, 0.12164273, 0.07270617, -0.06352636, 0.08894258, 0.04140841, 0.1230304, -0.030357126) * go_2(1.0, 1.0);
result += mat4(0.03320213, 0.015911903, -0.06288296, -0.121976145, 0.2713457, 0.13913193, -0.092420585, 0.105714336, 0.10294281, -0.04591945, -0.11767934, 0.032249406, -0.06506192, -0.04639334, 0.08137017, -0.031746846) * go_3(-1.0, -1.0);
result += mat4(0.13717805, 0.0071242675, -0.077256985, -0.14974317, -0.08467893, -0.20126395, -0.06240603, 0.09554399, -0.075844854, 0.28380412, 0.046030026, 0.053188596, 0.50943077, 0.1179795, 0.32203588, -0.06712207) * go_3(-1.0, 0.0);
result += mat4(-0.18528835, 0.0016975187, -0.0041140947, 0.11234392, -0.34049067, -0.056880493, -0.04325441, 0.09905571, 0.10978758, 0.009608353, -0.10801905, -0.04071131, -0.09096832, -0.12350487, 0.011801418, 0.22521795) * go_3(-1.0, 1.0);
result += mat4(0.040283076, -0.034117915, -0.026142653, -0.06058959, 0.12511659, 0.4131219, 0.59190845, 0.39758852, 0.16032091, -0.5975032, -0.14516282, 0.115154505, 0.03874097, 0.18462797, 0.22934213, 0.05285643) * go_3(0.0, -1.0);
result += mat4(-0.17804009, 0.33769128, -0.14572927, -0.029545018, 0.3897, -0.055615567, 0.15232995, 0.48788264, -0.21422523, 0.03397293, 0.0337794, -0.19830915, -0.022457365, -0.35096076, 0.42616987, -0.19268763) * go_3(0.0, 0.0);
result += mat4(-0.13191561, -0.18337126, 0.017879983, -0.070472844, -0.09409196, -0.025770849, -0.060219247, 0.10869267, -0.17341033, -0.09199785, -0.0667796, -0.093538545, -0.21300837, 0.030474098, -0.04540468, 0.041321553) * go_3(0.0, 1.0);
result += mat4(-0.0998177, -0.08669185, -0.0090886615, 0.0021083376, 0.08900095, 0.5062186, 0.45537788, 0.029077586, -0.1001008, -0.0077697043, -0.0096318, 0.11706454, 0.07401959, -0.00650215, 0.06092762, 0.037442297) * go_3(1.0, -1.0);
result += mat4(-0.18500404, 0.0024998419, -0.11761331, -0.026825588, 0.27255726, 0.093010515, 0.3281413, -0.051473666, -0.050259475, -0.17258662, -0.23394547, 0.104795866, 0.035074063, -0.061560635, 0.05975411, -0.094255395) * go_3(1.0, 0.0);
result += mat4(-0.023440497, -0.021479638, 0.0036277648, 0.004972212, 0.02416659, -0.09856867, -0.03971455, -0.27094853, 0.026615402, -0.0047890246, -0.13755885, 0.16591635, -0.0016293586, 0.133207, 0.047790572, 0.029041538) * go_3(1.0, 1.0);
result += vec4(-0.0063728676, -0.029053684, -0.052831043, 0.006475641);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_2_tf
//!BIND conv2d_2_tf1
//!SAVE conv2d_3_tf
//!WIDTH conv2d_2_tf.w
//!HEIGHT conv2d_2_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_2_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_2_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.0431447, 0.047972627, 0.09522898, 0.19048582, 0.0015511789, 0.1182684, -0.065335006, 0.061233886, -0.02451869, 0.065670215, -0.015341636, 0.06836347, 0.10215459, 0.17516296, 0.0857072, 0.072732896) * go_0(-1.0, -1.0);
result += mat4(0.10117189, 0.049022958, -0.016017418, -0.12119866, 0.089112304, 0.016286526, -0.025251161, 0.03239003, -0.0783818, -0.086096615, -0.13673106, -0.15934734, -0.51308054, -0.061430074, -0.16208844, 0.2227776) * go_0(-1.0, 0.0);
result += mat4(-0.011567444, 0.025550444, -0.018439503, -0.015003767, 0.11606929, -0.11613111, -0.040906087, -0.015202219, 0.03932618, -0.1106059, 0.03703376, 0.018548314, -0.12761284, -0.038109995, -0.23577367, 0.20272344) * go_0(-1.0, 1.0);
result += mat4(0.025444161, -0.075270735, 0.10999789, 0.16305386, 0.016178958, -0.074034974, 0.1177035, -0.077481024, -0.047774278, -0.029782977, 0.23137823, -0.2389453, 0.033015423, -0.10381626, -0.16437943, 0.20906886) * go_0(0.0, -1.0);
result += mat4(-0.098473966, 0.11013442, -0.18486807, 0.1907086, -0.17564997, -0.08509439, -0.42472756, -0.17446618, 0.3440862, 0.12719585, -0.12213955, -0.02246555, 0.18982963, 0.20809166, -0.36067408, 0.51116616) * go_0(0.0, 0.0);
result += mat4(-0.019805575, 0.07812505, 0.061653323, -0.08379226, 0.026396899, 0.009063019, -0.10845824, 0.0827647, 0.045301896, -0.07748021, -0.07435832, 0.14860612, -0.077515624, 0.010588131, -0.22704287, 0.26849246) * go_0(0.0, 1.0);
result += mat4(-0.02884339, -0.09512523, -0.038564682, 0.08862835, 0.041666254, -0.10532901, 0.040582962, -0.10063983, -0.15736029, -0.03644334, -0.005061672, 0.04302295, -0.046482194, -0.05262547, 0.05110866, 0.03204655) * go_0(1.0, -1.0);
result += mat4(-0.005932702, 0.033263832, 0.0044865874, -0.02328917, 0.056534443, -0.14084046, 0.022353357, 0.015087431, -0.2734596, -0.026544483, 0.06297078, 0.11277746, 0.06127936, 0.02466357, -0.04970561, 0.02098484) * go_0(1.0, 0.0);
result += mat4(0.013603583, 0.036264602, 0.10985147, 0.01532773, -0.09012781, 0.1132652, -0.17016481, 0.025332611, -0.077462606, 0.02990799, -0.10627784, -0.006231141, -0.089164406, -0.051507175, -0.043900985, 0.09049239) * go_0(1.0, 1.0);
result += mat4(-0.15391691, 0.1915742, 0.014101639, -0.022153432, 0.06291936, -0.017871676, -0.016763045, -0.14741553, -0.011252563, -0.20720159, -0.030648025, -0.0142307645, 0.010291614, -0.09243969, -0.052940153, 0.0061574522) * go_1(-1.0, -1.0);
result += mat4(0.032283742, 0.030768922, 0.1070225, -0.027818602, 0.10032608, 0.0061178426, -0.03561339, -0.26687133, 0.14369439, -0.11362691, -0.08980895, 0.066520914, 0.33414948, 0.006998835, 0.09193012, -0.2857383) * go_1(-1.0, 0.0);
result += mat4(-0.059588976, -0.02046844, -0.042585023, 0.031939838, 0.12796514, -0.06155685, 0.03540324, 0.009929082, -0.0039611827, 0.10790477, 0.049435645, -0.083034374, 0.23874004, -0.07460337, -0.020173345, -0.2006587) * go_1(-1.0, 1.0);
result += mat4(-0.13217632, 0.052319963, -0.026713084, -0.0051368694, -0.10380872, -0.28659084, 0.0044393227, 0.005174543, -0.05092618, -0.07092548, -0.027397033, -0.01609789, 0.13699281, -0.14706929, 0.17737861, -0.23746766) * go_1(0.0, -1.0);
result += mat4(0.19268502, 0.14133929, -0.1305119, -0.4034132, 0.057504695, -0.24550998, -0.081932545, 0.45489246, -0.29331785, 0.19625074, 0.063166246, 0.15158689, 0.6715147, -0.4610189, 0.08921431, 0.17761138) * go_1(0.0, 0.0);
result += mat4(0.044718128, -0.011809122, 0.024131307, -0.30093196, -0.05607289, 0.047759805, 0.004210022, 0.098192796, 0.030430846, 0.008207501, 0.12266905, -0.10549182, 0.11584339, -0.091016166, -0.08635591, -0.13889709) * go_1(0.0, 1.0);
result += mat4(-0.19226642, 0.07147627, -0.14759602, 0.4041079, 0.0744628, -0.19612685, 0.1498252, -0.06273549, 0.017959936, 0.10858338, -0.14985329, 0.062042814, -0.13240446, -0.24362786, 0.113626175, -0.15332204) * go_1(1.0, -1.0);
result += mat4(0.08383099, -0.13935047, -0.25981048, 0.16491203, 0.07513876, -0.28346774, 0.19722275, -0.044425573, 0.020889329, -0.22140723, 0.025403097, -0.09183192, 0.014202567, -0.18666178, 0.062913105, -0.047674105) * go_1(1.0, 0.0);
result += mat4(-0.1862771, 0.25878942, -0.043018065, 0.22144824, 0.016088247, 0.12113542, -0.11965952, -0.01587184, 0.07830932, -0.16069177, 0.13421321, 0.018718706, 0.09548377, 0.018543294, 0.013614677, -0.1054485) * go_1(1.0, 1.0);
result += mat4(-0.2121733, -0.015635416, 0.027564054, -0.085904464, 0.064805664, -0.070543915, 0.08966146, -0.06359783, 0.01131311, 0.046913184, -0.09809833, -0.092063695, -0.087217696, 0.012411829, 0.0045399712, 0.027389864) * go_2(-1.0, -1.0);
result += mat4(-0.19307798, 0.09449126, 0.084036835, 0.30262446, 0.011706106, 0.029800637, 0.04612629, 0.006186647, 0.11228541, 0.055147965, 0.17659879, -0.023410015, 0.19965266, -0.06684007, -0.081968054, -0.052410994) * go_2(-1.0, 0.0);
result += mat4(-0.058564443, 0.08252549, 0.058217794, 0.0864448, -0.25663558, 0.080260284, -0.0010294432, 0.05830051, -0.07684524, 0.1820709, 0.04438993, 0.019178499, -0.12425012, -0.04596089, -0.010032888, -0.0012803525) * go_2(-1.0, 1.0);
result += mat4(-0.43352658, 0.15262963, 0.25620222, 0.22428556, 0.09667152, 0.0037820593, -0.07951691, -0.11553085, 0.12982155, 0.17988266, -0.14283511, 0.074744284, 0.03604327, 0.00452661, -0.12865154, -0.020020623) * go_2(0.0, -1.0);
result += mat4(0.06850602, -0.18057181, 0.2093389, -0.07333886, 0.28406742, -0.048766967, 0.18114483, 0.47292945, -0.2340266, -0.06862712, 0.28263155, 0.3150323, -0.054724697, -0.16958356, 0.27928987, -0.19666018) * go_2(0.0, 0.0);
result += mat4(0.03281329, 0.0038649621, -0.07108877, 0.10791149, 0.15235375, -0.3083721, 0.168294, 0.10379698, 0.029038485, 0.16282903, 0.04483725, -0.018684763, 0.108186625, 0.027885616, -0.019351846, 0.1623065) * go_2(0.0, 1.0);
result += mat4(-0.110499054, 0.31347123, 0.030852, 0.01631416, -0.1466389, 0.080429435, -0.18689284, 0.10667815, 0.20645237, -0.18004708, -0.10570413, -0.15435064, -0.019000605, -3.126077e-06, 0.037761535, -0.015040956) * go_2(1.0, -1.0);
result += mat4(-0.023364332, -0.023399066, 0.2712722, 0.049637552, -0.10222765, -0.2698945, 0.20991959, 0.04921932, 0.21510898, -0.0751939, -0.19781734, -0.28162366, -0.041881047, 0.0065111094, -0.04102195, 0.0982682) * go_2(1.0, 0.0);
result += mat4(-0.032176614, 0.019144032, -0.08985387, 0.091637276, 0.1012352, 0.0003583357, 0.07897295, -0.09531175, -0.001155058, 0.074372366, -0.026186578, 0.07283374, 0.06052053, 0.009307753, -0.03874333, -0.06228009) * go_2(1.0, 1.0);
result += mat4(-0.022224072, -0.15717922, -0.1406057, -0.05941157, -0.028769474, -0.21226564, -0.036570027, 0.22266355, 0.14120889, 0.014577123, 0.10216447, 0.018429281, 0.056729726, -0.055834044, 0.058146577, -0.11999068) * go_3(-1.0, -1.0);
result += mat4(0.009995364, -0.020045493, -0.0057422677, 0.0643022, 0.016475432, -0.030856136, 0.042140726, 0.15077904, -0.32955253, 0.0694449, 0.17931722, 0.3439302, -0.12484157, -0.10958869, -0.15755124, -0.09755644) * go_3(-1.0, 0.0);
result += mat4(-0.008314924, 0.07704758, 0.043228816, -0.08110893, 0.099286236, -0.053224478, 0.22877018, -0.189486, -0.00798416, 0.018341504, 0.10734141, 0.0752633, -0.042524844, -0.086395286, 0.14299925, 0.026488977) * go_3(-1.0, 1.0);
result += mat4(-0.052531082, 0.19139186, 0.12205995, -0.2573172, 0.15157184, 0.0073150825, 0.089774385, 0.06604469, -0.16528498, -0.002511137, 0.14287429, -0.07819732, 0.025014274, 0.15338829, 0.0761692, -0.02803716) * go_3(0.0, -1.0);
result += mat4(-0.21000335, 0.15277153, 0.08546171, 0.2816124, -0.16559112, -0.11068559, 0.47053605, -0.009787771, -0.0013089112, -0.06985127, 0.44743782, 0.25142467, -0.32670796, 0.044035822, -0.12545367, -0.2996084) * go_3(0.0, 0.0);
result += mat4(-0.11526387, 0.15654811, 0.099616654, 0.15473685, 0.21278231, 0.046207245, 0.117993094, -0.26825273, -0.12539764, 0.14013724, 0.17357737, -0.05387817, 0.076738276, -0.13339446, 0.15005626, -0.2108176) * go_3(0.0, 1.0);
result += mat4(-0.0008846504, -0.05998622, -0.028892396, 0.04784136, 0.0104263965, 0.10899508, -0.073364735, 0.077516064, -0.074248806, -0.21749993, -0.26203, 0.041161157, 0.09366407, -0.026498007, 0.0122177545, 0.03892727) * go_3(1.0, -1.0);
result += mat4(0.04349908, 0.13671173, 0.2242545, -0.028021423, -0.03802222, 0.0052366396, -0.010709643, 0.031290106, 0.06291333, -0.024909683, -0.15439379, -0.04502091, 0.2062182, -0.5983536, -0.09670497, -0.38446042) * go_3(1.0, 0.0);
result += mat4(-0.008962513, 0.13044207, 0.04964221, 0.012250417, 0.012129821, 0.019985713, -0.06421885, 0.009168735, -0.044516414, 0.071368866, -0.006634213, 0.06497366, 0.08578495, -0.10586125, 0.06628038, -0.14006054) * go_3(1.0, 1.0);
result += vec4(0.056541316, 0.041788545, -0.036094554, -0.021763096);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_2_tf
//!BIND conv2d_2_tf1
//!SAVE conv2d_3_tf1
//!WIDTH conv2d_2_tf.w
//!HEIGHT conv2d_2_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_2_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_2_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.0647927, 0.053666476, -0.14723225, 0.027874574, -0.0003166473, 0.07337155, -0.061972085, -0.012667777, -0.17071614, 0.091927536, -0.051160213, 0.21336353, 0.13854574, 0.09582817, 0.032316446, 0.13838023) * go_0(-1.0, -1.0);
result += mat4(-0.0398984, 0.108049214, 0.093780346, -0.022015186, -0.15188989, -0.1381083, 0.2998843, 0.21623154, -0.08862326, 0.025862623, 0.06895634, 0.13529755, 0.06957801, -0.0011681129, 0.105972745, -0.04722446) * go_0(-1.0, 0.0);
result += mat4(-0.026321493, -0.04828038, -0.012545767, -0.005490858, -0.054038163, 0.075943105, -0.11526662, 0.022242405, -0.03543104, -0.12451852, -0.14911178, 0.013503498, 0.08773292, 0.09695139, -0.013498657, -0.27424073) * go_0(-1.0, 1.0);
result += mat4(0.018575635, -0.11321618, -0.07853153, 0.04104883, 0.0018416744, 0.11579002, 0.03685964, -0.031546146, -0.1755398, 0.23517849, -0.08095411, 0.031999595, -0.18542038, -0.26171613, -0.20567231, -0.05683613) * go_0(0.0, -1.0);
result += mat4(0.1538556, 0.21723682, 0.12131733, -0.15308167, 0.103326, -0.006956118, 0.043583486, -0.23811384, -0.103285454, 0.05543916, -0.37894246, 0.32072112, 0.22651967, 0.03516268, 0.34612176, 0.23688535) * go_0(0.0, 0.0);
result += mat4(0.040021293, 0.0029912095, 0.04885362, 0.061496444, 0.016926387, -0.118446946, 0.038948335, -0.0934512, -0.25194243, -0.054018084, -0.07149527, 0.017903058, 0.0845516, 0.33802906, 0.11953944, -0.081294954) * go_0(0.0, 1.0);
result += mat4(-0.09558082, -0.36974236, -0.07524102, 0.11131445, 0.047626104, 0.12854609, -0.10264962, -0.044669047, -0.05572307, 0.34475142, -0.16806377, -0.0037204176, 0.03400533, -0.04047774, 0.024379745, 0.09056291) * go_0(1.0, -1.0);
result += mat4(-0.039392482, 0.2553437, 0.11705501, 0.03219211, 0.073977776, -0.16610906, -0.032796364, -0.054669864, -0.07123178, 0.00079619256, -0.36920992, -0.029054813, 0.12830003, 0.004987549, 0.08724278, -0.029499404) * go_0(1.0, 0.0);
result += mat4(0.021272454, -0.063295126, 0.011779576, 0.103093, -0.011095461, 0.027948728, -0.014605259, -0.04723974, -0.05334346, -0.044831257, -0.07296399, -0.03314197, -0.01687865, -0.09261895, -0.06128567, 0.092708185) * go_0(1.0, 1.0);
result += mat4(0.0077418387, 0.00871427, 0.060824487, 0.1093608, -0.021077013, -0.057341542, -0.04769576, -0.08144089, 0.0212823, -0.06731425, -0.04134463, -0.0016761447, -0.03402026, 0.036424547, 0.11689576, -0.14946719) * go_1(-1.0, -1.0);
result += mat4(0.18536687, 0.020073935, 0.17041959, 0.024790209, 0.08397728, -0.13884324, 0.013950321, -0.055075396, -0.09317963, -0.05723721, -0.060491834, 0.0017911601, -0.109154835, 0.010338362, -0.1982491, -0.21752335) * go_1(-1.0, 0.0);
result += mat4(0.031852514, 0.031424347, 0.07817056, 0.07770759, 0.019805199, -0.091223724, 0.11914662, 0.1673029, -0.018734453, 0.16275099, 0.23245652, 0.36139074, -0.1396047, -0.14774057, 0.13756078, -0.123794965) * go_1(-1.0, 1.0);
result += mat4(-0.034937833, 0.20777488, 0.10104809, -0.035140667, 0.2536575, 0.010970045, 0.16896339, -0.081219964, -0.062478427, -0.0010431948, -0.027980985, 0.11446318, -0.127309, 0.21002083, 0.044436257, -0.16986957) * go_1(0.0, -1.0);
result += mat4(0.06309646, -0.042341243, 0.36642808, 0.18653205, 0.06973023, 0.06315932, -0.323688, 0.25672218, 0.042820994, 0.13792914, -0.12892757, -0.09220378, -0.18939693, 0.03862022, -0.17376114, -0.24673308) * go_1(0.0, 0.0);
result += mat4(-0.02130602, -0.35428852, -0.011634983, -3.9823462e-05, 0.110818714, -0.2981158, 0.060209107, 0.012538829, -0.0744833, -0.050204318, -0.12676497, -0.031484153, -0.28799182, 0.22338839, -0.070876874, -0.02102363) * go_1(0.0, 1.0);
result += mat4(-0.07929991, 0.014598492, 0.23034762, 0.024872296, 0.07480494, -0.17139243, -0.014421178, 0.056448363, -0.028626937, -0.022152562, 0.044871796, -0.048653606, 0.009350802, 0.019022083, -0.08554845, -0.0922645) * go_1(1.0, -1.0);
result += mat4(-0.027405115, 0.1831188, 0.28516722, 0.19882526, 0.27299204, -0.06910511, 0.03244419, -0.0031333128, 0.061055277, -0.114398144, 0.03729459, -0.07840815, -0.37776002, -0.24129418, -0.54815483, -0.2702045) * go_1(1.0, 0.0);
result += mat4(0.053723935, 0.13472083, 0.09563273, 0.19009806, -0.18722993, -0.25939655, -0.016197463, -0.067061596, 0.1647598, 0.061905228, 0.06191816, -0.018582113, -0.07218153, 0.11278394, 0.05478068, -0.104871586) * go_1(1.0, 1.0);
result += mat4(0.0036616288, -0.045782693, -0.226954, -0.05043515, -0.078096785, -0.036197383, 0.09269631, 0.016823346, -0.0060579977, -0.041455746, 0.09032774, -0.09217121, 0.058089796, 0.060311552, 0.033079024, 0.022586476) * go_2(-1.0, -1.0);
result += mat4(0.0436363, -0.079482526, 0.0027447809, 0.039558932, 0.13275702, 6.898711e-05, -0.21961488, -0.11315821, 0.0076181027, -0.025279062, -0.15829584, -0.063141204, 0.062049046, 0.13117202, -0.02435016, 0.109555416) * go_2(-1.0, 0.0);
result += mat4(-0.010148116, 0.056620967, -0.015910713, -0.07370375, 0.1529919, 0.005792597, 0.02771225, -0.17027487, 0.096740395, 0.063347995, 0.17823112, 0.054105148, 0.04995114, -0.28613812, 0.06369567, 0.15978208) * go_2(-1.0, 1.0);
result += mat4(-0.13688345, 0.16967694, -0.061759472, 0.013682004, -0.1290496, 0.07167547, -0.065592445, -0.17897636, 0.057080988, 0.035630587, 0.09140394, -0.08695068, 0.16807681, 0.014749346, 0.07875138, 0.034913708) * go_2(0.0, -1.0);
result += mat4(-0.098915346, -0.31459075, -0.10892429, 0.1557498, -0.19764107, -0.26881596, -0.03589311, 0.45288458, -0.34171388, 0.12675741, 0.18415868, -0.19770056, 0.29025507, -0.15812592, 0.09685835, 0.0027761247) * go_2(0.0, 0.0);
result += mat4(0.06425249, -0.01169722, 0.06379363, 0.053835012, -0.07356561, -0.06367294, 0.108630784, -0.14137438, 0.08536725, -0.03209748, 0.07250959, -0.014214082, 0.07170588, -0.25647813, 0.1092683, 0.18791042) * go_2(0.0, 1.0);
result += mat4(-0.023783233, 0.14261739, 0.102011986, -0.03633555, -0.05032627, 0.09378387, 0.11764051, 0.1353335, 0.032817088, -0.1352964, -0.00667997, -0.13388929, 0.022861317, 0.0037358075, 0.018605746, -0.0009892831) * go_2(1.0, -1.0);
result += mat4(0.22419162, -0.23105696, -0.09900454, -0.15831396, 0.12398773, 0.097933106, -0.13189293, 0.1330756, -0.19673057, -0.037342317, -0.13462654, -0.08974021, 0.030326528, -0.0815862, -0.118352115, 0.009187904) * go_2(1.0, 0.0);
result += mat4(-0.012130391, -0.06408448, 0.13710785, -0.06678414, -0.09970725, -0.14895032, -0.02366641, 0.029581001, -0.07101809, 0.09414698, 0.018300869, 0.009139046, -0.0027311493, -0.2359952, -0.011602826, -0.007582444) * go_2(1.0, 1.0);
result += mat4(-0.15473361, -0.06868751, -0.030721204, -0.08650113, 0.071349874, -0.08177769, 0.1611948, 0.18305337, -0.0144878505, 0.10975452, -0.026968453, -0.04909913, -0.059665974, 0.056036238, -0.11623168, -0.10584912) * go_3(-1.0, -1.0);
result += mat4(-0.096973225, 0.054132458, -0.010600018, 0.089397885, -0.0031138035, 0.037452973, 0.041115325, 0.1924831, 0.14759748, 0.032560788, -0.082884625, 0.0324635, -0.083511285, -0.050381303, 0.025589975, -0.0981257) * go_3(-1.0, 0.0);
result += mat4(-0.09183111, 0.034952193, -0.048511654, 0.020719057, 0.1863456, 0.01902738, 0.14455654, -0.008500172, 0.16385981, -0.07806569, -0.031216217, -0.17002788, -0.08882952, 0.07335293, -0.2223089, 0.01706056) * go_3(-1.0, 1.0);
result += mat4(-0.08361569, 0.046698716, -0.016646344, 0.09351987, 0.0054158634, -0.13641126, -0.12396605, 0.011380122, 0.040951792, -0.11222528, -0.0031548145, -0.0022303525, 0.0350846, -0.03280425, -0.09972476, -0.113325305) * go_3(0.0, -1.0);
result += mat4(-0.19961461, -0.27561286, -0.12783135, -0.062596925, 0.005870981, -0.24796526, 0.18717633, -0.16945636, -0.076396205, -0.08411448, 0.13751988, 0.21014418, -0.008655945, -0.09848541, -0.14536901, -0.2132181) * go_3(0.0, 0.0);
result += mat4(0.14118621, 0.20831147, -0.020545695, 0.008340737, 0.016840864, -0.16912372, -0.121718146, 0.15108089, -0.19803092, -0.07827729, -0.047639225, -0.12277847, 0.04974115, -0.09349339, -0.2756667, -0.19581003) * go_3(0.0, 1.0);
result += mat4(-0.0036992705, 0.16539848, 0.022026122, 0.07740234, -0.035687633, -0.004568715, 0.017408118, -0.09757294, -0.094941914, -0.3381112, -0.12724453, 0.025583982, -0.18571027, 0.047607586, -0.0704089, -0.055323426) * go_3(1.0, -1.0);
result += mat4(0.13821335, 0.028168043, 0.09990671, -0.032266147, -0.067236245, 0.11512147, -0.112986445, -0.10818019, -0.10062181, 0.21276556, 0.01681818, 0.069806606, 0.09628121, 0.06456379, 0.10394843, -0.02343886) * go_3(1.0, 0.0);
result += mat4(0.041937463, 0.072631165, 0.045366894, -0.0046993676, 0.03946691, 0.121010706, -0.030089365, -0.007266469, 0.0092267515, 0.14853416, -0.033248078, -0.027284347, -0.10031526, 0.15864117, -0.16782752, -0.18466589) * go_3(1.0, 1.0);
result += vec4(0.07722432, -0.025165567, 0.034291282, -0.09902708);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_3_tf
//!BIND conv2d_3_tf1
//!SAVE conv2d_4_tf
//!WIDTH conv2d_3_tf.w
//!HEIGHT conv2d_3_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_3_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_3_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.004729794, -0.0124398535, -0.08538641, -0.058604605, 0.008671952, 0.25604513, 0.020800482, 0.24144122, -0.028920606, -0.04705229, 0.030192787, 0.0010597534, 0.017666103, 0.0041322373, 0.20027764, 0.08919112) * go_0(-1.0, -1.0);
result += mat4(0.0001626656, 0.05816014, -0.0060765734, 0.08811165, 0.35835367, -0.016291425, -0.56892496, 0.083845764, 0.15026698, -0.15916558, 0.08069463, -0.3931291, -0.0123534845, -0.111639686, -0.14637001, -0.08171439) * go_0(-1.0, 0.0);
result += mat4(-0.114976816, 0.023376396, 0.13855027, 0.07438716, -0.069991484, 0.20377779, 0.23929878, -0.040769435, 0.018832395, 0.005638609, -0.091848075, 0.027843866, 0.023744943, -0.06620523, -0.11678267, 0.0844119) * go_0(-1.0, 1.0);
result += mat4(0.0035854098, -0.08432094, -0.17799544, -0.10041983, 0.25605857, 0.021009786, 0.030499447, -0.09928291, 0.052178737, -0.08286175, -0.057888374, 0.024606042, 0.046342995, 0.13875343, 0.11279266, 0.19826262) * go_0(0.0, -1.0);
result += mat4(-0.016232021, -0.21539623, 0.0936961, 0.021143785, 0.094262615, 0.049040064, 0.40978724, 0.15347758, 0.08884813, -0.24887115, -0.14756748, -0.5020875, 0.112477, 0.1466549, -0.33418837, 0.5769466) * go_0(0.0, 0.0);
result += mat4(-0.16832942, -0.07354198, -0.12081261, -0.055348314, 0.39716053, 0.25583258, 0.09870877, 0.2151021, -0.025700683, -0.1801462, -0.04616654, -0.02782245, -0.054461803, -0.00042802413, -0.00163228, -0.004240747) * go_0(0.0, 1.0);
result += mat4(-0.05193433, -0.0018198475, -0.17647028, -0.19462106, 0.1538165, 0.054894235, 0.12183955, 0.07340974, -0.0019901982, 0.0357373, -0.07597063, -0.06681543, -0.00090057997, -0.053894397, -0.010301875, -0.16553953) * go_0(1.0, -1.0);
result += mat4(-0.30873474, -0.2836045, 0.057037193, -0.5016378, 0.11952749, 0.102353275, 0.2351629, -0.14635189, -0.019398788, -0.08776502, 0.021669978, -0.089918956, -0.2187901, -0.1180891, -0.049789533, -0.16109149) * go_0(1.0, 0.0);
result += mat4(-0.078335494, -0.08867304, 0.03349591, -0.1000293, -0.20235832, 0.22917585, -0.09905303, 0.08381748, 0.014350217, -0.14478815, -0.027479894, -0.026432173, -0.10309177, -0.09860884, -0.019177807, -0.06963025) * go_0(1.0, 1.0);
result += mat4(0.008169383, 0.12532842, -0.23369955, 0.077973194, 0.09076616, -0.021277165, 0.1721421, -0.26914293, -0.014729218, -0.023279984, -0.057670787, 0.003598546, -0.015225789, -0.0115396585, -0.26196182, -0.10724508) * go_1(-1.0, -1.0);
result += mat4(0.16542235, 0.06589374, 0.07410237, 0.26753154, -0.3356288, 0.3096256, 0.07112498, -0.0992165, 0.15020338, -0.11021673, 0.18803611, 0.12918204, 0.109007336, -0.031968266, 0.057093572, 0.035949256) * go_1(-1.0, 0.0);
result += mat4(0.065006174, 0.031055925, 0.0390232, -0.01678507, -0.21553491, 0.14171642, -0.19541772, -0.033691674, -0.06241631, 0.07497651, 0.024557155, 0.056778047, -0.060191352, -0.0261998, 0.07493729, -0.0699132) * go_1(-1.0, 1.0);
result += mat4(-0.008541382, 0.020270415, -0.027760057, -0.040962905, -0.26732433, 0.34379438, -0.23012447, 0.0051356517, -0.04059567, 0.0972959, 0.039965224, -0.14796777, -0.0016924662, -0.116963714, -0.026353523, -0.29799464) * go_1(0.0, -1.0);
result += mat4(0.03329303, -0.12663862, -0.0004959157, -0.11162377, 0.26238343, 0.43260252, -0.16504994, 0.10727678, -0.22505566, 0.43474057, 0.43304008, 0.05143919, 0.40494493, 0.08689636, -0.035733614, 0.25727916) * go_1(0.0, 0.0);
result += mat4(0.12175736, -0.014467151, -0.17461288, -0.18480565, -0.26439998, 0.307935, -0.058916792, -0.014292711, -0.0569471, 0.10751278, -0.04134206, 0.1847734, -0.07519831, -0.033909313, -0.05001451, -0.136606) * go_1(0.0, 1.0);
result += mat4(0.1424893, -0.026820501, 0.19645774, -0.0011315406, -0.14680974, 0.07662838, 0.21108222, 0.13260938, 0.17923595, -0.085527614, 0.08217639, 0.06579479, 0.05985784, -0.09016323, 0.11172888, 0.111903176) * go_1(1.0, -1.0);
result += mat4(0.19842595, 0.0093640275, 0.10433465, 0.13341904, -0.082806975, 0.22555825, -0.1315717, 0.11907785, 0.24012424, 0.47776055, 0.1835734, 0.17483878, 0.079803735, 0.01155073, -0.21146573, -0.16484722) * go_1(1.0, 0.0);
result += mat4(0.15064004, 0.021381427, 0.18301587, 0.21225913, 0.054995645, 0.03212186, 0.052798916, -0.048424408, 0.03609021, 0.0964704, -0.059469886, -0.05133066, -0.08157349, 0.051145166, -0.09107608, -0.1362262) * go_1(1.0, 1.0);
result += mat4(0.090521574, -0.014747857, -0.081675015, -0.118686825, 0.04848682, -0.033071827, 0.008534588, 0.023765508, 0.16849907, -0.21797262, -0.17049783, -0.07824179, -0.033794608, 0.052612655, 0.095820345, -0.07262317) * go_2(-1.0, -1.0);
result += mat4(0.22816367, -0.13772108, -0.036353834, -0.47638395, -0.0530902, 0.14089061, 0.076203234, 0.18006112, 0.121814854, -0.20750527, 0.08266107, -0.28634354, 0.14301859, -0.13458411, 0.00501663, -0.039783802) * go_2(-1.0, 0.0);
result += mat4(-0.103384845, -0.14389835, 0.08275834, -0.068423435, 0.22643796, -0.02966374, -0.2847584, 0.037081387, 0.02349005, -0.19353923, -0.00095957273, -0.13623689, -0.073120415, 0.03941467, 0.21864155, -0.014019576) * go_2(-1.0, 1.0);
result += mat4(-0.082576886, 0.17085212, 0.08971252, -0.04213377, -0.032548156, 0.022137715, 0.08399252, -0.0011743539, -0.09410863, -0.41728264, -0.20709297, -0.18933547, 0.027059928, 0.09743364, 0.2504647, -0.041173562) * go_2(0.0, -1.0);
result += mat4(-0.20924084, 0.291118, 0.029851688, 0.16953468, 0.02936709, 0.12213576, 0.22944322, 0.108747594, 0.0001881129, -0.27398208, -0.009702691, 0.15449248, -0.9472944, -0.26114875, -0.28161275, -0.3495961) * go_2(0.0, 0.0);
result += mat4(-0.12994622, -0.2758638, -0.1091727, -0.0968308, -0.14323105, 0.035175014, -0.08023811, 0.006023802, -0.031529594, -0.1486306, -0.3398172, -0.23240276, -0.29163983, 0.173475, 0.18809283, 0.22197202) * go_2(0.0, 1.0);
result += mat4(0.048254848, -0.083444916, -0.014334202, 0.060992356, -0.023099286, -0.09492961, 0.05592045, 0.0026059286, 0.08998117, -0.108810075, -0.053304546, 0.045926623, 0.068255246, 0.099023566, 0.01595483, 0.1336309) * go_2(1.0, -1.0);
result += mat4(0.21916585, 0.2837387, 0.14624594, 0.18843961, -0.06747584, 0.054924384, -0.082568415, 0.05011459, 0.014297759, -0.3884833, -0.054417178, -0.18970548, 0.088336475, -0.030646667, -0.2980552, -0.030035203) * go_2(1.0, 0.0);
result += mat4(-0.02748568, -0.011897529, -0.2370837, -0.016740574, -0.0282112, 0.050353892, -0.10761107, -0.00036999505, 0.037646662, -0.17742962, 0.06489219, -0.158852, -0.08016933, 0.07808515, -0.105895035, 0.079869986) * go_2(1.0, 1.0);
result += mat4(-0.0058994526, -0.037170693, 0.2574696, 0.06199102, -0.04497728, -0.10667442, -0.15183865, 0.0212881, -0.030842574, 0.073473394, 0.010764398, -0.00084518327, -0.03893014, -0.009649613, 0.07443129, 0.15108284) * go_3(-1.0, -1.0);
result += mat4(0.11325495, -0.096435815, -0.097331434, -0.049700152, -0.17231967, 0.047090057, -0.019111065, 0.104790315, -0.15004838, 0.13950798, 0.055996202, -0.070548095, 0.047154237, -0.007650949, -0.053611025, -0.012242293) * go_3(-1.0, 0.0);
result += mat4(0.12787002, -0.04958212, 0.053988468, 0.0017896162, 0.049493514, -0.009475431, -0.0022641935, 0.03933694, -0.005174597, 0.043754533, -0.1432976, 0.037084177, -0.04601288, -0.032077815, -0.059897035, 0.12584484) * go_3(-1.0, 1.0);
result += mat4(0.019409029, 0.10492923, 0.268368, 0.12597778, -0.17733063, -0.0085961, -0.27136415, -0.049664587, 0.012515404, -0.21444482, -0.39275557, -0.12297177, 0.06800057, 0.19228315, 0.06245887, 0.35772634) * go_3(0.0, -1.0);
result += mat4(-0.16317715, 0.2288402, -0.23235172, 0.22230752, -0.1646375, 0.13366091, 0.16681044, -0.17399235, 0.33997267, -0.3179832, -0.34756508, 0.39843196, -0.10748536, 0.322923, 0.23339489, 0.08684083) * go_3(0.0, 0.0);
result += mat4(0.02835275, 0.12314228, 0.24030593, 0.30856124, 0.055735108, -0.044914473, 0.0031432225, 0.07469899, 0.1778018, 0.107083894, -0.023706734, -0.15501897, 0.0943098, -0.034707237, -0.18622099, 0.05257965) * go_3(0.0, 1.0);
result += mat4(0.042839274, 0.12597966, 0.08979042, -0.0647561, -0.050434645, 0.049438696, -0.20008127, -0.05572608, 0.046238814, 0.12622325, -0.019017145, -0.13960391, -0.040050175, 0.14298008, -0.20270552, 0.13391526) * go_3(1.0, -1.0);
result += mat4(-0.0073277587, 0.10606624, -0.08940439, -0.09656414, 0.12387374, -0.0013147948, 0.23607181, -0.00037969893, 0.050353236, -0.17266603, 0.27796733, -0.09877832, 0.02711225, 0.096394345, 0.07457944, 0.21541388) * go_3(1.0, 0.0);
result += mat4(-0.18612787, -0.00027517386, -0.17136407, -0.06413671, 0.025629476, -0.04570916, 0.0008431566, -0.03419168, 0.08123608, 0.09465922, 0.11975521, 0.1269741, 0.08413221, 0.12125001, 0.04727287, 0.072378494) * go_3(1.0, 1.0);
result += vec4(0.04244928, -0.014280219, 0.017129054, -0.08807801);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_3_tf
//!BIND conv2d_3_tf1
//!SAVE conv2d_4_tf1
//!WIDTH conv2d_3_tf.w
//!HEIGHT conv2d_3_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_3_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_3_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.01973856, -0.05053795, 0.015545361, 0.10867395, 0.33441806, 0.14731607, 0.6793983, -0.21394718, -0.00846322, 0.09146322, -0.07427475, -0.078477465, -0.090998545, 0.133366, 0.105515696, -0.13784988) * go_0(-1.0, -1.0);
result += mat4(-0.05404873, 0.09784018, -0.1337389, -0.18082313, 0.13461179, -0.3816801, 0.12209786, 0.08176651, 0.10461896, -0.43315184, 0.017470734, 0.20423968, -0.03941875, -0.101959296, -0.09440259, 0.09154717) * go_0(-1.0, 0.0);
result += mat4(0.17229515, -0.06907825, -0.008382803, -0.16671611, -0.01576541, 0.03985307, 0.08209482, -0.11707446, -0.11793074, 0.13702396, -0.02013158, 0.07302033, -0.022301994, -0.11464677, 0.036753565, -0.093276784) * go_0(-1.0, 1.0);
result += mat4(-0.017650167, 0.009475923, -0.17856382, 0.15925962, 0.06434641, -0.15568036, 0.038135886, 0.18855911, -0.04427734, 0.1878215, 0.10856261, 0.0041275816, -0.12046199, 0.13610138, 0.3741596, -0.12934728) * go_0(0.0, -1.0);
result += mat4(-0.24631616, 0.0169485, -0.035534818, 0.37795424, -0.08546174, 0.07817259, 0.42897213, -0.47965595, -0.0146556785, -0.20510523, -0.18889453, 0.06476019, 0.1021008, -0.35398817, -0.031071864, -0.21416448) * go_0(0.0, 0.0);
result += mat4(0.32810766, 0.050585747, -0.17658374, -0.13881154, 0.16417882, -0.21286008, -0.106835455, -0.1722344, -0.14151084, 0.08962986, 0.057395387, -0.01623662, 0.02570415, 0.15626897, -0.12687978, 0.080729105) * go_0(0.0, 1.0);
result += mat4(-0.050597478, -0.018753758, -0.036346875, -0.017908493, 0.058593344, 0.008303028, 0.05254987, -0.06635018, -0.022532012, 0.029511122, 0.026682215, -0.054647952, 0.069466785, -0.08892492, 0.025351115, -0.023130694) * go_0(1.0, -1.0);
result += mat4(0.2412473, -0.16138165, -0.15117447, 0.11851003, -0.096868426, 0.082690425, 0.27923304, 0.11590443, 0.19363573, -0.15770023, -0.066793665, 0.011681678, 0.14037277, -0.112065665, -0.048159517, 0.009453693) * go_0(1.0, 0.0);
result += mat4(0.1580054, -0.0060506654, 0.05267837, -0.09178131, -0.09107123, 0.23191126, 0.21108283, -0.070422985, 0.024321035, 0.06131459, 0.066626504, 0.032481454, 0.044402298, 0.1390604, -0.14432502, 0.040869843) * go_0(1.0, 1.0);
result += mat4(0.10264861, 0.013504324, 0.012482852, -0.1781206, -0.12799414, -0.27026084, -0.123830505, 0.098105, -0.039127555, 0.09367889, 0.122323096, 0.1416734, 0.044763107, -0.21801683, -0.14018978, 0.17646866) * go_1(-1.0, -1.0);
result += mat4(0.017453065, 0.11498537, -0.10998983, -0.3116098, -0.3099762, 0.5024706, 0.051817298, 0.03170681, -0.18937826, 0.07946567, -0.11978771, -0.09523745, -0.0033551592, -0.11768945, 0.08932359, -0.06689581) * go_1(-1.0, 0.0);
result += mat4(0.1507582, -0.013266159, -0.073085934, -0.07252967, -0.06301927, -0.13218755, 0.12984878, -0.13678701, 0.023422396, 0.082123175, 0.006906731, -0.004018426, -0.15813835, 0.13711788, 0.016018609, 0.13443229) * go_1(-1.0, 1.0);
result += mat4(-0.06960673, 0.16156524, -0.1374069, -0.05803206, -0.077960715, -0.10676749, 0.26282015, 0.03521529, 0.058099385, -0.014738148, 0.0011174522, 0.24279532, -0.023991548, -0.108812414, -0.08886019, 0.20584475) * go_1(0.0, -1.0);
result += mat4(-0.08043308, 0.063343, 0.055290066, -0.15991378, -0.08096304, -0.23888679, 0.019161629, 0.38381267, 0.3672934, -0.119608454, -0.43623593, -0.46014485, -0.5323366, 0.1318621, 0.087373205, -0.05535459) * go_1(0.0, 0.0);
result += mat4(0.20640239, -0.1369444, -0.21677823, 0.08202178, 0.10515278, 0.06810837, 0.073207974, 0.23623931, 0.102422275, -0.05016664, -0.0039228587, -0.1810343, -0.2235563, -0.1246854, 0.1428113, -0.10609135) * go_1(0.0, 1.0);
result += mat4(-0.031941894, -0.08905056, 0.21501167, 0.11244667, -0.011811734, 0.21630247, 0.07589472, -0.040489636, -0.11824066, -0.11520391, -0.10075633, -0.035642453, 0.062144946, 0.0073282206, 0.14119269, -0.060479023) * go_1(1.0, -1.0);
result += mat4(-0.29382935, -0.056808118, 0.051812876, -0.061358813, -0.08344258, 0.124203674, 0.037964176, -0.01961274, -0.000951725, 0.50005037, -0.24176972, 0.06487161, -0.15469861, 0.04336187, 0.17826353, 0.040010225) * go_1(1.0, 0.0);
result += mat4(0.02044482, -0.0879271, -0.01053958, -0.31148303, 0.07497373, -0.11548258, -0.1666126, 0.02369657, -0.058044076, 0.010801491, -0.005933901, -0.08910467, 0.007953008, 0.03761974, -0.029501524, 0.16816042) * go_1(1.0, 1.0);
result += mat4(0.1779597, -0.10213089, 0.29942423, -0.016642543, -0.015537001, -0.04676146, 0.09585872, -0.0055750017, -0.014361908, -0.20667697, -0.11348746, 0.13081487, -0.10437329, 0.14328459, 0.11648822, -0.09163837) * go_2(-1.0, -1.0);
result += mat4(0.019033967, -0.12420627, -0.07748253, 0.43203858, -0.109799065, 0.07605535, 0.060791396, -0.24517195, -0.15674245, 0.21267459, 0.10665515, -0.073150024, -0.1358355, 0.0054066703, -0.16434059, -0.06031853) * go_2(-1.0, 0.0);
result += mat4(-0.18834068, 0.26840356, -0.12937617, 0.16103932, -0.0062331813, -0.13630053, -0.013911821, 0.022389365, -0.044232946, -0.056454606, 0.022426741, 0.18010215, 0.041900013, 0.03375041, -0.11376866, -0.010313381) * go_2(-1.0, 1.0);
result += mat4(0.12497669, -0.31161824, 0.097568035, 0.19443443, -0.05056519, -0.0031457904, 0.1055554, -0.083650924, 0.07630523, -0.34177595, -0.093093194, 0.20701368, -0.030962149, -0.054470222, -0.23853977, 0.004326528) * go_2(0.0, -1.0);
result += mat4(0.34370202, 0.085750066, -0.16071722, -0.54335934, -0.35595295, -0.050744478, -0.17405547, 0.008628697, -0.007086256, 0.23164117, 0.340156, 0.5475976, -0.15292351, 0.28019544, 0.038059216, 0.0044727) * go_2(0.0, 0.0);
result += mat4(-0.08231968, -0.0052294536, 0.07451547, 0.22278999, -0.3305531, 0.0017458396, 0.10818422, -0.21325395, -0.08807993, -0.110342845, 0.10082142, -0.051594347, 0.24192205, -0.18042035, -0.0095462985, -0.08757798) * go_2(0.0, 1.0);
result += mat4(0.096379586, 0.021887815, -0.05097233, -0.06797989, -0.026171045, 0.022944937, -0.015915364, 0.037667938, 0.17216732, -0.014889412, 0.07343887, 0.028236505, 0.0015047621, 0.1355103, -0.09918284, -0.07673695) * go_2(1.0, -1.0);
result += mat4(-0.25385055, 0.15163356, 0.0030003798, 0.18464413, 0.05611221, 0.099498056, -0.07128191, 0.042955168, 0.027493173, 0.07440157, 0.07814497, 0.096160784, 0.13571084, 0.056412842, -0.031997006, -0.16073681) * go_2(1.0, 0.0);
result += mat4(-0.21634746, 0.025153082, -0.064477116, 0.0005679147, -0.0029436245, 0.12794618, 0.024849026, 0.03018052, 0.11723976, 0.059955597, -0.013594654, 0.09091745, 0.04775348, 0.21260159, -0.07463213, -0.06727042) * go_2(1.0, 1.0);
result += mat4(-0.12166018, 0.024545137, 0.08611618, -0.17627168, 0.09042604, -0.14157623, -0.22147785, 0.09100581, 0.11078359, 0.031410985, -0.17170976, 0.09532806, -0.059569277, 0.09392676, 0.11784347, -0.21471368) * go_3(-1.0, -1.0);
result += mat4(0.1483187, -0.2217563, 0.12032977, 0.14932398, 0.27428308, -0.04568031, 0.12670338, 0.09586169, 0.06700745, 0.005126449, 0.0027694793, -0.033667028, 0.06447861, -0.08585174, -0.05509812, -0.11358761) * go_3(-1.0, 0.0);
result += mat4(-0.22750492, 0.032906335, -0.029479047, 0.11580199, -0.05812372, -0.032269973, 0.05219915, 0.041658226, 0.010897959, 0.065550454, 0.0076911976, -0.045743827, 0.11614996, -0.10393113, -0.0012606392, -0.034367524) * go_3(-1.0, 1.0);
result += mat4(0.09350742, 0.09561609, 0.3735968, 0.031685118, -0.042026598, 0.17006761, -0.3910107, 0.16984761, 0.25679177, 0.036610503, -0.13772772, 0.11101589, -0.1137049, 0.07211461, 0.18065079, -0.12324793) * go_3(0.0, -1.0);
result += mat4(-0.020749722, 0.14413361, -0.061903823, -0.21550268, 0.31306142, -0.11532895, 0.029482557, 0.03282164, -0.09800627, -0.20765196, 0.33030233, 0.075725295, 0.49252015, 0.042455837, -0.07264194, -0.10401895) * go_3(0.0, 0.0);
result += mat4(-0.22697076, -0.15738785, 0.09740376, -0.072098814, -0.06638972, 0.12336611, 0.0073687397, 0.048267826, 0.06717852, -0.027047804, -0.123397194, 0.17829034, 0.04215185, 0.066311836, -0.061742183, -0.046373066) * go_3(0.0, 1.0);
result += mat4(0.041311592, 0.2813485, 0.055084586, -0.01823069, 0.08105147, -0.087944716, -0.10135052, -0.02653456, 0.063169874, -0.1351186, 0.06722432, -0.016406318, 0.08666922, 0.0555909, 0.12086502, -0.17224412) * go_3(1.0, -1.0);
result += mat4(0.26026788, -0.18303715, 0.029279215, -0.12858874, 0.027197823, 0.0919464, 0.00849638, 0.10547888, -0.12952055, -0.14414985, 0.1903315, 0.05004528, -0.12657289, 0.038008716, -0.036606666, -0.054025438) * go_3(1.0, 0.0);
result += mat4(0.069167465, 0.2699947, -0.11137602, -0.05888806, -0.107324794, -0.07598601, 0.06042177, 0.0064530694, -0.039780665, -0.076666445, -0.00846108, -0.06165907, -0.06978219, -0.19108103, -0.040026028, -0.120319635) * go_3(1.0, 1.0);
result += vec4(-0.14375664, -0.0056876075, 0.052177623, 0.07152566);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_4_tf
//!BIND conv2d_4_tf1
//!SAVE conv2d_5_tf
//!WIDTH conv2d_4_tf.w
//!HEIGHT conv2d_4_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_4_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_4_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.15667982, -0.31441393, 0.29112124, -0.15737213, 0.022372838, 0.10690639, -0.12019085, -0.051941186, -0.30367845, 0.02612279, 0.2372532, 0.2021648, -0.20481086, -0.003770439, 0.14981231, 0.066780254) * go_0(-1.0, -1.0);
result += mat4(0.03270688, -0.42270073, 0.044317324, 0.15907793, 0.14681059, -0.2934784, 0.24933252, -0.067273855, 0.07752533, -0.23194817, 0.0686707, 0.08999225, 0.121678345, -0.12916678, 0.012397381, 0.012315053) * go_0(-1.0, 0.0);
result += mat4(-0.10090412, -0.20792678, 0.11076032, -0.02938975, -0.1944187, -0.2003259, 0.04438032, 0.36946484, -0.019868722, -0.15830222, 0.042811528, 0.015641417, 0.113098525, 0.080257006, 0.011135628, -0.2877629) * go_0(-1.0, 1.0);
result += mat4(0.15482685, 0.06579119, 0.28301102, 0.23729764, 0.15990537, 0.4529694, 0.107880585, 0.10668121, -0.42430598, -0.2631025, 0.10513542, -0.036242936, -0.09827965, -0.0069260495, -0.11689201, -0.041436482) * go_0(0.0, -1.0);
result += mat4(0.08472191, -0.13051608, 0.047930017, 0.36831668, 0.1164478, 0.21384816, 0.22062506, 0.2094167, 0.48668453, 0.32302913, 0.36268055, -0.091801375, -0.079141125, -0.26613805, -0.16608004, 0.03810683) * go_0(0.0, 0.0);
result += mat4(-0.13474251, -0.04824603, 0.23303726, -0.116136365, 0.0056330245, 0.15829784, 0.0012259148, 0.12648389, 0.038680512, 0.05131116, 0.024099711, 0.4555406, 0.0035716395, 0.11633299, 0.094744846, -0.2457627) * go_0(0.0, 1.0);
result += mat4(-0.0576871, -0.04037522, 0.16857862, 0.0031084458, -0.027274646, -0.18154246, 0.13337846, 0.035422433, -0.0030749738, -0.17288287, 0.019983152, -0.31871706, -0.03280405, 0.06825421, -0.1563798, 0.05031885) * go_0(1.0, -1.0);
result += mat4(-0.066631876, 0.012560506, 0.1690693, -0.018248236, 0.0450104, 0.016296914, -0.14910112, -0.16191053, 0.5078224, -0.017615631, 0.15226597, -0.13373777, 0.20148668, 0.060258996, 0.13215344, 0.18430072) * go_0(1.0, 0.0);
result += mat4(0.12976126, -0.072738245, 0.053067926, 0.09752956, -0.04716214, 0.04136464, 0.014162617, -0.06621296, -0.09617736, 0.057469178, 0.01280261, -0.042976785, -0.12570308, 0.006027807, 0.031038594, 0.06569918) * go_0(1.0, 1.0);
result += mat4(-0.12655424, -0.41563693, -0.030971345, -0.06357555, -0.14121394, -0.15667427, 0.14398985, 0.05995984, 0.0821605, 0.12462943, 0.007492498, -0.0030187522, -0.22804567, -0.10487421, 0.13180672, -0.13978589) * go_1(-1.0, -1.0);
result += mat4(-0.075991526, 0.12352044, -0.17844258, 0.010614991, -0.18293494, 0.25009897, -0.080779895, 0.21548378, 0.22215544, 0.048670914, -0.057372037, 0.078176, 0.17490411, 0.004919551, 0.059619516, 0.12660357) * go_1(-1.0, 0.0);
result += mat4(-0.06282951, 0.10929357, 0.026720649, -0.15939257, 0.17107709, -0.04334904, -0.03047162, -0.101681694, 0.03118431, 0.19994627, 0.025729552, 0.035035726, -0.0012207883, -0.08618888, 0.061205562, 0.009940555) * go_1(-1.0, 1.0);
result += mat4(-0.23581573, 0.08002133, -0.15170844, 0.08872338, -0.25767094, -0.09273545, 0.18153891, 0.2544269, -0.084598936, -0.089766875, -0.14610913, 0.002247754, 0.1802837, -0.019625561, 0.30239686, -0.032793984) * go_1(0.0, -1.0);
result += mat4(0.5223286, 0.10347663, 0.4000593, 0.25440502, -0.07646958, -0.31940606, 0.053407036, -0.09356492, 0.2738851, 0.23945184, -0.2907089, -0.45822915, 0.13415676, 0.17187089, 0.08731114, -0.27670014) * go_1(0.0, 0.0);
result += mat4(0.059273496, -0.107137166, 0.12087539, 0.179237, -0.021209063, -0.02548005, 0.061256204, 0.033822674, 0.54491127, -0.2475085, 0.08055858, -0.4071213, -0.045093834, 0.07161349, 0.08219979, -0.31735933) * go_1(0.0, 1.0);
result += mat4(-0.29527053, 0.021469543, 0.07202354, -0.07103959, 0.03990857, 0.2490762, -0.19419849, -0.13916986, -0.05325315, 0.12922864, -0.041463424, -0.031249814, 0.073991664, -0.09723187, 0.35132217, 0.024760868) * go_1(1.0, -1.0);
result += mat4(0.09606787, -0.0951808, -0.0059865676, -0.052033573, -0.3118038, 0.4432636, -0.12943317, 0.09484738, 0.10621756, -0.10550469, 0.11264014, 0.1402276, -0.012679125, -0.08809835, 0.029994955, -0.15121669) * go_1(1.0, 0.0);
result += mat4(0.123397775, 0.048338536, -0.00975707, -0.103767075, -0.041053303, -0.07228534, 0.046792876, 0.0668788, 0.29554394, 0.012451002, 0.19568972, 0.112091154, 0.10882395, -0.0995439, 0.051324263, 0.24967718) * go_1(1.0, 1.0);
result += mat4(0.2699648, 0.17300771, -0.16056584, 0.1099392, 0.11674778, -0.19811755, 0.111880325, -0.06075038, -0.095849104, -0.04510651, -0.04180761, -0.0052786698, 0.11037549, -0.24115366, 0.018509468, -0.07819484) * go_2(-1.0, -1.0);
result += mat4(0.10981622, 0.044488225, 0.050722387, -0.3146652, -0.0013019707, -0.24084032, -0.10475088, 0.026944289, 0.1592903, 0.33087498, 0.061839584, -0.043863457, -0.06904603, -0.08635262, 0.088630445, -0.15485142) * go_2(-1.0, 0.0);
result += mat4(-0.06810522, 0.19927117, -0.08130387, 0.11612667, -0.015104349, -7.738651e-05, -0.06419643, -0.14813533, 0.026650215, 0.015038833, 0.08161237, 0.058321163, 0.015005185, -0.16189656, 0.024501886, 0.1927279) * go_2(-1.0, 1.0);
result += mat4(0.31858218, 0.11962043, -0.20560326, -0.13190113, 0.02138715, -0.057066392, -0.085771754, -0.124566585, 0.044749223, 0.13687828, 0.1195792, 0.14021616, 0.26204133, 0.05119197, -0.13980037, 0.050747477) * go_2(0.0, -1.0);
result += mat4(-0.21238558, -0.0734057, -0.2036023, -0.34308743, -0.29370925, 0.2393742, -0.37877437, 0.036869828, -0.17053255, -0.26900926, -0.23330869, 0.32902205, -0.4882585, 0.27430108, -0.033711653, 0.15501487) * go_2(0.0, 0.0);
result += mat4(0.23487025, 0.085289046, -0.14281847, 0.12543266, 0.15871634, -0.13858907, 0.14810285, -0.0239261, 0.1286852, 0.07754033, 0.01072327, -0.14313328, 0.05480442, -0.12195059, 0.11341822, 0.08224607) * go_2(0.0, 1.0);
result += mat4(0.19490337, 0.023521842, -0.24548791, 0.0035114093, -0.07937166, -0.07674376, 0.08365873, -0.003286068, 0.023862893, 0.009626835, 0.032829892, 0.0078141205, 0.053484406, -0.08297165, 0.09303188, 0.004273738) * go_2(1.0, -1.0);
result += mat4(-0.0032906602, 0.13636959, 0.027821168, 0.06270053, 0.024775786, -0.077529594, 0.03799126, 0.030000908, 0.031749167, 0.04360487, 0.004448846, -0.17835903, -0.30834544, 0.013150946, -0.13758293, -0.03296242) * go_2(1.0, 0.0);
result += mat4(-0.14166978, 0.034131095, 0.049779188, 0.09453289, -0.011406557, -0.07020709, -0.0031981543, -0.03443845, -0.00010218944, 0.0855161, -0.10951453, 0.042758763, 0.1718446, -0.1577923, 0.0410027, -0.04992991) * go_2(1.0, 1.0);
result += mat4(0.1219178, 0.105126485, -0.041097324, -0.08110963, -0.04857337, -0.11544925, -0.14572923, 0.092435546, 0.091857366, 0.15425235, -0.020324683, -0.05764375, -0.020458939, -0.10527823, -0.085554086, 0.16358297) * go_3(-1.0, -1.0);
result += mat4(-0.12372687, -0.009976829, 0.14252265, -0.1321053, -0.05965866, -0.1393898, -0.017603246, -0.02714342, -0.16824952, -0.23083204, -0.012299022, -0.06689838, -0.015830487, 0.21299921, -0.11637202, 0.0074968333) * go_3(-1.0, 0.0);
result += mat4(-0.01979935, -0.182785, -0.015397454, 0.14175794, -0.011465284, 0.11285164, -0.036115747, 0.07150463, -0.083641894, -0.10221778, -0.13871445, 0.099696055, 0.04603662, -0.06463785, -0.007984529, -0.0032940735) * go_3(-1.0, 1.0);
result += mat4(0.072830334, -0.057334073, 0.09086239, 0.13039105, 0.06350303, 0.17130788, -0.2181585, -0.09137403, -0.31397742, -0.019071499, -0.017274613, 0.13762084, 0.10195637, -0.021455176, 0.04011394, -0.08029658) * go_3(0.0, -1.0);
result += mat4(-0.26982597, -0.40265098, -0.4151411, 0.038557775, -0.095602125, 0.3503172, -0.029988842, -0.03484708, 0.095536314, -0.0030311556, 0.31589827, 0.52763534, -0.12629713, -0.24356791, 0.0059487303, 0.42298427) * go_3(0.0, 0.0);
result += mat4(0.054166105, 0.18827972, -0.081673265, -0.06720384, 0.09375001, 0.22173035, -0.14050071, 0.108400136, -0.15553835, -0.08716729, -0.037366748, 0.10971073, -0.02560103, -0.26702073, -0.05201882, 0.2432563) * go_3(0.0, 1.0);
result += mat4(0.16196893, 0.0889265, -0.09887943, -0.042956755, -0.054403376, -0.123823255, 0.045847844, 0.017027669, 0.00539936, -0.112265736, 0.050549984, -0.104931094, -0.06883012, -0.25745714, 0.11155538, -0.15363649) * go_3(1.0, -1.0);
result += mat4(-0.22157209, 0.18200903, -0.13290548, 0.026721261, -0.06066069, -0.18150693, 0.08768983, 0.037362453, -0.1073367, -0.070236765, -0.41223463, -0.168915, -0.15517351, -0.13949952, -0.13307643, -0.15935421) * go_3(1.0, 0.0);
result += mat4(-0.026589906, 0.0930502, 0.05195435, 0.06301585, -0.01107014, -0.019382332, 0.027223695, -0.004045145, -0.15238355, -0.0345132, 0.06355168, 0.0011230056, 0.16690113, 0.0017829507, -0.0023939044, -0.09471834) * go_3(1.0, 1.0);
result += vec4(0.024455175, 0.01669877, -0.066231176, 0.036848705);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_4_tf
//!BIND conv2d_4_tf1
//!SAVE conv2d_5_tf1
//!WIDTH conv2d_4_tf.w
//!HEIGHT conv2d_4_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_4_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_4_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.01763509, -0.17156707, -0.06841296, -0.026132878, -0.10600523, 0.11245994, 0.121395074, -0.09331501, 0.12764473, 0.0428028, -0.11837395, 0.2092563, -0.04357652, -0.0490096, 0.024701532, 0.10518723) * go_0(-1.0, -1.0);
result += mat4(-0.17130826, -0.31987694, -0.07639005, 0.21362033, 0.058639023, 0.066175915, -0.25344703, -0.07923442, -0.14766373, 0.040518284, -0.031103026, -0.040075514, -0.051108997, -0.28214613, -0.18504949, 0.27544948) * go_0(-1.0, 0.0);
result += mat4(0.030991005, -0.011353306, 0.15237464, 0.15458584, 0.1250524, 0.19959912, 0.14049476, 0.38410887, 0.07378578, -0.017728366, 0.0963528, -0.043756213, -0.039577194, -0.11800575, -0.08392266, -0.07599512) * go_0(-1.0, 1.0);
result += mat4(0.022089608, -0.027317125, 0.051330008, -0.0075439885, 0.021650828, -0.0009390209, -0.12043464, 0.049332134, -0.055557396, -0.053297505, -0.0918705, -0.13089466, -0.10994107, 0.072746456, 0.11496739, -0.05225977) * go_0(0.0, -1.0);
result += mat4(0.29730305, 0.26317745, 0.052159555, -0.32006654, 0.48288685, -0.049926184, -0.08091092, -0.13825637, -0.1485706, -0.288657, -0.41443697, 0.06856032, -0.23809211, -0.12953928, 0.4783034, -0.47557938) * go_0(0.0, 0.0);
result += mat4(0.026139118, -0.23031352, 0.04861487, 0.033556074, 0.2702056, 0.22802536, -0.15385233, 0.1664119, 0.18749923, 0.36927548, -0.011473684, -0.11771165, -0.16859052, -0.4513202, 0.12863952, 0.02482837) * go_0(0.0, 1.0);
result += mat4(0.0073229345, -0.061915245, 0.06710329, 0.0062416573, -0.00555983, 0.14592186, 0.11201052, -0.123630054, 0.32611257, -0.11279885, -0.059449438, 0.2891043, -0.10519016, 0.040108994, -0.012468261, 0.02083298) * go_0(1.0, -1.0);
result += mat4(-0.057483062, 0.08454755, -0.15529329, -0.12572923, 0.2600099, -0.02319978, -0.04037675, 0.11496361, 0.07728194, -0.12908956, -0.025529336, 0.112581626, 0.02971823, 0.11659056, -0.01298622, 0.017061908) * go_0(1.0, 0.0);
result += mat4(0.22417091, -0.00222947, 0.04980858, 0.12260437, -0.025507605, 0.042577885, 0.120813504, -0.048522256, -0.038494784, -0.0072195013, -0.23012944, -0.020850847, -0.078296244, -0.014830018, 0.19759563, -0.10000253) * go_0(1.0, 1.0);
result += mat4(-0.032090195, 0.023757193, -0.08989734, 0.14419042, 0.0112194475, -0.093776144, -0.020197887, 0.29295877, 0.06872183, 0.09511462, -0.03245769, -0.06504889, 0.05132126, 0.00399527, 0.075911656, 0.250893) * go_1(-1.0, -1.0);
result += mat4(-0.3418496, 0.25525784, 0.0018161442, 0.028484365, -0.17573346, -0.12457501, 0.18466166, 0.20209278, 0.10282706, 0.16353399, 0.025052028, -0.059714165, -0.055806916, -0.28651386, 0.112798095, 0.11624314) * go_1(-1.0, 0.0);
result += mat4(-0.018793896, 0.07500149, -0.01728254, -0.1726998, -0.13333, 0.09590344, -0.036537904, -0.11522523, 0.19445558, 0.22680458, 0.12061006, -0.06225618, 0.1127748, 0.28380096, -0.07099846, -0.007440302) * go_1(-1.0, 1.0);
result += mat4(-0.43887648, -0.10018577, -0.29267642, 0.12149727, -0.14333835, 0.04161915, 0.19442867, 0.16506511, 0.09655387, -0.0014398015, 0.13189743, -0.14068556, 0.049408, 0.0829072, 0.2950336, 0.36965907) * go_1(0.0, -1.0);
result += mat4(0.41486958, -0.023498302, -0.37900022, -0.31752598, 0.13758768, -0.18782206, -0.31358528, 0.3330786, -0.4039293, -0.06539036, 0.032599606, 0.10663507, -0.26369813, -0.17365438, 0.20723309, 0.1801556) * go_1(0.0, 0.0);
result += mat4(0.004117444, -0.14894462, 0.14915143, -0.047375835, -0.2609916, -0.10172324, -0.14925237, -0.33830285, 0.12131607, -0.18156646, -0.42382464, -0.052582145, 0.2329045, -0.4576963, 0.13756892, 0.055571318) * go_1(0.0, 1.0);
result += mat4(-0.31689477, 0.017058033, -0.01904924, -0.016893756, -0.011479519, 0.07316262, -0.07086077, 0.08923511, -0.08190091, -0.025866933, -0.06909204, -0.028601022, 0.023224542, 0.03082087, 0.2230426, -0.16713654) * go_1(1.0, -1.0);
result += mat4(0.13457374, 0.110913865, -0.1130815, -0.031438913, -0.55201167, 0.04831016, 0.25107765, -0.014003224, 0.19532952, 0.02062346, 0.04839241, 0.088673405, 0.30325848, -0.20222804, -0.085780576, 0.22512968) * go_1(1.0, 0.0);
result += mat4(0.076354, 0.021940092, -0.16170324, 0.0025543426, -0.0032400405, -0.0046705627, 0.06241069, -0.031247333, 0.098353796, 0.03723474, 0.22971998, -0.017877292, 0.119858086, 0.008041448, 0.2140585, 0.10343376) * go_1(1.0, 1.0);
result += mat4(0.08627595, 0.04532834, 0.027579082, -0.16222088, 0.15583228, -0.14371829, -0.07243855, -0.111895435, -0.14438897, -0.10250594, 0.0034202964, -0.066547595, -0.034390844, -0.021545287, 0.014540157, -0.10215731) * go_2(-1.0, -1.0);
result += mat4(0.19720152, 0.21534947, 0.1130938, -0.011730973, 0.013247983, -0.10344174, -0.1906514, -0.015767017, -0.020093633, -0.26487067, -0.005960781, -0.057149183, 0.030110173, 0.047692046, -0.19308545, -0.25292158) * go_2(-1.0, 0.0);
result += mat4(0.039498243, 0.053682897, -0.01844695, -0.017540915, 0.039454967, -0.27696076, 0.09503274, -0.038958035, 0.17321438, -0.036311295, 0.03123055, 0.02310311, 0.040591653, 0.0054627894, -0.03520426, -0.026101988) * go_2(-1.0, 1.0);
result += mat4(0.055991564, 0.06512919, -0.12532505, 0.024075158, -0.04926237, -0.11701171, 0.026792146, 0.013033238, -0.052847516, -0.01550091, -0.008442071, -0.077945165, -0.033220004, -0.13678443, -0.07040586, 0.121846326) * go_2(0.0, -1.0);
result += mat4(-0.19537796, -0.016634773, 0.10707109, -0.024361614, -0.16002733, -0.44066608, 0.16488662, 0.013152995, 0.22407806, 0.12854017, 0.19028598, -0.08379244, -0.05594235, -0.15909895, 0.511962, 0.39027596) * go_2(0.0, 0.0);
result += mat4(-0.032652248, 0.06004893, 0.011166194, 0.102761306, -0.035113614, -0.29961765, -0.013817978, 0.20938557, 0.08488225, -0.1118558, -0.0375328, -0.035511103, 0.0046933405, 0.20203683, -0.13552529, -0.12685429) * go_2(0.0, 1.0);
result += mat4(0.03054923, 0.08224908, -0.059128158, -0.02583655, -0.02133876, 0.0048713544, 0.10848829, 0.06324404, 0.028332822, -0.011002306, -0.027557913, -0.06072362, 0.1019048, -0.02587316, 0.08563405, -0.08119947) * go_2(1.0, -1.0);
result += mat4(-0.10568117, 0.1075248, 0.19379964, -0.14337265, 0.019374132, -0.0907804, -0.13827625, -0.03628561, 0.014735499, -0.026882607, -0.25948793, 0.034926686, -0.05988073, -0.22735636, 0.053511668, 0.04765336) * go_2(1.0, 0.0);
result += mat4(-0.029848114, 0.09183966, 0.084713496, 0.09422864, 0.069713995, -0.10584984, -0.020899031, 0.059645247, -0.075805016, -0.01828552, 0.06689195, -0.13804196, -0.023465823, -0.034038994, -0.12946706, 0.058709413) * go_2(1.0, 1.0);
result += mat4(0.061918218, 0.038984764, 0.013660938, -0.19340219, -0.014949839, 0.12946278, 0.12725051, 0.13429146, 0.05993008, -0.015394284, 0.011232483, 0.0344157, 0.022161875, -0.023923954, 0.061736204, 0.025963215) * go_3(-1.0, -1.0);
result += mat4(0.048136763, 0.03162042, -0.01967249, 0.06374493, 0.034645267, 0.22403605, 0.036197048, -0.06903216, -0.1024706, -0.0005459356, 0.049185563, 0.16309108, 0.07394778, 0.10351343, 0.28430694, -0.13531347) * go_3(-1.0, 0.0);
result += mat4(-0.14705071, -0.09458433, 0.03063114, 0.07901115, -0.11911086, -0.06428132, -0.013549552, -0.041342866, -0.20770676, -0.15104479, 0.054365363, -0.11652907, 0.05639815, 0.070518605, 0.0017846811, -0.00056205114) * go_3(-1.0, 1.0);
result += mat4(0.27148908, 0.07358356, 0.13644488, -0.13824654, 0.0112991175, -0.021521023, -0.10197379, 0.007816017, -0.13314332, 0.12318473, -0.043214846, -0.15759036, -0.19744353, -0.10267182, -0.28249928, 0.11233295) * go_3(0.0, -1.0);
result += mat4(-0.096474804, 0.17893109, 0.014679829, -0.21218887, -0.24170275, 0.10603527, 0.05375366, -0.059315052, 0.17087384, 0.13633691, -0.37958893, 0.43264794, 0.17829923, 0.06485103, -0.37551817, -0.22082718) * go_3(0.0, 0.0);
result += mat4(-0.30536333, -0.033212308, -0.25232, 0.11730442, -0.11176368, 0.26223183, -0.049025323, -0.01375941, -0.29028055, 0.16842811, -0.035684332, -0.4180911, -0.1611732, 0.07683385, -0.14263596, 0.17508087) * go_3(0.0, 1.0);
result += mat4(0.23580009, 0.025621435, -0.15757325, 0.008123166, -0.021905439, -0.02162503, -0.059497356, -0.01636353, 0.047654126, -0.084423855, -0.033733923, 0.0127116265, -0.059593942, -0.053935718, -0.050729543, 0.013887048) * go_3(1.0, -1.0);
result += mat4(-0.19232626, 0.07915767, -0.05909752, 0.007695347, 0.058876406, 0.057521783, -0.080253534, 0.2011056, -0.27965516, -0.08033169, -0.13025513, 0.12854645, 0.053400308, -0.18445957, -0.18463044, 0.27920377) * go_3(1.0, 0.0);
result += mat4(-0.061806213, -0.020037206, 0.003183183, -0.029844081, -0.039553937, 0.028905323, -0.11367984, -0.097321615, -0.10112643, 0.0039709485, -0.06020118, -0.23871279, -0.077974856, 0.05806996, -0.21440302, 0.11898043) * go_3(1.0, 1.0);
result += vec4(-0.023832673, 0.03702965, -0.04749135, -0.10982549);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_5_tf
//!BIND conv2d_5_tf1
//!SAVE conv2d_6_tf
//!WIDTH conv2d_5_tf.w
//!HEIGHT conv2d_5_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_5_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_5_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.030931145, 0.013683292, -0.0650242, -0.028732346, 0.120067924, -0.029404473, 0.0038229884, -0.14631765, 0.041900825, -0.076596744, -0.11096378, -0.27100095, 0.0052598766, -0.05929686, -0.06816563, -0.086864315) * go_0(-1.0, -1.0);
result += mat4(-0.043620087, -0.16360405, 0.006527374, 0.15706524, 0.08338088, -0.19027525, 0.22595987, -0.054963548, 0.01825031, -0.03149212, 0.025471251, 0.06429379, -0.011633275, -0.079389006, -0.0030728737, 0.17345747) * go_0(-1.0, 0.0);
result += mat4(-0.011275288, -0.10668036, 0.05718997, 0.010336089, 0.33393976, -0.2029354, 0.075444475, -0.092244044, 0.07605498, 0.20125951, 0.10493973, -0.12306946, 0.03658231, 0.08233366, -0.12205888, -0.116969004) * go_0(-1.0, 1.0);
result += mat4(-0.0070305974, 0.105127215, 0.006041873, 0.26743913, 0.028119443, 0.14823505, -0.28344348, 0.12362866, -0.1215781, 0.08104382, 0.102011785, 0.085380934, 0.061244503, -0.06230063, -0.05353345, 0.1166729) * go_0(0.0, -1.0);
result += mat4(0.08945733, 0.4101902, -0.06404005, 0.040728435, 0.13076581, -0.20805469, -0.10897316, -0.14924604, 0.10090762, 0.015475414, 0.26346552, 0.12096677, -0.20199244, 0.2780031, 0.18515368, 0.35105625) * go_0(0.0, 0.0);
result += mat4(0.07463155, 0.26932517, -0.06768551, 0.10470878, -0.1423996, 0.013550665, -0.06167201, -0.1022994, -0.3107166, -0.15609552, 0.1695213, -0.1277181, 0.12582655, -0.1596128, 0.015612055, -0.19826376) * go_0(0.0, 1.0);
result += mat4(0.011745468, 0.006471601, 0.008110513, 0.025831396, 0.1272883, -0.221959, 0.11993834, -0.007903633, 0.009993582, -0.10170755, 0.026594637, -0.027883623, 0.030666083, -0.036415886, 0.007469573, 0.0674783) * go_0(1.0, -1.0);
result += mat4(-0.022760388, -0.10911659, -0.012589904, -0.046462692, 0.36987287, 0.71668935, -0.04466556, 0.12082762, 0.0026539841, 0.07070946, -0.00020439121, -0.13925348, 0.08672072, 0.20075354, -0.066352285, 0.14655356) * go_0(1.0, 0.0);
result += mat4(-0.081081845, -0.21956222, 0.06781787, -0.106362104, -0.03016425, -0.010460211, -0.009725996, -0.009805538, 0.07037355, 0.19254607, 0.038890257, 0.29580075, -0.10355764, 0.12613009, 0.02485986, -0.031927988) * go_0(1.0, 1.0);
result += mat4(-0.13882205, 0.21770848, 0.015392157, 0.010310204, 0.008225721, 0.07457836, 0.09984027, -0.25452816, 0.2193511, -0.22262146, -0.12950355, 0.026151875, 0.022114651, -0.030566849, 0.034688126, 0.03047327) * go_1(-1.0, -1.0);
result += mat4(0.0363441, 0.19290726, -0.1143055, 0.30871987, -0.05780708, 0.082128406, -0.115280904, 0.07636388, 0.48947453, -0.29715258, 0.146737, -0.3275992, -0.055972476, -0.09991753, 0.17435446, 0.10917291) * go_1(-1.0, 0.0);
result += mat4(0.026389305, 0.054523308, -0.028950177, 0.06913328, -0.18626037, 0.08829993, 0.10407121, 0.001246911, 0.103938825, -0.3117343, -0.045564886, 0.07316613, 0.0027089121, 0.099437356, -0.046500806, -0.0927284) * go_1(-1.0, 1.0);
result += mat4(0.051037624, -0.2068234, 0.061572235, -0.3345198, 0.16960172, -0.30289862, -0.002583443, 0.39312238, 0.08246557, 0.16374862, -0.31902805, -0.13205275, -0.032050006, 0.01670186, 0.13852347, 0.120012194) * go_1(0.0, -1.0);
result += mat4(-0.67096996, -0.06274476, 0.18575665, 0.80282855, 0.23201196, -0.0054729837, 0.050396994, -0.42014772, 0.34904522, 0.26281372, 0.24697208, 0.55475426, 0.49850988, -0.06581312, -0.0068906257, -0.15741143) * go_1(0.0, 0.0);
result += mat4(-0.04252036, -0.28224963, 0.009723064, 0.116357096, 0.2992567, -0.26702902, -0.05648925, 0.12729199, -0.37574205, 0.54211813, -0.25248805, -0.13023548, 0.18903324, -0.5182459, 0.0141203115, -0.19444294) * go_1(0.0, 1.0);
result += mat4(-0.0017735233, -0.010132458, -0.040924776, -0.13767008, 0.20757031, -0.06509882, -0.09756446, 0.018974079, 0.090851985, -0.010158765, -0.03999607, -0.12055641, 0.03629025, -0.018645551, -0.05506811, -0.014202848) * go_1(1.0, -1.0);
result += mat4(0.16203491, 0.011118734, -0.18486023, -0.024290733, -0.3673846, -0.20295864, 0.23055002, -0.1555852, -0.02706522, 0.03262891, 0.008724611, -0.03760652, -0.20946771, -0.01951837, 0.16955496, 0.11690098) * go_1(1.0, 0.0);
result += mat4(0.0783421, 0.22656651, -0.15715368, -0.024174158, 0.020260733, 0.032390315, -0.029133298, 0.086601086, 0.13871798, -0.12525433, 0.16097449, 0.058946393, 0.029865682, 0.08508385, 0.040569812, -0.09402932) * go_1(1.0, 1.0);
result += mat4(-0.05063873, 0.11269313, -0.057484943, -0.13579641, 0.047973365, -0.07103839, -0.07838756, -0.0028928046, -0.019466015, 0.018428024, 0.010016324, -0.057396665, -0.19495595, 0.034307264, -0.022888038, 0.08112259) * go_2(-1.0, -1.0);
result += mat4(-0.09790086, 0.10613111, 0.06611674, 0.19356097, -0.00073371036, -0.019078335, 0.076719105, -0.016212497, -0.3283475, -0.07547389, -0.08140701, 0.3185625, -0.25060275, 0.16820994, -0.123497784, 0.43272668) * go_2(-1.0, 0.0);
result += mat4(-0.06365342, 0.11186735, -0.17493224, -0.04207358, 0.0003117533, 0.034089327, -3.067692e-05, -0.03422754, 0.16267666, 0.054771993, 0.048384454, -0.041866794, 0.0036008756, 0.0021496525, 0.20258942, -0.06297619) * go_2(-1.0, 1.0);
result += mat4(0.03578836, 0.08763908, -0.22370125, -0.32465744, 0.019142643, 0.011316954, 0.17920344, 0.031633645, 0.03766343, -0.116487674, -0.05281752, -0.018965483, 0.049297336, -0.34511214, 0.42598158, 0.051361635) * go_2(0.0, -1.0);
result += mat4(0.26638633, -0.33628765, 0.04437907, 0.09616201, -0.020049393, 0.2560829, -0.027108455, 0.255752, 0.3666511, 0.052277412, -0.46667686, 0.48482272, 0.51302284, -0.06941614, -0.17967525, -0.07889891) * go_2(0.0, 0.0);
result += mat4(0.18503937, 0.088710256, 0.2083147, -0.20758459, -0.036416974, 0.018303726, 0.03729963, -0.035969947, -0.2685231, -0.42169708, -0.039593916, -0.02642618, 0.29050872, -0.25723743, -0.111259766, 0.15001127) * go_2(0.0, 1.0);
result += mat4(-0.026473878, -0.07241443, 0.022400148, -0.03214132, 0.0859297, -0.0036677981, -0.07039137, 0.03703108, 0.042322673, -0.01222808, -0.08151938, 0.033109214, -0.048737407, 0.25929528, -0.40535828, -0.123594694) * go_2(1.0, -1.0);
result += mat4(0.10233285, 0.22455986, -0.13368733, 0.033236265, -0.052114893, -0.11709317, 0.009709581, 0.19201641, -0.02973698, 0.032114245, -0.09771862, 0.085680574, 0.15827927, -0.15042172, 0.21833214, -0.13262676) * go_2(1.0, 0.0);
result += mat4(-0.08460587, -0.09473209, 0.019323658, -0.057233352, 0.0019434267, -0.14437936, 0.034232683, 0.0030602294, -0.023598112, 0.10692026, -0.09960999, 0.005887181, 0.014738836, -0.32473162, -0.10886747, -0.08365826) * go_2(1.0, 1.0);
result += mat4(0.10900178, 0.00080280803, -0.14009437, -0.053074867, -0.07811151, -0.03456029, -0.104943685, 0.016918905, -0.11335709, 0.079421654, 0.13481963, 0.037818357, -0.027339859, 0.05856774, -0.044562265, 0.03908084) * go_3(-1.0, -1.0);
result += mat4(0.07628258, -0.23815769, 0.2840278, -0.3541637, -0.044292126, -0.09310441, -0.1335055, -0.031899665, -0.11981227, 0.24012394, -0.041896038, -0.10168982, 0.20248915, -0.10036763, -0.044115108, 0.08520525) * go_3(-1.0, 0.0);
result += mat4(0.07234102, -0.119480744, -0.01401321, -0.025182616, -0.031284854, -0.050089385, 0.014808948, 0.038662236, -0.18539418, 0.017342187, 0.023812262, 0.13428104, 0.020824855, -0.07433546, 0.054307282, 0.08511016) * go_3(-1.0, 1.0);
result += mat4(-0.11046813, -0.04663274, 0.33497185, 0.023273284, -0.24681108, 0.116665915, 0.12045893, 0.13306482, -0.039098527, 0.04747061, 0.042796664, 0.053514794, 0.011861975, -0.048702, 0.008408589, -0.09497112) * go_3(0.0, -1.0);
result += mat4(0.34634927, 0.37973458, -0.79267627, -0.7362719, 0.35489878, -0.07635863, 0.24082923, -0.27480397, -0.3236968, -0.25523046, 0.05118527, -0.040529836, -0.6000509, 0.39020586, 0.27632973, 0.5141453) * go_3(0.0, 0.0);
result += mat4(0.16761221, -0.033125393, 0.00561569, 0.083019435, -0.101278506, 0.07810264, 0.12060661, 0.16048536, 0.14257826, -0.15996903, 0.018831912, -0.094429865, -0.22227801, 0.426937, -0.054677445, 0.05067348) * go_3(0.0, 1.0);
result += mat4(0.02233958, 0.02608942, -0.045318656, 0.06509929, 0.035911568, 0.025316885, 0.0840986, 0.08326237, 0.048455603, -0.13630742, 0.07230253, -0.047261715, -0.092630014, 0.04786565, 0.10354939, -0.07094341) * go_3(1.0, -1.0);
result += mat4(-0.1463382, -0.14900577, 0.2835977, -0.106733374, -0.11554754, -0.168429, -0.1411373, -0.20654152, -0.06388508, 0.039648015, 0.08543832, -0.13253337, 0.017264463, -0.06346233, -0.10823598, 0.067361064) * go_3(1.0, 0.0);
result += mat4(0.04419582, 0.039152585, 0.06222691, 0.05757103, 0.012084537, 0.051425997, -0.061130576, 0.16752882, 0.07497411, 0.13495837, -0.15585983, -0.02050144, -0.08555421, -0.09147339, 0.025115604, 0.05948922) * go_3(1.0, 1.0);
result += vec4(0.00590038, 0.03082865, 0.002111702, -0.03330112);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_5_tf
//!BIND conv2d_5_tf1
//!SAVE conv2d_6_tf1
//!WIDTH conv2d_5_tf.w
//!HEIGHT conv2d_5_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_5_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_5_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.009029573, 0.029218858, 0.029705316, -0.019268971, -0.0023235187, -0.072589695, 0.1424836, 0.09049359, 0.04342995, 0.18134294, 0.018145641, 0.14789368, 0.050923645, 0.06524081, 0.036812488, 0.11108108) * go_0(-1.0, -1.0);
result += mat4(-0.026506428, 0.016968496, 0.015961196, 0.010030791, -0.3141888, -0.06769598, -0.23920257, -0.031002127, -0.07351358, -0.19290134, -0.24282931, -0.18831016, -0.0928966, 0.075177215, -0.19699521, -0.05810917) * go_0(-1.0, 0.0);
result += mat4(-0.017991852, -0.079427645, 0.035970494, -0.017095685, -0.27197137, -0.20046075, 0.2616644, 0.021876303, -0.077394076, -0.04978692, 0.20363241, -0.013741705, -0.032103598, 0.14403099, 0.01442474, 0.048115995) * go_0(-1.0, 1.0);
result += mat4(-0.16939245, -0.001777, 0.026244136, -0.14122388, -0.056853324, 0.54357284, -0.19769607, -0.03187079, 0.04559263, -0.16048127, 0.12830622, 0.1442168, 0.006611398, -0.01618195, 0.012860053, -0.16539487) * go_0(0.0, -1.0);
result += mat4(0.13116026, -0.006161343, 0.7209969, 0.18338475, 0.3099777, 0.6500026, 0.3883795, -0.021434233, 0.31667513, 0.008917659, 0.14124091, -0.22335114, 0.12198921, -0.16449445, 0.08773425, 0.30054978) * go_0(0.0, 0.0);
result += mat4(-0.10413989, -0.10316161, 0.04342709, -0.021252686, 0.120892406, 0.37798002, -0.35963747, 0.021069285, 0.37587845, -0.08159587, 0.011139747, 0.2501104, -0.094568014, 0.037900843, -0.025109999, -0.030106556) * go_0(0.0, 1.0);
result += mat4(0.09680291, -0.040868275, 0.051731605, 0.089064725, -0.56098557, -0.38148618, -0.017037416, 0.08508287, -0.019247344, 0.019857002, -0.03512887, 0.031057188, -0.09648583, -0.04474188, 0.028748507, -0.11880965) * go_0(1.0, -1.0);
result += mat4(-0.010236943, 0.04257042, -0.08202597, -0.004203426, -0.26801527, -0.11716526, -0.017402772, -0.05819106, -0.13394608, 0.0234606, -0.15404865, -0.06801164, -0.0047627664, -0.1975249, 0.09420144, 0.23249897) * go_0(1.0, 0.0);
result += mat4(0.107361935, 0.07373787, 0.06242962, 0.05236332, -0.028867323, 0.025924044, -0.042526353, -0.0015729597, -0.1323144, -0.4040712, 0.023919407, -0.09535502, 0.049100045, 0.081110805, 0.08946112, 0.058505684) * go_0(1.0, 1.0);
result += mat4(0.13236825, -0.04468476, -0.04426802, 0.031087106, -0.09093992, -0.07470971, -0.01591504, 0.05924266, -0.21910913, 0.065537, -0.18358919, -0.02533145, -0.1512009, -0.04953928, 0.015540006, -0.0043442883) * go_1(-1.0, -1.0);
result += mat4(-0.14016777, -0.1086958, 0.16316028, 0.050777458, 0.23148167, 0.04944809, -0.10599886, -0.10447021, -0.40729257, -0.10926556, 0.069055155, 0.110635415, 0.108922414, -0.1716362, 0.10743909, -0.102534756) * go_1(-1.0, 0.0);
result += mat4(0.017795928, -0.066930935, 0.09396082, 0.092585504, 0.14223933, 0.059458215, 0.072033696, -0.04507726, -0.19956456, 0.1251282, -0.31733638, -0.10465904, 0.08546377, 0.048638333, 0.031372465, -0.08720661) * go_1(-1.0, 1.0);
result += mat4(0.108719654, -0.092161916, -0.014724377, 0.20068261, -0.24350016, 0.2113636, -0.07483714, -0.45665312, -0.25134233, 0.2753893, -0.11324696, -0.04472, 0.1576102, -0.045395147, 0.06013951, -0.12507361) * go_1(0.0, -1.0);
result += mat4(0.546225, -0.281897, 0.19477816, -0.116612464, -0.3145171, -0.41660902, 0.333625, 0.35902345, 0.48333502, 0.4662005, 0.10222491, -0.15314859, -0.3036888, 0.22849742, 0.20740797, 0.41399437) * go_1(0.0, 0.0);
result += mat4(0.007284074, 0.0393942, -0.31192186, -0.15687793, -0.289214, -0.015956698, -0.24718472, -0.1637855, -0.00765037, 0.26677555, 0.20215511, 0.37790874, -0.22096673, 0.25287116, -0.2446764, -0.13610223) * go_1(0.0, 1.0);
result += mat4(-0.16734968, 0.16721225, -0.053508647, -0.041097626, 0.062356673, 0.07812319, -0.263546, -0.39739034, 0.003389846, 0.12676363, -0.13175991, -0.19019242, -0.011847587, -0.007580052, -0.023946386, 0.046034034) * go_1(1.0, -1.0);
result += mat4(-0.17047611, 0.13298693, -0.07506747, -0.045542978, 0.33571973, 0.20192616, 0.30674616, 0.25668672, -0.24134545, 0.031693842, -0.009647641, 0.040534843, 0.03159419, -0.1100516, 0.11371316, 0.06098735) * go_1(1.0, 0.0);
result += mat4(-0.05518961, 0.19402988, -0.09646874, -0.059196774, -0.0073436056, -0.1381309, 0.06868669, 0.061328378, -0.1480867, -0.15774113, -0.022572191, 0.122521356, -0.04067007, -0.10145177, 0.13006335, -0.099452734) * go_1(1.0, 1.0);
result += mat4(0.06962972, 0.07768411, 0.021085173, 0.108355984, -0.03132525, 0.10220273, -0.11626593, -0.14104277, 0.018778645, -0.024237925, 0.048783034, 0.09074447, 0.4120426, -0.01948466, 0.073218934, 0.055681944) * go_2(-1.0, -1.0);
result += mat4(-0.22553118, -0.12923603, -0.22068842, -0.35037905, 0.005709937, -0.09528472, 0.08718399, 0.13200706, 0.17220478, 0.096844435, -0.30439013, -0.14122063, 0.15733318, -0.1014675, 0.33836862, 0.042193163) * go_2(-1.0, 0.0);
result += mat4(0.15826897, -0.034870047, 0.09295099, -0.17674965, -0.042326324, 0.06680338, -0.074267656, -0.0631393, -0.11267909, -0.19795708, 0.22005288, 0.35703793, 0.033995766, -0.12663686, -0.02449896, -0.123250045) * go_2(-1.0, 1.0);
result += mat4(0.021434195, 0.058398597, 0.04828315, -0.0016824572, -0.04291545, -0.0744907, -0.07698706, -0.15937585, -0.18852457, -0.17966963, 0.023800725, 0.025979731, -0.51412296, -0.018316887, -0.23076254, -0.12298674) * go_2(0.0, -1.0);
result += mat4(0.16054317, -0.0002730893, -0.54173076, -0.62443435, 0.04300197, -0.08529622, 0.15392275, 0.15742144, 0.025834514, -0.2800517, -0.17600477, 0.0020806703, -0.3010582, 0.45233512, 0.25595665, 0.103661336) * go_2(0.0, 0.0);
result += mat4(-0.024034392, -0.43800178, 0.28606912, -0.20908915, 0.078471914, -0.030501373, -0.059055753, 0.050494444, 0.063274644, -0.025071034, 0.17561312, -0.100698635, -0.25631955, 0.039981876, -0.18506624, 0.08366402) * go_2(0.0, 1.0);
result += mat4(-0.1413656, 0.03589635, -0.020917566, 0.017598262, 0.020156413, -0.018854238, 0.027228508, -0.03806087, -0.021715842, 0.071974196, -0.040065665, 0.08459291, -0.23530225, 0.16599682, -0.2772327, 0.10041177) * go_2(1.0, -1.0);
result += mat4(-0.055056706, 0.1286236, -0.11890451, -0.1790546, 0.16517544, -0.040448934, 0.12548013, 0.017075695, 0.07185459, -0.13236302, 0.19354409, 0.12767012, 0.31120765, 0.16378082, -0.036915366, -0.19724306) * go_2(1.0, 0.0);
result += mat4(-0.02225051, 0.033263147, 0.003279449, 0.08826271, -0.047833472, 6.574577e-05, 0.13721916, 0.04801998, -0.014958419, 0.08791209, -0.08076282, 0.024002168, -0.18028922, 0.23835851, -0.23309888, -0.119310364) * go_2(1.0, 1.0);
result += mat4(0.044960875, 0.18821983, 0.027640678, 0.013462449, 0.19011214, 0.21559924, -0.03329638, 0.07234414, 0.030880248, -0.11273214, 0.102028474, 0.12203351, 0.035855662, 0.008828778, 0.007218363, -0.012421797) * go_3(-1.0, -1.0);
result += mat4(-0.09450626, 0.025191775, -0.10738468, 0.16237053, 0.073676676, 0.12488881, -0.048748355, 0.007877263, 0.3572506, -0.07911043, 0.14684045, 0.0015310893, -0.33411503, -0.1151223, 0.004201752, 0.017775744) * go_3(-1.0, 0.0);
result += mat4(-0.10607509, -0.008143826, -0.08448629, -0.27557802, 0.0046665915, 0.008158659, 0.030826218, 0.020516023, 0.2333065, -0.017463414, -0.041772116, -0.03027809, -0.028166672, -0.080471426, 0.048199337, 0.08341059) * go_3(-1.0, 1.0);
result += mat4(-0.14640257, -0.18334304, -0.061674733, 0.0008892598, -0.2374775, -0.2721524, -0.040371176, 0.26362613, 0.19872928, -0.11246391, 0.0842288, 0.11188515, 0.0045209546, -0.04250933, -0.0738212, -0.069005966) * go_3(0.0, -1.0);
result += mat4(-0.08760266, 0.4816288, -0.21241407, 0.22734411, -0.1783721, -0.26842996, 0.099888, -0.2867675, 0.085521065, -0.3780281, -0.018543908, -0.039699722, 0.75688565, -0.5333645, 0.47567275, 0.09518891) * go_3(0.0, 0.0);
result += mat4(-0.04072665, 0.05998423, -0.48314768, -0.29495844, 0.10358383, -0.09816629, 0.028586809, -0.047708735, 0.008320228, 0.04089551, -0.18359782, -0.27615002, 0.12414414, -0.072417594, 0.25932562, 0.30268723) * go_3(0.0, 1.0);
result += mat4(0.14481631, 0.06484443, -0.09898657, -0.06553556, 0.25750044, -0.07265585, 0.12903488, -0.022347894, -0.04693863, -0.000107379274, 0.030295763, -0.0325354, 0.086214684, -0.021326948, 0.039682828, -0.034843277) * go_3(1.0, -1.0);
result += mat4(-0.031971477, -0.25145087, 0.03931631, 0.14262606, -0.06044626, 0.22820354, -0.10506207, 0.18064679, 0.0069641788, 0.01477993, -0.003626875, 0.118767865, 0.109416224, -0.002998205, 0.035680585, 0.07843882) * go_3(1.0, 0.0);
result += mat4(0.03375426, -0.059815384, 0.11632834, -0.12411481, 0.022583738, 0.02544465, -0.054889992, -0.07031964, -0.10140042, 0.16750422, -0.1448294, -0.09316004, 0.035582513, -0.026138382, -0.031955894, 0.040148776) * go_3(1.0, 1.0);
result += vec4(-0.03573331, 0.032919675, 0.011109369, 0.008329268);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(VL)-Conv-4x1x1x112
//!HOOK MAIN
//!BIND conv2d_tf
//!BIND conv2d_tf1
//!BIND conv2d_1_tf
//!BIND conv2d_1_tf1
//!BIND conv2d_2_tf
//!BIND conv2d_2_tf1
//!BIND conv2d_3_tf
//!BIND conv2d_3_tf1
//!BIND conv2d_4_tf
//!BIND conv2d_4_tf1
//!BIND conv2d_5_tf
//!BIND conv2d_5_tf1
//!BIND conv2d_6_tf
//!BIND conv2d_6_tf1
//!SAVE conv2d_last_tf
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define g_0 (max((conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_1 (max((conv2d_tf1_tex(conv2d_tf1_pos)), 0.0))
#define g_2 (max(-(conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_3 (max(-(conv2d_tf1_tex(conv2d_tf1_pos)), 0.0))
#define g_4 (max((conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_5 (max((conv2d_1_tf1_tex(conv2d_1_tf1_pos)), 0.0))
#define g_6 (max(-(conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_7 (max(-(conv2d_1_tf1_tex(conv2d_1_tf1_pos)), 0.0))
#define g_8 (max((conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_9 (max((conv2d_2_tf1_tex(conv2d_2_tf1_pos)), 0.0))
#define g_10 (max(-(conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_11 (max(-(conv2d_2_tf1_tex(conv2d_2_tf1_pos)), 0.0))
#define g_12 (max((conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_13 (max((conv2d_3_tf1_tex(conv2d_3_tf1_pos)), 0.0))
#define g_14 (max(-(conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_15 (max(-(conv2d_3_tf1_tex(conv2d_3_tf1_pos)), 0.0))
#define g_16 (max((conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_17 (max((conv2d_4_tf1_tex(conv2d_4_tf1_pos)), 0.0))
#define g_18 (max(-(conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_19 (max(-(conv2d_4_tf1_tex(conv2d_4_tf1_pos)), 0.0))
#define g_20 (max((conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_21 (max((conv2d_5_tf1_tex(conv2d_5_tf1_pos)), 0.0))
#define g_22 (max(-(conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_23 (max(-(conv2d_5_tf1_tex(conv2d_5_tf1_pos)), 0.0))
#define g_24 (max((conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_25 (max((conv2d_6_tf1_tex(conv2d_6_tf1_pos)), 0.0))
#define g_26 (max(-(conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_27 (max(-(conv2d_6_tf1_tex(conv2d_6_tf1_pos)), 0.0))
vec4 hook() {
vec4 result = mat4(-0.11498094, -0.053904895, -0.11520678, -0.05479549, 0.028396055, 0.032767884, 0.052479446, 0.05257866, -0.25706592, -0.3454966, -0.24713765, -0.2854201, -0.10287636, 0.0023146886, -0.09190338, -0.011193905) * g_0;
result += mat4(-0.05461422, 0.008780496, -0.07738697, -0.032230727, -0.047554165, -0.025061952, -0.051897213, -0.009545297, -0.14548294, -0.15184018, -0.01313442, -0.015299784, -0.0007883845, -0.12866738, -0.15260352, -0.27081275) * g_1;
result += mat4(0.11007706, 0.035344437, 0.11020841, 0.0425353, 0.1613199, 0.18417408, 0.09274313, 0.11943135, 0.106862, 0.079875536, 0.0937752, 0.068030775, 0.029093558, -0.06441164, 0.06467169, -0.021989612) * g_2;
result += mat4(0.049548414, -0.012455486, 0.07185561, 0.021865537, 0.020969186, -0.03374196, -0.024260623, -0.07739141, 0.07164591, 0.12741035, 0.0379913, 0.076403245, 0.07049977, 0.0744538, 0.0062989634, 0.01818882) * g_3;
result += mat4(-0.12511204, -0.010836819, 0.13709816, 0.22472954, 0.21280868, -0.006484726, 0.17554289, -0.009977173, 0.078398876, 0.20698707, 0.13432744, 0.29740283, -0.24750128, -0.32757792, -0.19807857, -0.2537023) * g_4;
result += mat4(-0.27207088, -0.1385644, -0.2166476, -0.07687419, -0.20300622, -0.29678395, -0.13135734, -0.20851587, 0.0361364, 0.011243289, -0.06845459, -0.11796941, 0.11575868, 0.070215136, -0.10295678, -0.12281369) * g_5;
result += mat4(0.13619795, -0.0019436983, -0.12701888, -0.25933513, -0.20134166, 0.00062823144, -0.076756015, 0.11002947, 0.0059049693, -0.18756741, -0.0718802, -0.2589954, 0.23413423, 0.30107784, 0.14445266, 0.18920745) * g_6;
result += mat4(0.1494216, 0.0587532, 0.05478662, -0.039123338, 0.23322394, 0.29950607, 0.24384268, 0.27843767, -0.16094431, -0.04705998, -0.016345032, 0.028868208, -0.102872886, -0.04659664, 0.104105346, 0.14305067) * g_7;
result += mat4(-0.001037014, 0.010001526, -0.0052278573, 0.024779709, 0.06857274, 0.067640975, 0.085439384, 0.09242789, -0.066597246, -0.055928994, 0.0015658981, 0.016131008, -0.03524695, -0.018364554, -0.047754433, -0.014295886) * g_8;
result += mat4(-0.042207, 0.02835915, -0.1404656, -0.08563323, -0.030979915, -0.0673764, 0.10733943, 0.057902794, 0.00022424995, -0.0023634837, -0.10778953, -0.10202357, -0.020368295, -0.019088887, -0.06875738, -0.08504131) * g_9;
result += mat4(-0.00043458896, 0.00045652856, -0.02016843, -0.020062413, -0.08740103, -0.042085808, -0.10644177, -0.09226477, 0.11212161, -0.00048174805, 0.021872435, -0.05868698, 0.0333954, 0.058184672, 0.05532576, 0.07621587) * g_10;
result += mat4(0.054245148, 0.001020329, 0.09106849, 0.05303779, 0.009889632, 0.01309413, -0.09187347, -0.08618193, -0.011621187, 0.016222361, 0.061095525, 0.060885344, 0.078050986, 0.0111776795, 0.08829944, 0.032022282) * g_11;
result += mat4(0.01643529, 0.02285545, -0.03498564, 0.00769657, -0.0042474116, 0.015836312, -0.025771018, -0.0016368, -0.008897948, -0.012588166, -0.01416411, -0.003578984, 0.025991246, 0.021237152, 0.017450012, 0.025172485) * g_12;
result += mat4(0.014568868, 0.017796224, -0.036679734, -0.03138748, 0.019457601, -0.027607411, -0.004529679, -0.038048342, -0.054055385, -0.03876025, 0.041948095, 0.005869784, 0.02439633, 0.05177997, 0.016000897, 0.0057169925) * g_13;
result += mat4(-0.03021866, 0.017678728, -0.01371109, 0.013548159, -0.0038099394, -0.014066414, 0.028093752, 0.0027308422, -0.010615999, 0.012673458, -0.03028171, -0.016818244, -0.06530097, -0.018845048, -0.0072947564, -0.0038243714) * g_14;
result += mat4(-0.019006258, -0.007847591, 0.03690709, 0.06714211, 0.0073993434, -0.009766907, -0.0021441753, -0.01308625, 0.06658726, 0.06701995, -0.027305668, -0.016032105, -0.028976806, -0.0036668575, -0.0027825525, 0.0105632655) * g_15;
result += mat4(0.028945107, -0.0014701135, 0.048950657, -0.01923516, -0.0014054152, 0.002650635, -0.005300331, 0.004860559, 0.011158468, 0.005940625, -0.012095051, 0.0041518128, -0.020433836, -0.025870577, -0.0007547932, -0.026509356) * g_16;
result += mat4(-0.004545374, 0.04264545, 0.021741537, 0.029115127, 0.04225599, -0.0055392785, 0.026570829, -0.031795148, -0.008307126, 0.020176455, 0.010904648, 0.017765503, -0.10806103, -0.01776947, 0.00070428237, -0.06356262) * g_17;
result += mat4(-0.05663172, 0.05908046, -0.03837452, 0.06636983, -0.007960516, -0.06384041, 0.023125881, -0.030108837, 0.0038054318, -0.023263922, 0.020264054, -0.0062937695, 0.031630237, 0.020909082, 0.03594235, 0.035879835) * g_18;
result += mat4(-0.0050448794, 0.033650696, -0.002830413, 0.035174295, -0.024521282, 0.013054315, -0.020833842, 0.037953895, 0.08249671, 0.024239466, -0.012758333, -0.027316988, 0.051040914, 0.0005025873, 0.039778862, 0.0024668393) * g_19;
result += mat4(0.017232442, 0.022482058, 0.020233413, 0.024337437, 0.07986929, 0.06234036, 0.12662584, -0.05271183, -0.009718745, -0.0046989853, -0.0030333172, -0.04034237, -0.0113442, 0.022746231, -0.035293855, -0.009433693) * g_20;
result += mat4(0.015766997, 0.013647276, -0.029327558, 0.039106004, -0.010398323, -0.032851525, 0.02908329, -0.003789618, 0.12963496, 0.010851003, 0.1126276, -0.049255487, 0.06867432, 0.07970792, 0.017840397, -0.026481882) * g_21;
result += mat4(-0.058729574, -0.07886952, 0.033267397, 0.02755372, -0.0172006, 0.012404398, -0.0230168, -0.015059758, -0.09239916, -0.029533267, -0.043251917, 0.0035152994, 0.022931995, 0.101714484, -0.044946067, 0.094993) * g_22;
result += mat4(-0.04708704, -0.032475296, -0.03228093, -0.08810475, 0.013745045, 0.027828002, -0.031922746, 0.022986397, -0.061620213, -0.03694645, -0.055026993, 0.0031291894, -0.028799903, -0.0025357977, -0.03441407, 0.0028600092) * g_23;
result += mat4(0.058981724, -0.10447273, -0.088705614, 0.16546178, -0.023549391, -0.008831522, -0.018411588, 0.029640056, -0.068086684, -0.05414636, -0.029401174, 0.036180343, -0.031988926, -0.047249753, 0.008162177, 0.00548062) * g_24;
result += mat4(0.05287462, -0.030657746, 0.02821435, 0.037005343, 0.03534311, -0.15614955, 0.07085459, -0.11997641, -0.009156166, -0.021968868, -0.054147746, -0.07307657, -0.006428544, -0.017528288, 0.012614676, 0.037840024) * g_25;
result += mat4(-0.021977803, 0.047799855, 0.02660416, -0.07292106, 0.045195807, -0.0056674764, 0.10824326, -0.112114795, 0.1447127, -0.0119616175, 0.0011661504, -0.04553905, 0.13048342, 0.14574122, -0.105522245, -0.102792375) * g_26;
result += mat4(-0.16397473, 0.15785863, -0.06666504, -0.01682913, 0.06070918, 0.070222184, 0.037701584, 0.026657054, -0.0835267, -0.009457008, 0.13232987, 0.13508691, -0.056414206, -0.06818828, 0.079076104, 0.032249212) * g_27;
result += vec4(-0.10795144, -0.09953324, -0.055413827, -0.03875493);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(VL)-Conv-4x1x1x112
//!HOOK MAIN
//!BIND conv2d_tf
//!BIND conv2d_tf1
//!BIND conv2d_1_tf
//!BIND conv2d_1_tf1
//!BIND conv2d_2_tf
//!BIND conv2d_2_tf1
//!BIND conv2d_3_tf
//!BIND conv2d_3_tf1
//!BIND conv2d_4_tf
//!BIND conv2d_4_tf1
//!BIND conv2d_5_tf
//!BIND conv2d_5_tf1
//!BIND conv2d_6_tf
//!BIND conv2d_6_tf1
//!SAVE conv2d_last_tf1
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define g_0 (max((conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_1 (max((conv2d_tf1_tex(conv2d_tf1_pos)), 0.0))
#define g_2 (max(-(conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_3 (max(-(conv2d_tf1_tex(conv2d_tf1_pos)), 0.0))
#define g_4 (max((conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_5 (max((conv2d_1_tf1_tex(conv2d_1_tf1_pos)), 0.0))
#define g_6 (max(-(conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_7 (max(-(conv2d_1_tf1_tex(conv2d_1_tf1_pos)), 0.0))
#define g_8 (max((conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_9 (max((conv2d_2_tf1_tex(conv2d_2_tf1_pos)), 0.0))
#define g_10 (max(-(conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_11 (max(-(conv2d_2_tf1_tex(conv2d_2_tf1_pos)), 0.0))
#define g_12 (max((conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_13 (max((conv2d_3_tf1_tex(conv2d_3_tf1_pos)), 0.0))
#define g_14 (max(-(conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_15 (max(-(conv2d_3_tf1_tex(conv2d_3_tf1_pos)), 0.0))
#define g_16 (max((conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_17 (max((conv2d_4_tf1_tex(conv2d_4_tf1_pos)), 0.0))
#define g_18 (max(-(conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_19 (max(-(conv2d_4_tf1_tex(conv2d_4_tf1_pos)), 0.0))
#define g_20 (max((conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_21 (max((conv2d_5_tf1_tex(conv2d_5_tf1_pos)), 0.0))
#define g_22 (max(-(conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_23 (max(-(conv2d_5_tf1_tex(conv2d_5_tf1_pos)), 0.0))
#define g_24 (max((conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_25 (max((conv2d_6_tf1_tex(conv2d_6_tf1_pos)), 0.0))
#define g_26 (max(-(conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_27 (max(-(conv2d_6_tf1_tex(conv2d_6_tf1_pos)), 0.0))
vec4 hook() {
vec4 result = mat4(0.024905335, -0.0020974763, 0.02695263, 0.00016802056, -0.024053082, -0.02133723, -0.031614035, -0.031826317, 0.120421864, 0.10555479, 0.08609448, 0.116875134, 0.046175968, 0.04224941, 0.059216674, 0.035143953) * g_0;
result += mat4(0.059397914, 0.016519934, 0.07189327, 0.047407165, 0.04808963, 0.02792908, 0.057017103, 0.034324065, 0.14228246, 0.11275426, 0.088058695, 0.059600517, 0.02063494, 0.052596953, 0.047207687, 0.08789091) * g_1;
result += mat4(-0.013453174, 0.008474715, -0.017593835, 0.009218917, 0.070580654, 0.040542338, 0.08812338, 0.074653216, -0.016356857, 0.015809007, -0.008739107, 0.0097674895, -0.018381525, -0.007775341, -0.040571664, -0.011188163) * g_2;
result += mat4(-0.026196122, -0.034825727, -0.042998232, -0.033436514, -0.01678153, -0.004592797, -0.010311677, 0.0008815291, -0.08899181, -0.10274026, -0.066960976, -0.082430154, -0.057137426, -0.07554528, -0.030993424, -0.050372377) * g_3;
result += mat4(0.022921838, -0.010479244, -0.050794605, -0.073633075, -0.053708922, 0.009594084, -0.071259, -0.01054356, 0.005165821, -0.08024963, -0.049251772, -0.09581235, 0.17995799, 0.09743011, 0.13533138, 0.11643848) * g_4;
result += mat4(0.09727046, 0.07292666, 0.06820908, 0.041535784, -0.0049705, 0.0048759184, -0.035702795, -0.015944308, -0.010730028, 0.018847652, 0.06466244, 0.086318985, -0.05661574, -0.040698618, 0.010839972, 0.0027009705) * g_5;
result += mat4(-0.04628466, 0.010060396, 0.02609333, 0.08664702, 0.057045907, 0.033591177, 0.02186063, -0.024303377, 0.006569828, 0.08025825, 0.016128821, 0.10180713, -0.12228169, -0.112990454, -0.078443415, -0.09126021) * g_6;
result += mat4(-0.12733299, -0.087755, -0.07374111, -0.044979006, -0.025347412, -0.004083168, 0.023782173, 0.02900392, -0.017815407, -0.041119996, -0.057978686, -0.13521095, 0.08364004, 0.06950181, 0.023554614, 0.008043734) * g_7;
result += mat4(0.009062775, -0.003570175, -0.007378757, -0.0018487388, 0.01145638, 0.05217187, -0.008250244, 0.008433307, -0.056756936, -0.044681005, -0.08096105, -0.08033185, -0.023784965, -0.01859799, 0.013042476, 0.021188647) * g_8;
result += mat4(-0.0071619656, -0.012498299, -0.05144986, -0.078112476, -0.034992415, -0.017038302, -0.04464615, -0.044504963, 0.024249, -0.004297534, 0.03674578, 0.03090718, 0.04698553, 0.008344952, 0.057619847, -0.0338724) * g_9;
result += mat4(-0.011845145, -0.0045043705, -1.6646482e-06, -0.0038495932, -0.01992515, 0.004827126, 0.019493148, 0.00862289, 0.10151322, 0.0021909082, 0.09940764, 0.03728846, 0.027824005, 0.04358071, 0.014909185, 0.036326095) * g_10;
result += mat4(0.022513246, 0.028257169, 0.0102195935, 0.03301329, 0.052253865, -0.0021944977, 0.08247392, 0.03256867, -0.040685873, -0.0052207555, -0.0451257, -0.054165114, 0.01647699, 0.0028809097, -0.015233776, -0.0008741886) * g_11;
result += mat4(0.017371105, 0.01597189, -0.052552313, -0.008554715, -0.0023150423, 0.006076517, -0.012868931, 0.0039361073, -0.007524978, -0.004284313, -0.021520883, -0.010327569, 0.02543678, 0.008725823, -0.0073885336, 0.005528395) * g_12;
result += mat4(0.019192757, 0.016561812, 0.0027538154, 0.0013078215, 0.007916496, -0.042525183, -0.013173432, -0.05265476, -0.062195376, -0.011255499, 0.020898128, 0.021532273, -0.001524097, 0.034835674, -0.004051403, -0.0292426) * g_13;
result += mat4(-0.049191684, -9.43322e-06, -0.009106849, 0.012845289, -0.019482708, -0.011163468, 0.0034011535, -0.007062845, -0.006469714, 0.03177786, -0.033006195, -0.0006813464, -0.053963087, 0.00085209147, 0.02734121, 0.034086403) * g_14;
result += mat4(-0.03232248, -0.004037002, -0.010319106, 0.030889064, 0.019604538, 0.0020888883, 0.010277864, 0.000661223, 0.057915937, 0.030683514, 0.00042533095, -0.013019287, -0.015896408, 0.0038484468, -0.0042103594, 0.02174542) * g_15;
result += mat4(0.032975145, 0.0011456647, 0.04913679, -0.017063798, 0.0117176045, 0.007440557, 0.0020480808, 0.009415731, 0.027573857, 0.015140836, -0.01679426, -0.006124731, -0.03206279, -0.029842237, -0.010428016, -0.028513178) * g_16;
result += mat4(-0.00506859, 0.055869613, 0.010164368, 0.027031485, 0.042289548, -0.0054258504, 0.032214936, -0.029970925, -0.0058315448, 0.022889478, 0.01681123, 0.02985076, -0.111186065, -0.02202099, 0.0030994313, -0.062343158) * g_17;
result += mat4(-0.060951103, 0.06079555, -0.0396464, 0.070911355, -0.011480358, -0.06803282, 0.01637355, -0.043100975, -0.00423709, -0.028337711, 0.021635853, 0.0014857082, 0.030084312, 0.018155476, 0.043694943, 0.038795974) * g_18;
result += mat4(-0.0060662925, 0.029721662, -0.008117774, 0.034551267, -0.024477571, 0.018841071, -0.027095588, 0.034495078, 0.082398005, 0.008998768, -0.016399248, -0.043801688, 0.05936684, 0.006066549, 0.045399766, 3.5319943e-05) * g_19;
result += mat4(0.019259382, 0.02494012, 0.029301709, 0.028329274, 0.09122267, 0.06900443, 0.1412115, -0.043169618, -0.01627418, -0.004989528, -0.0042651827, -0.04556752, -0.023623291, 0.013007996, -0.04483056, -0.015727345) * g_20;
result += mat4(0.016332543, 0.016384754, -0.030676385, 0.045312885, -0.0100853555, -0.032632045, 0.031514473, -0.0070776115, 0.13642761, 0.0023589598, 0.12214136, -0.062155515, 0.08240989, 0.08894205, 0.03325406, -0.016589595) * g_21;
result += mat4(-0.06494277, -0.08158925, 0.030425413, 0.019835634, -0.012624623, 0.013942616, -0.030527417, -0.021668324, -0.09444672, -0.033064254, -0.044167448, 0.0011024752, 0.03210801, 0.12662941, -0.03912534, 0.1112649) * g_22;
result += mat4(-0.04716062, -0.03751481, -0.031030515, -0.09067383, 0.0077815712, 0.02169541, -0.035285182, 0.02290573, -0.0704085, -0.03916127, -0.058103334, 0.004915147, -0.0333844, -0.011548617, -0.031151932, -0.00043817286) * g_23;
result += mat4(0.05976319, -0.107285, -0.097245865, 0.17706421, -0.021453341, -0.0047738464, -0.017621001, 0.033400454, -0.07225561, -0.05599672, -0.027600193, 0.038664024, -0.03762786, -0.052429967, 0.0104017975, 0.007116869) * g_24;
result += mat4(0.06014114, -0.029824806, 0.03209269, 0.04392036, 0.031300627, -0.16249833, 0.06878509, -0.12658615, -0.012383169, -0.025043553, -0.06527381, -0.08149099, -0.014006842, -0.018669648, 0.014510818, 0.042045828) * g_25;
result += mat4(-0.023342922, 0.047104675, 0.029629575, -0.082307704, 0.04035797, -0.0013049254, 0.11085582, -0.11031226, 0.14778149, -0.016699014, -0.00634342, -0.055320874, 0.14306462, 0.15896587, -0.110229075, -0.1069649) * g_26;
result += mat4(-0.17449625, 0.15787153, -0.06711028, -0.023110518, 0.06862914, 0.074063435, 0.042682912, 0.029800726, -0.08768606, -0.009814701, 0.14180017, 0.14780663, -0.05672417, -0.074305914, 0.07873489, 0.028458012) * g_27;
result += vec4(0.06026231, 0.040204916, 0.037672628, 0.023496555);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(VL)-Conv-4x1x1x112
//!HOOK MAIN
//!BIND conv2d_tf
//!BIND conv2d_tf1
//!BIND conv2d_1_tf
//!BIND conv2d_1_tf1
//!BIND conv2d_2_tf
//!BIND conv2d_2_tf1
//!BIND conv2d_3_tf
//!BIND conv2d_3_tf1
//!BIND conv2d_4_tf
//!BIND conv2d_4_tf1
//!BIND conv2d_5_tf
//!BIND conv2d_5_tf1
//!BIND conv2d_6_tf
//!BIND conv2d_6_tf1
//!SAVE conv2d_last_tf2
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define g_0 (max((conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_1 (max((conv2d_tf1_tex(conv2d_tf1_pos)), 0.0))
#define g_2 (max(-(conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_3 (max(-(conv2d_tf1_tex(conv2d_tf1_pos)), 0.0))
#define g_4 (max((conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_5 (max((conv2d_1_tf1_tex(conv2d_1_tf1_pos)), 0.0))
#define g_6 (max(-(conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_7 (max(-(conv2d_1_tf1_tex(conv2d_1_tf1_pos)), 0.0))
#define g_8 (max((conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_9 (max((conv2d_2_tf1_tex(conv2d_2_tf1_pos)), 0.0))
#define g_10 (max(-(conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_11 (max(-(conv2d_2_tf1_tex(conv2d_2_tf1_pos)), 0.0))
#define g_12 (max((conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_13 (max((conv2d_3_tf1_tex(conv2d_3_tf1_pos)), 0.0))
#define g_14 (max(-(conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_15 (max(-(conv2d_3_tf1_tex(conv2d_3_tf1_pos)), 0.0))
#define g_16 (max((conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_17 (max((conv2d_4_tf1_tex(conv2d_4_tf1_pos)), 0.0))
#define g_18 (max(-(conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_19 (max(-(conv2d_4_tf1_tex(conv2d_4_tf1_pos)), 0.0))
#define g_20 (max((conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_21 (max((conv2d_5_tf1_tex(conv2d_5_tf1_pos)), 0.0))
#define g_22 (max(-(conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_23 (max(-(conv2d_5_tf1_tex(conv2d_5_tf1_pos)), 0.0))
#define g_24 (max((conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_25 (max((conv2d_6_tf1_tex(conv2d_6_tf1_pos)), 0.0))
#define g_26 (max(-(conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_27 (max(-(conv2d_6_tf1_tex(conv2d_6_tf1_pos)), 0.0))
vec4 hook() {
vec4 result = mat4(0.1765669, 0.14268716, 0.19186598, 0.15799578, 0.016374417, 0.018578433, 0.0039475, 0.0046772263, 0.39840183, 0.36909792, 0.35409746, 0.37422222, -0.108508386, -0.1331279, -0.10336035, -0.14776541) * g_0;
result += mat4(-0.057757027, -0.14071062, -0.025283009, -0.09397916, -0.09031894, -0.14219165, -0.08299535, -0.13970287, -0.12259208, -0.14382727, -0.22002274, -0.25016093, -0.048906635, 0.06620249, 0.016965045, 0.1295978) * g_1;
result += mat4(-0.16748372, -0.13718611, -0.18565705, -0.15029612, -0.080749065, -0.09955825, 0.032431383, 0.023855643, -0.2748885, -0.23232168, -0.29121292, -0.26405892, 0.16556135, 0.18657646, 0.1424068, 0.18855052) * g_2;
result += mat4(0.10960496, 0.10851629, 0.095003806, 0.11053746, 0.09885307, 0.14437789, 0.13191165, 0.17365928, 0.16558935, 0.15473324, 0.21136154, 0.19976667, -0.07267957, -0.11469687, -0.029134216, -0.06817615) * g_3;
result += mat4(0.10202856, 0.04216857, -0.03959349, -0.09849683, -0.1576996, -0.049997438, -0.1579918, -0.058789205, 0.029792828, -0.07311781, -0.045432188, -0.11312683, 0.24257647, 0.16204113, 0.17869382, 0.16024388) * g_4;
result += mat4(0.17193612, 0.12692013, 0.13177487, 0.0796725, 0.0797928, 0.08952722, -0.012468046, 0.011071511, -0.068559825, -0.024852324, 0.0526428, 0.07917346, -0.085534215, -0.09591339, 0.04615827, 0.024577664) * g_5;
result += mat4(-0.14653449, -0.067267366, -0.002524394, 0.086243175, 0.13660401, 0.08039592, 0.09179008, 0.022573143, -0.024744196, 0.09120211, 0.017654825, 0.14114714, -0.16093308, -0.14538004, -0.09950235, -0.111152865) * g_6;
result += mat4(-0.188637, -0.12968326, -0.1200479, -0.06537649, -0.12589337, -0.106242515, -0.02788782, -0.025949068, 0.04948153, 0.02222735, -0.025291357, -0.12379292, 0.11074645, 0.11902375, -0.00056989543, -0.0024386419) * g_7;
result += mat4(0.018286629, 0.0072215167, 0.00037828335, 0.0047001047, 0.011478272, 0.041745186, -0.015742473, -0.002282524, -0.03440817, -0.02196847, -0.07838253, -0.07993771, -0.010155526, -0.017590692, 0.027141469, 0.029741213) * g_8;
result += mat4(0.016512005, 0.004950637, -0.0238836, -0.05587327, -0.03164328, -0.009499985, -0.059880238, -0.061794154, 0.023154303, -0.013266373, 0.04701534, 0.0415862, 0.06357814, 0.033057794, 0.08389772, 0.00035060212) * g_9;
result += mat4(-0.016403968, -0.012538788, -0.0015746636, -0.004771009, -0.021361275, -0.009695242, 0.020548422, -0.0024130535, 0.07796766, -0.01516671, 0.09961382, 0.042754963, 0.017363647, 0.03729065, -0.004795824, 0.01550197) * g_10;
result += mat4(-0.0028093113, 0.011869523, -0.02216933, 0.011177349, 0.033342455, -0.021146454, 0.07830085, 0.032490104, -0.03281833, 0.0060484232, -0.04081057, -0.04945058, -0.0056189033, -0.010636801, -0.041949317, -0.025739705) * g_11;
result += mat4(0.012979897, 0.016758928, -0.049062215, -0.0035748442, 0.0085972, 0.0036381132, -0.0055621094, 0.0041307937, -0.0008907763, -0.0034079372, -0.025680453, -0.015531803, 0.012816766, 0.009977763, -0.016416566, 0.0034859509) * g_12;
result += mat4(0.021753248, 0.016452711, 0.009833835, 0.0065052663, 0.0014061348, -0.046160888, -0.0132271005, -0.05051269, -0.05746351, -0.0012690664, 0.017191738, 0.018192926, -0.008879476, 0.026354216, -0.012801991, -0.029587373) * g_13;
result += mat4(-0.04220692, -0.0015560482, -0.0019648245, 0.013402305, -0.018259782, -0.0036008905, 0.0035650074, -0.0019178417, 0.00051580026, 0.027355857, -0.017914988, 0.004937948, -0.046335887, 0.00013612259, 0.030293299, 0.030688645) * g_14;
result += mat4(-0.036683388, -0.0031274238, -0.026074665, 0.021684237, 0.022639066, 0.0022493738, 0.011508554, -0.0006385944, 0.04890418, 0.020119468, 0.004167364, -0.008356099, -0.008598796, 0.0089028, -0.0029575853, 0.016687104) * g_15;
result += mat4(0.027207986, 0.0011099194, 0.042383645, -0.015179333, 0.014744431, 0.006148344, 0.005165422, 0.0070196544, 0.030286826, 0.016620956, -0.01611366, -0.00667594, -0.029524863, -0.024751091, -0.013321004, -0.025199674) * g_16;
result += mat4(0.0027477827, 0.054622147, 0.010154094, 0.025437292, 0.031773083, -0.01055473, 0.022864206, -0.029010754, -0.0029999653, 0.025018329, 0.015316208, 0.027188798, -0.10096525, -0.017268656, 0.0012529213, -0.062078856) * g_17;
result += mat4(-0.053670805, 0.057336535, -0.037418038, 0.06443577, -0.016027879, -0.058168363, 0.007034215, -0.03390141, -0.0019346164, -0.027947908, 0.021723913, -0.0018286633, 0.030507812, 0.018293543, 0.042917266, 0.033528328) * g_18;
result += mat4(-0.004559579, 0.029667616, -0.001870353, 0.0378995, -0.017147437, 0.020192018, -0.021574946, 0.031568103, 0.07487145, 0.0032376775, -0.018893708, -0.041981626, 0.054478757, 0.0061423797, 0.041280247, 0.000878061) * g_19;
result += mat4(0.017076394, 0.023647636, 0.029403262, 0.029923365, 0.08866472, 0.060613394, 0.1314274, -0.04490231, -0.016304834, -0.0062647443, -0.0031828512, -0.03989252, -0.024330825, 0.00741213, -0.04075287, -0.01615817) * g_20;
result += mat4(0.017866978, 0.017720113, -0.02846163, 0.040761847, -0.0063438355, -0.02347501, 0.029564403, -0.0029562064, 0.12505588, -0.0073986333, 0.11250363, -0.06179967, 0.07854423, 0.08546533, 0.034743227, -0.010757377) * g_21;
result += mat4(-0.06416677, -0.08344284, 0.030138884, 0.017635904, -0.012087523, 0.014205202, -0.03221233, -0.023834767, -0.091186255, -0.028958676, -0.04724334, 0.00013161585, 0.027391518, 0.1249978, -0.045047652, 0.10737729) * g_22;
result += mat4(-0.04326348, -0.03543181, -0.029558217, -0.08582413, 0.007812453, 0.014296562, -0.028779754, 0.018517692, -0.063755795, -0.036619596, -0.050809663, 0.005431336, -0.029205568, -0.011827915, -0.031110523, -0.005648626) * g_23;
result += mat4(0.05499293, -0.10000709, -0.0943537, 0.16143042, -0.019952895, -0.0039807972, -0.014841254, 0.0320363, -0.065173544, -0.049425576, -0.023904482, 0.03759679, -0.03207411, -0.047782745, 0.01352581, 0.008140566) * g_24;
result += mat4(0.055923894, -0.025134467, 0.029583648, 0.04096879, 0.027551858, -0.14995384, 0.06467113, -0.11633077, -0.01563784, -0.026909819, -0.06292879, -0.078409635, -0.009081105, -0.015533088, 0.019585673, 0.04334208) * g_25;
result += mat4(-0.021717606, 0.042464726, 0.02743202, -0.07388838, 0.03460472, 0.0038285658, 0.099842004, -0.098247, 0.13276267, -0.020793032, -0.008603039, -0.051913783, 0.12959045, 0.14735717, -0.10888226, -0.10263746) * g_26;
result += mat4(-0.16819532, 0.141579, -0.062480718, -0.021918943, 0.06348125, 0.06849444, 0.03888676, 0.027375204, -0.08194279, -0.012574497, 0.13523251, 0.13739482, -0.047547445, -0.058767617, 0.07009549, 0.028136581) * g_27;
result += vec4(0.069033325, 0.040207114, 0.027286075, 0.0065334598);
return result;
}
//!DESC Anime4K-v3.2-Upscale-CNN-x2-(VL)-Depth-to-Space
//!HOOK MAIN
//!BIND MAIN
//!BIND conv2d_last_tf
//!BIND conv2d_last_tf1
//!BIND conv2d_last_tf2
//!SAVE MAIN
//!WIDTH conv2d_last_tf.w 2 *
//!HEIGHT conv2d_last_tf.h 2 *
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
vec4 hook() {
vec2 f0 = fract(conv2d_last_tf_pos * conv2d_last_tf_size);
ivec2 i0 = ivec2(f0 * vec2(2.0));
float c0 = conv2d_last_tf_tex((vec2(0.5) - f0) * conv2d_last_tf_pt + conv2d_last_tf_pos)[i0.y * 2 + i0.x];
vec2 f1 = fract(conv2d_last_tf1_pos * conv2d_last_tf1_size);
ivec2 i1 = ivec2(f1 * vec2(2.0));
float c1 = conv2d_last_tf1_tex((vec2(0.5) - f1) * conv2d_last_tf1_pt + conv2d_last_tf1_pos)[i1.y * 2 + i1.x];
vec2 f2 = fract(conv2d_last_tf2_pos * conv2d_last_tf2_size);
ivec2 i2 = ivec2(f2 * vec2(2.0));
float c2 = conv2d_last_tf2_tex((vec2(0.5) - f2) * conv2d_last_tf2_pt + conv2d_last_tf2_pos)[i2.y * 2 + i2.x];
float c3 = c2;
return vec4(c0, c1, c2, c3) + MAIN_tex(MAIN_pos);
}
@@ -0,0 +1,300 @@
// MIT License
// Copyright (c) 2019-2021 bloc97
// All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(M)-Conv-4x3x3x3
//!HOOK MAIN
//!BIND MAIN
//!SAVE conv2d_tf
//!WIDTH MAIN.w
//!HEIGHT MAIN.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (MAIN_texOff(vec2(x_off, y_off)))
vec4 hook() {
vec4 result = mat4(-0.029052526, 0.059789784, -0.024398074, 0.06907132, 0.18920785, -0.12923062, 0.0766382, -0.12048348, -0.017786544, 0.06251133, -0.068393864, 0.056690093, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, -1.0);
result += mat4(0.14032267, -0.077691495, -0.009036259, -0.13049065, 0.20954624, 0.023231741, -0.2759354, 0.49927905, 0.039609738, -0.092625424, 0.09426452, -0.2246486, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 0.0);
result += mat4(-0.023119625, 0.046549924, 0.073033765, 0.03727065, 0.04498207, 0.024455868, 0.17602317, -0.3150503, 0.019985953, 0.03670126, 0.0071220254, 0.107966185, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 1.0);
result += mat4(0.111121014, -0.084099665, 0.12595509, -0.048271902, -0.007799661, 0.04831373, 0.11868961, 0.11607051, 0.05169621, -0.050569464, 0.120362274, 0.034607537, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, -1.0);
result += mat4(-0.41167754, -0.44940078, 0.35485214, 0.58048695, -1.0151424, -0.70137614, 0.38405335, 0.37337455, -0.096364655, -0.14538667, 0.17917591, 0.32259464, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 0.0);
result += mat4(0.06378494, -0.040756933, -0.4773648, -0.47702238, 0.1803328, -0.21388084, -0.5509359, -0.6491179, -0.048081584, -0.0025129975, -0.28561604, -0.22229671, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 1.0);
result += mat4(-0.037024107, 0.016497454, -0.05315267, -0.023392007, 0.1840294, 0.12675077, 0.037417043, -0.022394283, -0.028192522, -0.016344562, -0.07269005, -0.04747136, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, -1.0);
result += mat4(0.039480202, 0.5577544, -0.117326505, 0.06622856, -0.038784727, 0.65673745, -0.109742545, 0.22294083, 0.00038519394, 0.24552485, -0.07008514, 0.00029412706, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 0.0);
result += mat4(-0.009279719, -0.031882852, 0.14124188, -0.0759899, -0.024016602, 0.15252088, 0.13614258, -0.09961189, 0.05446014, -0.03827061, 0.11210173, -0.028823104, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 1.0);
result += vec4(0.012836382, -0.0062823873, -0.03165346, -0.0017501811);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_tf
//!SAVE conv2d_1_tf
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.06215308, -0.054471835, 0.1285146, 0.037585296, -0.14467795, 0.0057610427, 0.006528968, 0.18607244, 0.03762581, -0.121003255, 0.0827445, 0.076479666, 0.07540097, 0.16371846, -0.18786757, -0.12048073) * go_0(-1.0, -1.0);
result += mat4(-0.11520603, -0.22384967, 0.0970881, 0.045122143, -0.40358877, 0.12487416, -0.4489702, 0.04854906, -0.08240888, -0.0058777514, 0.19108902, 0.042189106, -0.07843178, 0.0012592699, -0.11303816, -0.1118517) * go_0(-1.0, 0.0);
result += mat4(0.29387334, 0.08150406, -0.06009834, 0.07054583, -0.146034, 0.04053809, 0.23284695, -0.08233496, 0.0957811, 0.20221621, 0.13570721, -0.081069246, -0.031743366, -0.038912926, -0.059060514, 0.05190416) * go_0(-1.0, 1.0);
result += mat4(0.18404631, -0.034244366, -0.13406059, 0.2242061, -0.23668705, -0.10359684, -0.09689738, -0.04932347, -0.0020525968, 0.15236467, -0.2634303, -0.029878438, 0.3283669, 0.09891668, -0.46656898, -0.03271751) * go_0(0.0, -1.0);
result += mat4(-0.1824976, -1.0545974, 0.93027455, -0.13887188, 0.3128633, -0.2734884, -0.831517, -0.18065166, -0.2119423, 0.19241124, -0.13313763, -0.10523897, 0.2675327, -0.06853148, 0.007885104, 0.26000848) * go_0(0.0, 0.0);
result += mat4(-0.18109167, -0.019768981, -0.14131357, -0.3181756, 0.22158594, 0.1431138, -0.12970252, -0.011459096, -0.03742945, 0.2316056, -0.054623842, -0.09360549, 0.10176328, -0.108150974, 0.122787155, 0.07614884) * go_0(0.0, 1.0);
result += mat4(-0.02370754, -0.10264054, 0.030659392, 0.056694325, 0.034085, -0.0538203, 0.09111551, 0.0063995267, 0.0835243, -0.06623529, 0.20924146, 0.09457414, 0.27925664, 0.049511474, -0.22062886, 0.03312504) * go_0(1.0, -1.0);
result += mat4(0.17009354, 0.34391564, -0.1680695, -0.051450633, -0.044037253, -0.1412577, 0.01097572, -0.049040757, 0.104024716, -0.34571946, 0.5213214, -0.17010914, -0.016452854, 0.3303069, -0.22249438, 0.23866816) * go_0(1.0, 0.0);
result += mat4(0.20491506, 0.20098424, 0.015425732, -0.033999693, -0.0111842435, 0.09945295, -0.025766203, 0.17068656, 0.049262784, 0.0077788536, 0.068585835, 0.19229786, -0.013048818, 0.04877973, 0.024053875, -0.06846659) * go_0(1.0, 1.0);
result += mat4(0.14208305, 0.09790381, -0.008420949, -0.016165754, 0.02445528, 0.08700781, 0.046639573, -0.22105917, -0.08529265, 0.06606378, 0.0947481, 0.08149193, -0.0959293, -0.037756894, -0.008136973, 0.046241503) * go_1(-1.0, -1.0);
result += mat4(0.2577669, 0.13766493, 0.021107635, 0.018978242, 0.452542, 0.25566816, -0.68909633, 0.03804329, 0.06771752, 0.07894156, 0.22501312, -0.047511246, 0.00040517355, -0.0202232, -0.27541754, -0.040150844) * go_1(-1.0, 0.0);
result += mat4(-0.30176973, -0.15739526, -0.038548045, -0.07003333, 0.32053417, 0.025467036, -0.044913124, 0.20454903, 0.12475206, -0.03966162, 0.07139637, 0.12101497, -0.10777517, -0.062379625, 0.06598757, -0.14795317) * go_1(-1.0, 1.0);
result += mat4(0.12068516, 0.0026514034, -0.055378057, -0.0976728, 0.15887645, 0.15590422, 0.076294206, -0.15417404, -0.16548084, -0.18422292, -0.1670212, 0.041155312, -0.11765263, 0.16991171, -0.21535093, 0.01542368) * go_1(0.0, -1.0);
result += mat4(-0.37845853, 0.5732961, 0.114283465, 0.14638355, -0.109194644, 0.087304994, -0.15938401, 0.58242995, -0.025850652, 0.02730721, -0.48582682, -0.2547883, 0.1899583, 0.24296008, -0.8162976, 0.018036429) * go_1(0.0, 0.0);
result += mat4(0.1633212, -0.117295206, 0.021892091, 0.07762347, -0.09726402, -0.1364192, 0.10752197, 0.42020246, 0.06665656, -0.10822656, 0.1337331, 0.0552859, 0.04700212, 0.108017646, -0.2009353, -0.0435288) * go_1(0.0, 1.0);
result += mat4(-0.045603696, 0.05774526, -0.0071174325, 0.24119262, -0.06899063, 0.016012343, 0.009982042, -0.19038968, -0.17796072, -0.12510185, 0.22739507, -0.1805478, -0.100294635, 0.017557602, 0.039842658, 0.13116726) * go_1(1.0, -1.0);
result += mat4(0.0131523665, -0.20472725, 0.121814765, -0.17769355, 0.097669855, 0.09648846, -0.072887406, 0.22992326, -0.019087939, 0.35375193, -0.057155706, 0.17699116, 0.030690158, -0.423475, 0.03703492, -0.03429164) * go_1(1.0, 0.0);
result += mat4(-0.12143413, -0.018402342, 0.04536776, -0.12743106, 0.03355068, -0.09694192, 0.09913357, -0.036602203, 0.11038047, 0.13236065, 0.12966877, -0.10163848, 0.0030612876, -0.116145626, 0.045318183, 0.11492169) * go_1(1.0, 1.0);
result += vec4(0.05657016, -0.04848861, 0.10297782, -0.0076417355);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_1_tf
//!SAVE conv2d_2_tf
//!WIDTH conv2d_1_tf.w
//!HEIGHT conv2d_1_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.088122115, -0.009916053, -0.124469265, -0.032139737, -0.13709281, 0.09177288, -0.06794775, -0.03962873, 0.17613642, -0.11064388, 0.2531882, -0.3817648, -0.1172188, -0.042132895, -0.098772734, -0.114560865) * go_0(-1.0, -1.0);
result += mat4(0.18876404, -0.14613804, 0.23869626, 0.06580185, -0.13533239, 0.25754455, -0.29734856, 0.028218834, -0.39304733, -0.14716247, 0.19408274, -0.18518063, -0.31482637, -0.1508887, -0.3841371, 0.021975968) * go_0(-1.0, 0.0);
result += mat4(-0.22316615, -0.0923483, 0.16932568, -0.13138154, 0.139829, 0.010975908, -0.0587337, -0.054484393, -0.13758336, -0.030077504, -0.050642505, -0.14933856, -0.040563874, -0.030220931, 0.2867556, 0.17022403) * go_0(-1.0, 1.0);
result += mat4(0.12611523, -0.07087836, 0.08281469, 0.024588918, -0.023549056, -0.13102995, 0.17571726, 0.0740372, -0.3167631, 0.17491543, 0.4459055, -0.4687942, -0.19755729, 0.03723031, -0.06757113, 0.03502462) * go_0(0.0, -1.0);
result += mat4(0.12098187, 0.12341856, -0.061940372, 0.7251308, 0.055730965, -0.5169302, 0.37959704, -0.08753306, -0.45700142, -0.4960699, 0.1690022, -0.40233734, 0.0262836, -0.13345262, 0.11002605, -0.16773209) * go_0(0.0, 0.0);
result += mat4(-0.20428565, 0.117523015, -0.044863444, -0.1770644, 0.22925, 0.029694336, -0.23891294, 0.039587863, -0.11235541, -0.23890465, 0.037618574, -0.039127905, 0.14058869, 0.020599412, -0.074353516, 0.12343045) * go_0(0.0, 1.0);
result += mat4(-0.04680316, -0.049870726, 0.06975308, -0.21486782, -0.08177838, 0.09760846, -0.031408366, 0.13881667, -0.14650045, 0.29182404, -0.080848776, -0.25525567, 0.018876432, -0.015662232, -0.016014043, -0.08435915) * go_0(1.0, -1.0);
result += mat4(-0.100864016, -0.24647528, -0.007994345, 0.13047779, 0.14746517, 0.25517163, 0.054900885, -0.07251866, -0.29500577, -0.03758923, 0.05514366, -0.058372885, -0.03055354, -0.062586576, 0.017739896, 0.08644674) * go_0(1.0, 0.0);
result += mat4(-0.029502464, -0.08905223, 0.0047584837, -0.09646073, 0.044714086, 0.15522493, -0.070930906, -0.026954453, 0.057949875, -0.017211404, -0.00566463, -0.0050975676, 0.0050182147, -0.010722001, 0.011812942, -0.04698445) * go_0(1.0, 1.0);
result += mat4(0.18863353, -0.11575336, 0.26541254, -0.15280409, 0.14376, -0.05783716, 0.08554402, 0.27605456, 0.004611954, 0.074173525, -0.07963756, 0.082979314, 0.099553905, -0.06539344, 0.4330784, 0.07996894) * go_1(-1.0, -1.0);
result += mat4(-0.31001288, 0.035875235, 0.049856357, -0.09614268, 0.23397788, -0.12425775, 0.45108303, 0.27973723, 0.0753222, 0.11388394, -0.043821793, -0.05610102, -0.06536777, 0.009822641, 0.7956708, -0.05798737) * go_1(-1.0, 0.0);
result += mat4(0.19827974, 0.010130333, -0.13153136, 0.11593003, -0.15762039, -0.0040722084, 0.20404483, 0.28999883, 0.08152756, 0.07773477, 0.019730574, 0.0123460535, -0.034676805, -0.19133334, 0.01860159, -0.12945038) * go_1(-1.0, 1.0);
result += mat4(-0.1861255, 0.039945368, 0.28345293, -0.17425321, 0.36748698, 0.03729066, -0.35957313, 0.11234573, -0.07122196, 0.012845119, -0.09049443, 0.10106711, 0.07425845, -0.14626606, 0.46169114, -0.2652126) * go_1(0.0, -1.0);
result += mat4(-0.6222811, -0.08538015, 0.023319554, -0.7206892, -0.33495513, 0.2960924, -0.033479776, -0.29255456, 0.29802337, 0.47570458, 0.012769826, 0.19874385, -0.25652033, -0.4018595, 0.3055839, 0.1881051) * go_1(0.0, 0.0);
result += mat4(0.32152474, 0.0024920676, -0.113435976, 0.14440896, -0.287736, 0.0251382, 0.28160754, 0.02769615, 0.067614004, 0.0832741, -0.095353454, -0.19792004, -0.40394694, -0.24224225, 0.3224996, 0.030300485) * go_1(0.0, 1.0);
result += mat4(0.0045148246, -0.04621849, -0.026470715, 0.0588576, 0.14559188, 0.037437905, -0.13778603, 0.08173416, 0.033548757, -0.015654223, 0.15016593, 0.07761835, -0.016546778, 0.02640291, 0.112717085, 0.020371364) * go_1(1.0, -1.0);
result += mat4(0.114227325, -0.0405595, -0.11662477, 0.014747093, 0.11170598, 0.58740836, -0.27560827, -0.1848705, -0.02491223, -0.15605451, 0.0028677192, -0.11290364, -0.12331832, -0.3191161, 0.3505101, 0.15236251) * go_1(1.0, 0.0);
result += mat4(0.21131381, 0.13965495, -0.103683874, 0.26049778, -0.010777816, 0.015093082, 0.13207617, 0.14064828, -0.007847294, 0.025702007, -0.082993574, -0.04923462, -0.052900862, -0.006775377, 0.1432969, 0.09598549) * go_1(1.0, 1.0);
result += vec4(0.08015534, 0.10264796, -0.031173404, 0.21282151);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_2_tf
//!SAVE conv2d_3_tf
//!WIDTH conv2d_2_tf.w
//!HEIGHT conv2d_2_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.13262276, 0.18682314, -0.1433667, -0.0061677806, -0.15057871, -0.15389217, 0.40721065, -0.082456455, 0.028175479, -0.06136406, 0.13517159, -0.0066659097, -0.03311807, -0.056219388, 0.066265404, -0.017012158) * go_0(-1.0, -1.0);
result += mat4(0.0652481, -0.02717338, -0.17586891, -0.1458622, 0.37166637, -0.13651049, -0.095090784, 0.1450258, -0.08856753, -0.029000161, -0.11024598, 0.14231622, 0.027118085, 0.060637098, -0.028174674, 0.020973917) * go_0(-1.0, 0.0);
result += mat4(0.17137158, 0.015818363, -0.1761587, -0.07798954, -0.22039492, -0.08250406, 0.15350278, 0.05466543, 0.07231244, 0.124937475, -0.14530692, -0.036220204, -0.20202135, 0.16154502, -0.1472417, 0.045183204) * go_0(-1.0, 1.0);
result += mat4(-0.06751513, 0.3630837, -0.23374555, -0.17641832, 0.23866339, -0.12625019, 0.14955078, 0.3757683, 0.25546572, -0.0009440543, -0.029705383, 0.12500505, 0.039303612, 0.02745342, 0.06280759, -0.027673393) * go_0(0.0, -1.0);
result += mat4(-0.40253955, 0.5532656, 0.15580782, 0.23305601, 0.04307387, -0.37548792, 0.021682428, -0.14554474, -0.44655007, 0.12335231, 0.22693188, -0.19185324, -0.39905196, -0.36661598, 0.34626722, 0.3220371) * go_0(0.0, 0.0);
result += mat4(0.13051705, -0.051269528, 0.027860573, 0.12866034, 0.095374286, 0.0072371624, 0.06641015, -0.040609945, 0.14411138, 0.03813084, 0.024812538, -0.069997884, -0.2398024, 0.16384888, 0.004522481, -0.2734798) * go_0(0.0, 1.0);
result += mat4(-0.048976544, 0.36923414, -0.23769425, -0.02964149, 0.13426293, -0.070416726, -0.036279447, 0.21007125, -0.0062456504, 0.12307804, -0.18920022, 0.016429992, 0.091225415, -0.00714184, -0.079064, 0.050525308) * go_0(1.0, -1.0);
result += mat4(0.007005748, -0.1929285, -0.27960134, -0.014070343, -0.012031938, -0.21320626, 0.22591045, 0.06750757, 0.038049847, -0.08933499, 0.15640227, 0.36653376, 0.11274315, 0.0015512784, -0.14319079, -0.41117874) * go_0(1.0, 0.0);
result += mat4(0.039254356, 0.04123307, -0.14476523, 0.19676228, -0.1746638, 0.068685316, 0.19318552, -0.007086376, -0.08810745, 0.041937724, 0.1393943, 0.27539206, -0.08331265, 0.043064818, -0.02783017, -0.13006629) * go_0(1.0, 1.0);
result += mat4(0.18761271, -0.009960496, -0.18572417, 0.010640895, 0.10240658, 0.036137953, -0.109363064, 0.05820501, -0.04092678, 0.11809751, -0.11843415, 0.11893309, -0.06356792, 0.1509876, -0.12252014, -0.0070098704) * go_1(-1.0, -1.0);
result += mat4(-0.0012312894, 0.038436964, -0.046054237, 0.04859312, -0.4190657, 0.2529927, 0.23133701, -0.00065297337, -0.039581586, 0.00905735, 0.16532114, -0.12568031, 0.17818217, -0.28053075, 0.38509414, -0.03763847) * go_1(-1.0, 0.0);
result += mat4(-0.0897875, 0.063593514, 0.07660054, 0.12268424, 0.21554653, -0.1025501, 0.2557211, 0.04492533, 0.10992355, -0.035215836, -0.009733763, -0.02165148, 0.08618596, -0.19276536, 0.18174514, -0.18021213) * go_1(-1.0, 1.0);
result += mat4(-0.002999377, -0.12630916, -0.030010369, -0.2676409, -0.20229307, 0.15253967, -0.12200155, -0.1552754, -0.16193017, 0.10819683, 0.10696224, -0.1920264, -0.29354608, -0.32021165, 0.08644405, -0.16153689) * go_1(0.0, -1.0);
result += mat4(0.49931613, -0.3669461, -0.49107462, -0.3654748, 0.32047966, 0.03246311, -0.06424334, 0.009108802, 0.2367612, -0.46587244, 0.16957493, 0.3237888, 0.93676794, 0.01834384, -0.9349752, -0.04654371) * go_1(0.0, 0.0);
result += mat4(-0.112562165, 0.006074484, -0.12288025, -0.08560263, 0.25336134, 0.025205871, 0.25063732, 0.12370882, -0.40429187, 0.12992847, -0.2816234, 0.08179623, 0.27197668, 0.066299304, -0.12988937, 0.16257611) * go_1(0.0, 1.0);
result += mat4(0.047864527, -0.05821779, -0.06311128, -0.0065775234, -0.065763995, 0.014864688, 0.09148591, -0.25059348, 0.008846306, -0.22123712, 0.4062609, -0.100248575, -0.50293785, -0.13373566, 0.21480446, -0.0841981) * go_1(1.0, -1.0);
result += mat4(0.19313097, -0.061253734, -0.1801314, -0.20178059, -0.039574936, 0.08167749, 0.010974997, 0.069656976, -0.13193963, 0.35555324, 0.62686867, -0.28656846, -0.27831817, -0.0040086447, 0.4031064, 0.47767937) * go_1(1.0, 0.0);
result += mat4(0.15396428, 0.069321476, -0.15190981, -0.24133344, 0.106151104, -0.11271092, 0.06878746, 0.14279713, -0.02006402, -0.36284852, -0.00926688, -0.39887694, -0.20926239, -0.021860912, 0.07588468, 0.2620174) * go_1(1.0, 1.0);
result += vec4(-0.0073282495, -0.040352557, -0.063710704, 0.07255652);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_3_tf
//!SAVE conv2d_4_tf
//!WIDTH conv2d_3_tf.w
//!HEIGHT conv2d_3_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.17903937, -0.0014294779, 0.1824805, -0.19555633, -0.0052551827, -0.013796094, 0.06358042, 0.13301018, 0.008874768, 0.06605332, 0.06117636, 0.012946474, 0.048656575, 0.0060409275, -0.0671362, -0.06897735) * go_0(-1.0, -1.0);
result += mat4(-0.16098012, 0.10772552, -0.13175552, -0.5299018, 0.068713695, -0.048258893, -0.49698257, 0.36581638, 0.21755004, -0.12125899, -0.27382872, -0.12268086, 0.014334542, 0.20573758, 0.45879167, -0.29648975) * go_0(-1.0, 0.0);
result += mat4(0.06860283, -0.18047708, 0.024707617, 0.11900479, 0.09474589, -0.16559775, -0.054095846, -0.011377782, -0.008733984, 0.105020404, -0.040116277, -0.0022003972, 0.1453799, -0.032110006, -0.018741792, -0.12511599) * go_0(-1.0, 1.0);
result += mat4(0.20024729, -0.01969923, -0.026999667, -0.39064395, -0.14559332, -0.11634086, -0.13226044, 0.11779975, -0.08838282, -0.0882447, -0.23166943, -0.15760234, 0.030928904, -0.032423917, 0.20324136, -0.19692755) * go_0(0.0, -1.0);
result += mat4(0.49499384, 0.7327846, -0.6173799, -0.53821295, -0.15000962, 0.11169762, 0.6942423, 0.07956513, 0.06913002, -0.19037646, -0.19826908, 0.68080276, -0.2747096, -0.15832238, 0.47366706, 0.090432756) * go_0(0.0, 0.0);
result += mat4(-0.18274948, 0.09204629, 0.16644076, 0.05641037, 0.03328184, -0.6218293, 0.26432592, -0.093742386, 0.33038342, -0.24853565, -0.23683667, -0.37430722, -0.20684583, -0.32283148, -0.07633969, -0.08765815) * go_0(0.0, 1.0);
result += mat4(0.06821987, 0.06395764, -0.14685121, -0.15894371, -0.093540885, 0.057568345, -0.048376244, -0.009256543, -0.26325077, -0.03193119, -0.16857445, -0.02404981, 0.110593356, 0.042911418, 0.06626762, -0.0312436) * go_0(1.0, -1.0);
result += mat4(0.3108626, 0.37123847, -0.082249805, -0.21339422, -0.3756041, -0.08518717, -0.16853802, 0.011641729, -0.30096757, 0.26942274, -0.08990497, -0.19451031, 0.21974437, -0.04231723, 0.26160353, -0.040834647) * go_0(1.0, 0.0);
result += mat4(0.11795158, 0.24436565, 0.043223023, -0.0159957, -0.19689156, 0.13223267, -0.013983249, 0.09437164, -0.47648698, -0.00082660443, -0.085406005, 0.10885898, 0.104696035, -0.053257108, 0.024389362, 0.0282572) * go_0(1.0, 1.0);
result += mat4(0.032890156, 0.0115719065, -0.01898909, -0.03034875, -0.041037276, -0.1026382, 0.03337663, 0.20108728, -0.00023235095, -0.018033072, -0.028535927, 0.07359915, 0.075182244, 0.02959868, 0.15107772, -0.09815672) * go_1(-1.0, -1.0);
result += mat4(-0.004040557, 0.06707476, 0.039022792, 0.52437925, -0.08027356, 0.040488366, 0.035332825, 0.07683081, -0.03521227, -0.081861034, 0.090804815, 0.10580108, 0.20452882, -0.58755285, 0.04303056, 0.41562977) * go_1(-1.0, 0.0);
result += mat4(0.09290062, 0.03495193, 0.02347216, -0.012873525, -0.076936446, 0.1453216, -0.03742271, -0.14174925, -0.058219753, 0.19095406, 0.055627216, 0.09437343, -0.010424211, -0.314692, 0.3314579, -0.053285643) * go_1(-1.0, 1.0);
result += mat4(-0.053961687, 0.1483992, 0.042458896, -0.1966439, 0.13864957, 0.07587672, -0.06519269, 0.09530391, 0.04215073, 0.039545458, 0.21056756, 0.09972659, 0.02987125, -0.08102741, 0.07075036, 0.21867757) * go_1(0.0, -1.0);
result += mat4(-0.5512795, 0.03104814, 0.27901977, 0.122875504, -0.2656715, 0.007895486, -0.6735937, 0.20810314, -0.31432617, 0.07420857, 0.2573659, -0.35361463, 0.19826569, -0.47774056, 0.15816487, -0.29203883) * go_1(0.0, 0.0);
result += mat4(0.35078493, -0.07371588, -0.026663188, -0.20976657, -0.009644347, 0.037428845, -0.33933878, -0.010807704, 0.088060796, 0.16753472, -0.12296045, 0.17563403, 0.1501952, 0.07353703, 0.32531765, 0.11667607) * go_1(0.0, 1.0);
result += mat4(0.096126616, -0.058021486, -0.03439203, 0.06868024, 0.047914367, 0.026945053, 0.04207778, 0.046023168, 0.16024022, 0.07846185, 0.004195093, 0.07272046, -0.10458233, -0.0904536, 0.16049337, 0.015941419) * go_1(1.0, -1.0);
result += mat4(0.032256138, -0.055398785, 0.079738356, 0.113359064, 0.11975066, -0.074372105, 0.102006756, -0.011490042, 0.15155345, 0.0025528704, 0.23328577, -0.059241068, -0.067783386, -0.18220833, 0.0057692174, 0.039900843) * go_1(1.0, 0.0);
result += mat4(-0.06173998, -0.07121991, -0.01118306, -0.063749574, -0.032665797, 0.0014987896, 0.03113169, 0.06916617, 0.0066490914, -0.052818965, -0.050131317, 0.10337558, -0.030870482, -0.14671221, 0.12152145, -0.05003445) * go_1(1.0, 1.0);
result += vec4(-0.010524109, -0.008519857, -0.08958723, -0.07917139);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_4_tf
//!SAVE conv2d_5_tf
//!WIDTH conv2d_4_tf.w
//!HEIGHT conv2d_4_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.11029161, 0.027180295, -0.115622066, 0.16493714, 0.29633296, -0.11739625, -0.36390316, 0.15221693, -0.009233659, -0.062213745, -0.07184558, 0.07418268, -0.05182182, 0.0066014086, -0.006811494, -0.010030367) * go_0(-1.0, -1.0);
result += mat4(-0.18361749, 0.08565693, 0.24127418, -0.20478591, 0.6198113, -0.17994536, -0.011840256, 0.120292775, 0.2873902, -0.019704796, -0.062267166, 0.0104749305, -0.048370067, -0.028105626, 0.11494511, -0.15941763) * go_0(-1.0, 0.0);
result += mat4(-0.08084502, 0.10195475, -0.03200553, 0.032734055, 0.030348243, -0.028927604, 0.045914374, 0.029237835, 0.07756032, -0.06346545, -0.290196, 0.057043966, 0.13982558, -0.12195619, -0.15895663, -0.10097537) * go_0(-1.0, 1.0);
result += mat4(-0.12018707, -0.320156, -0.4089669, 0.26015735, 0.59622765, -0.05654362, 0.28581724, 0.32069868, -0.0013007161, -0.060870633, -0.2732852, 0.2357145, 0.2137239, 0.0110256495, -0.069258444, 0.113870576) * go_0(0.0, -1.0);
result += mat4(0.54700065, -0.072552234, 0.27267826, -0.26660076, 0.7043544, 0.18192886, 0.80024594, 0.2447395, -0.3289639, -0.2681839, 0.063631415, -1.0118654, 0.45691678, 0.42904988, -0.2301862, -0.6652257) * go_0(0.0, 0.0);
result += mat4(0.19215634, 0.030154131, 0.07679603, 0.50318545, 0.056434657, 0.028623195, -0.14471184, -0.13905096, -0.03254216, -0.1191584, -0.18907212, 0.49208716, 0.5069476, -0.1490824, -0.104480386, -0.06595394) * go_0(0.0, 1.0);
result += mat4(-0.08893682, 0.13113782, 0.023672188, 0.013086517, -0.25986442, 0.038162243, -0.10951209, -0.2027832, -0.013547809, -0.029482972, -0.17670235, 0.13529542, -0.0621569, -0.0979757, -0.10714689, -0.08474307) * go_0(1.0, -1.0);
result += mat4(-0.032828752, 0.00037559783, 0.023968933, -0.047482926, -0.20302027, 0.08830911, -0.20885307, -0.11137413, 0.16585048, -0.076796696, -0.030462325, -0.2020944, 0.048723634, -0.45607433, -0.29950324, -0.5867916) * go_0(1.0, 0.0);
result += mat4(0.008863689, 0.061761267, -0.039097138, 0.24465923, -0.05917457, -0.21383028, -0.085846715, -0.14150433, 0.0988731, -0.0160538, -0.045119412, 0.095252946, -0.057551738, 0.21348421, -0.03480491, -0.26071647) * go_0(1.0, 1.0);
result += mat4(-0.21351442, 0.10038809, 0.34001955, -0.100911774, 0.0208522, -0.028755441, 0.025793588, 0.013080005, 0.03849989, 0.13662058, 0.04311886, 0.17398632, -0.01397261, -0.016415505, -0.0070752064, 0.007656161) * go_1(-1.0, -1.0);
result += mat4(-0.280189, 0.09252764, -0.077729605, 0.12662902, -0.10433321, 0.03644144, -0.06625324, 0.05696802, 0.15468478, 0.08328583, 0.069849946, 0.061947342, -0.05560477, -0.0074776993, -0.15365681, -0.03526299) * go_1(-1.0, 0.0);
result += mat4(0.05886785, 0.15303846, 0.0066637015, -0.19983207, -0.07803175, -0.10772685, -0.12690999, -0.08275092, 0.033436153, 0.08424011, 0.17092863, 0.0043526487, 0.014620474, 0.044702258, 0.1686881, -0.016890949) * go_1(-1.0, 1.0);
result += mat4(0.1833738, 0.14381635, -0.025888365, -0.14182197, -0.25804865, 0.07216123, 0.025790794, 0.14096753, 0.023591481, 0.15610993, 0.026975863, 0.008755717, -0.13039349, -0.063048325, -0.121329494, -0.12331732) * go_1(0.0, -1.0);
result += mat4(0.0005065098, 0.44017914, 0.18493074, 0.13099027, -0.36087477, -0.37567857, -0.48981526, 0.5590752, -0.23918836, 0.19170256, 0.16816153, -0.29986876, -0.44738817, 0.018545123, 0.66217834, 0.31810755) * go_1(0.0, 0.0);
result += mat4(-0.16725904, 0.05753713, 0.058880586, -0.336765, 0.013667228, 0.056172702, 0.13465533, -0.07573556, -0.06313958, 0.06746643, 0.18878669, 0.09404202, -0.21780397, 0.12862128, -0.09476746, -0.34096682) * go_1(0.0, 1.0);
result += mat4(-0.07169524, 0.072302215, 0.052789338, -0.14035568, 0.078670934, -0.22246763, -0.0098074945, 0.024950746, 0.10949147, 0.06182366, 0.021721192, 0.12129548, 0.094007075, 0.06076156, 0.016474832, 0.08092115) * go_1(1.0, -1.0);
result += mat4(-0.10960447, 0.1878152, -0.029822018, 0.10598909, 0.1582181, 0.086522795, 0.093725055, 0.12908185, 0.23202112, -0.28859115, 0.26614165, 0.124523655, 0.19427507, 0.059677128, 0.003624697, 0.44220912) * go_1(1.0, 0.0);
result += mat4(-0.03620583, -0.102766834, 0.025527107, -0.11316131, -0.1507822, 0.0543862, -0.08225627, -0.06438472, 0.04580623, 0.6329729, 0.23854075, 0.35752076, 0.04363613, -0.12580468, -0.0006126687, -0.04995386) * go_1(1.0, 1.0);
result += vec4(0.060475674, -0.042036578, 0.06406282, 0.05569301);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(M)-Conv-4x3x3x8
//!HOOK MAIN
//!BIND conv2d_5_tf
//!SAVE conv2d_6_tf
//!WIDTH conv2d_5_tf.w
//!HEIGHT conv2d_5_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max(-(conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.008187961, -0.08433309, -0.17281345, 0.1306418, -0.2925821, 0.20668334, 0.14854355, -0.15960559, 0.08599311, -0.096088655, -0.10121403, 0.067429096, 0.049158614, 0.036637552, 0.22137405, -0.17016457) * go_0(-1.0, -1.0);
result += mat4(0.024813082, 0.028489944, 0.06814137, -0.1245949, 0.54239255, 0.08648708, 0.28979865, 0.110916786, -0.1927179, -0.17756873, -0.1878214, 0.05795718, -0.080397904, 0.22125137, 0.1907366, -0.016493658) * go_0(-1.0, 0.0);
result += mat4(0.027259264, 0.01494357, 0.04852894, 0.037580628, 0.031005561, 0.003570554, -0.072993, -0.053475574, 0.031119492, -0.14140029, -0.10386501, -0.015266508, 0.01153506, 0.16006693, 0.088294305, 0.04201491) * go_0(-1.0, 1.0);
result += mat4(0.13026185, 0.097255, 0.49145448, -0.33619553, -0.21144676, 0.019042643, -0.27274492, 0.3033865, 0.14040698, -0.13656893, 0.28211337, -0.26930946, -0.1626638, 0.105105706, -0.50837296, 0.39536825) * go_0(0.0, -1.0);
result += mat4(-0.42495522, 0.14972518, 0.0007564128, 0.37367433, 0.44553527, 0.3338494, -0.26058698, 0.087256804, -0.4324135, 0.20706014, 0.33552194, -0.13375738, -0.13469471, 0.22374928, -0.36969653, -0.34171587) * go_0(0.0, 0.0);
result += mat4(0.26543954, -0.004381978, 0.10609993, -0.09718426, -0.15620759, -0.03287476, 0.093032375, 0.00028344034, -0.11699793, -0.016492033, 0.023340177, 0.0062737763, -0.14305823, -0.2721832, -0.160177, -0.06915171) * go_0(0.0, 1.0);
result += mat4(0.03334679, 0.12436332, -0.13226178, 0.13868971, 0.017779246, -0.012697869, -0.11553709, 0.08638636, 0.0955215, -0.0309646, 0.040856246, -0.03978358, 0.023490254, -0.07178907, 0.23794931, -0.1714287) * go_0(1.0, -1.0);
result += mat4(-0.11820261, 0.116130814, 0.58924234, -0.37785482, 0.016644944, -0.071019046, 0.0076222476, -0.024118654, -0.076183304, -0.14971451, 0.06356606, -0.07225465, -0.17400762, 0.030856986, 0.03957665, -0.0070553776) * go_0(1.0, 0.0);
result += mat4(0.10954708, 0.063694, -0.058218896, 0.0010372304, 0.032423936, 0.006164447, -0.031383317, 0.012955956, -0.17115591, 0.16328962, 0.07279567, 0.06571465, 0.005532307, 0.13575353, 0.04082173, 0.041579492) * go_0(1.0, 1.0);
result += mat4(0.03146011, -0.08227295, -0.03498218, 0.04772092, 0.12055223, -0.12383867, 0.05448358, -0.07948453, -0.019064998, -0.0964146, -0.024651276, 0.041473705, -0.06493721, -0.054806646, -0.21607941, 0.20078054) * go_1(-1.0, -1.0);
result += mat4(-0.25740683, -0.33160943, -0.37422308, 0.12679969, -0.032204475, 0.41485202, 0.4538808, -0.082535125, 0.11784846, 0.10195789, 0.064491615, -0.10170162, -0.09500746, -0.15640756, -0.079364255, -0.12576963) * go_1(-1.0, 0.0);
result += mat4(-0.02532797, -0.014487023, -0.09441118, -0.060885422, -0.41196415, -0.1359501, 0.07101173, -0.053279232, 0.010979353, 0.1914526, 0.054606825, 0.015926225, 0.10410896, 0.010272597, -0.048138764, -0.02698072) * go_1(-1.0, 1.0);
result += mat4(-0.35856235, 0.099759184, -0.11972965, -0.03850837, -0.5143867, 0.3721666, -0.100802526, 0.21814734, -0.11864143, 0.15086797, 0.047075786, -0.14188164, 0.48882273, -0.12767795, 0.4937544, -0.41288656) * go_1(0.0, -1.0);
result += mat4(0.21679138, 0.023770422, -0.5454043, 0.18567741, 0.15965948, -0.84900963, -0.4684333, -0.21884751, 0.5876668, -0.9346244, -0.30144307, 0.97177315, 0.24103107, 0.35953388, 0.2032729, 1.2934744) * go_1(0.0, 0.0);
result += mat4(-0.5121466, -0.123357795, 0.1833694, 0.048652876, -0.20895603, 0.0619325, 0.064119816, 0.072841786, -0.21813762, -0.126957, -0.23441431, -0.009071302, -0.09766064, -0.12546945, 0.086008705, -0.0072638122) * go_1(0.0, 1.0);
result += mat4(0.15312338, -0.051029235, 0.07638347, -0.14028431, 0.10694411, -0.14639509, 0.3193828, -0.22767228, -0.19987194, 0.18207504, -0.19648756, 0.24752761, -0.03402804, -0.04186147, -0.20177092, 0.09467012) * go_1(1.0, -1.0);
result += mat4(-0.3587345, -0.20358992, -0.11016057, 0.21079709, -0.26201126, 0.040362626, 0.3186598, -0.059521858, 0.27564716, -0.041431133, 0.19315968, -0.30228892, 0.01191173, -0.10380854, 0.03030344, 0.026699625) * go_1(1.0, 0.0);
result += mat4(0.13373446, -0.011457521, -0.24851708, 0.06563771, -0.051668253, 0.09096929, -0.013976447, 0.041433394, -0.046981215, -0.00015144625, 0.05696515, 0.024501698, 0.2714476, -0.017434085, 0.025333954, -0.054034695) * go_1(1.0, 1.0);
result += vec4(0.06757453, -0.021112159, -0.015639946, 0.05520713);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(M)-Conv-4x1x1x56
//!HOOK MAIN
//!BIND conv2d_tf
//!BIND conv2d_1_tf
//!BIND conv2d_2_tf
//!BIND conv2d_3_tf
//!BIND conv2d_4_tf
//!BIND conv2d_5_tf
//!BIND conv2d_6_tf
//!SAVE conv2d_last_tf
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define g_0 (max((conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_1 (max(-(conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_2 (max((conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_3 (max(-(conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_4 (max((conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_5 (max(-(conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_6 (max((conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_7 (max(-(conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_8 (max((conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_9 (max(-(conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_10 (max((conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_11 (max(-(conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_12 (max((conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_13 (max(-(conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
vec4 hook() {
vec4 result = mat4(0.03795613, 0.09572901, 0.019826923, 0.10568741, -0.0030050736, -0.018890928, 0.0095737, 0.00807826, -0.022741016, 0.0046556294, -0.017018225, -0.010523109, -0.017621946, -0.0006488902, -0.009405731, -0.0027796263) * g_0;
result += mat4(-0.046617493, -0.018167915, -0.039274286, -0.027566826, -0.015821747, 0.003789104, -0.0020801623, 0.004032968, -0.05708595, -0.018440764, -0.032891296, 0.004184342, 0.047413353, 0.0034510887, 0.019148773, -0.0035636695) * g_1;
result += mat4(-0.046619494, -0.017274255, -0.03372405, -0.011152855, 0.10981248, 0.036214054, 0.07969624, 0.05590572, -0.031791378, -0.00307391, -0.0032425344, 0.0025762853, 0.0053703627, -0.02076939, -0.00058634114, -0.012593452) * g_2;
result += mat4(0.110471316, 0.031102506, 0.07860556, -0.018570926, -0.05038586, -0.07667239, -0.0819002, -0.08958284, 0.03846167, -0.007570915, 0.008598097, -0.0082979705, -0.03610172, -0.022735123, 0.02343143, 0.030037913) * g_3;
result += mat4(-0.075562544, -0.020187575, -0.020969959, 0.0062222136, 0.019780673, 0.059694994, 0.019240001, 0.05951303, 0.004168261, 0.00041100322, -0.0013793377, 0.002048099, -0.040564027, -0.031818517, -0.015498987, -0.02695407) * g_4;
result += mat4(-0.0016428401, 0.018965026, -0.013192817, -0.008289604, -0.044686675, -0.009061507, -0.049217258, -0.043777503, -0.07308355, -0.063734084, 0.019393511, -0.028853234, 0.057311818, 0.04126226, 0.086301416, 0.11784249) * g_5;
result += mat4(-0.06087458, 0.046508487, -0.10723279, 0.017619802, 0.13637137, 0.2054238, 0.013641375, 0.091581754, 0.03556439, 0.0500333, 0.0696777, 0.0922045, -0.020914901, -0.025425691, -0.050319638, -0.049094327) * g_6;
result += mat4(0.0030941095, -0.008679898, -0.05815756, -0.038728733, -0.062450465, -0.073838525, -0.030359933, -0.08355475, -0.039032117, -0.0689333, -0.04834296, -0.079471886, 0.09694701, 0.17491414, 0.093450785, 0.16742545) * g_7;
result += mat4(0.035618782, -0.027659958, 0.055540156, 0.013073733, 0.12144545, 0.05981087, -0.015131131, -0.0476281, -0.090847984, 0.005347584, 0.015588529, 0.024184622, -0.10743599, -0.01785147, -0.08566232, -0.14611128) * g_8;
result += mat4(-0.03812077, 0.018126076, -0.016625525, -0.06906415, -0.06267368, -0.058914356, 0.0009385371, -0.026746314, 0.048242237, 0.028906677, -0.028120263, -0.004209134, 0.009636235, 0.013206963, 0.07449269, 0.038961377) * g_9;
result += mat4(-0.014510558, -0.021065345, 0.09356215, -0.005815953, 0.08807958, 0.067895725, 0.08723713, 0.057831496, -0.10227873, -0.07699344, -0.06321843, -0.07448854, 0.09820774, 0.007563063, -0.14045772, -0.014161681) * g_10;
result += mat4(-0.18385889, 0.2255883, -0.29741547, 0.14618248, -0.08100661, -0.06860545, -0.112705804, -0.122642964, -0.06736901, 0.06971933, 0.12909706, -0.0418256, -0.32786265, 0.032497127, 0.4390302, 0.032726523) * g_11;
result += mat4(0.10560793, 0.083280005, -0.20369564, -0.14290833, -0.119196005, -0.028741803, 0.020456403, -0.06509816, 0.073811695, 0.02724128, -0.08691891, 0.10240907, 0.16827166, -0.17502932, -0.18295282, 0.15154512) * g_12;
result += mat4(0.0036247042, -0.002368346, 0.049646147, 0.058079436, 0.14403848, 0.07125248, 0.040327612, -0.013934329, 0.03871744, -0.1717596, 0.20666012, -0.24093682, -0.09846371, 0.011563227, 0.11973811, -0.0574434) * g_13;
result += vec4(0.022095086, 0.021079032, 0.030224537, 0.02154015);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(M)-Depth-to-Space
//!HOOK MAIN
//!BIND MAIN
//!BIND conv2d_last_tf
//!SAVE MAIN
//!WIDTH conv2d_last_tf.w 2 *
//!HEIGHT conv2d_last_tf.h 2 *
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
vec4 hook() {
vec2 f0 = fract(conv2d_last_tf_pos * conv2d_last_tf_size);
ivec2 i0 = ivec2(f0 * vec2(2.0));
float c0 = conv2d_last_tf_tex((vec2(0.5) - f0) * conv2d_last_tf_pt + conv2d_last_tf_pos)[i0.y * 2 + i0.x];
float c1 = c0;
float c2 = c1;
float c3 = c2;
return vec4(c0, c1, c2, c3) + MAIN_tex(MAIN_pos);
}
@@ -0,0 +1,969 @@
// MIT License
// Copyright (c) 2019-2021 bloc97
// All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(VL)-Conv-4x3x3x3
//!HOOK MAIN
//!BIND MAIN
//!SAVE conv2d_tf
//!WIDTH MAIN.w
//!HEIGHT MAIN.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (MAIN_texOff(vec2(x_off, y_off)))
vec4 hook() {
vec4 result = mat4(0.28296316, -0.020139743, 0.1038232, 0.09352482, -0.16964972, 0.07910997, -0.049914766, -0.10661066, -0.121037185, -0.029087039, -0.02511847, -0.078911744, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, -1.0);
result += mat4(-0.3927183, 0.01805193, -0.031168332, -0.13300525, 0.20814548, 0.118818566, 0.1655351, 0.095023684, 0.17600809, -0.03928444, -0.014350658, 0.08458312, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 0.0);
result += mat4(0.079089314, -0.0421829, 0.05452305, -0.22055493, 0.013279097, -0.12875281, 0.02452735, -0.101503745, -0.085946664, 0.05539176, 0.022408713, 0.14837204, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 1.0);
result += mat4(-0.102643915, -0.011254746, 0.1478563, 0.1030208, 0.12396588, 0.0016621432, 0.2551224, -0.10399001, -0.01068436, 0.07155532, -0.104522154, 0.026937222, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, -1.0);
result += mat4(-0.8789423, 0.35707328, -0.29964274, -0.064913996, 0.4962815, 0.26001287, -0.9511284, 0.49574667, 0.39539725, 0.16308042, 0.119878456, -0.30259115, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 0.0);
result += mat4(-0.08852938, -0.32612664, -0.006712046, 0.28693515, 0.06320871, -0.3322611, 0.04651086, -0.11020996, 0.01821082, -0.22851005, -0.07803438, 0.021527015, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 1.0);
result += mat4(0.12295851, -0.011285535, 0.015859747, 0.04005441, -0.018136669, 0.03171969, -0.0406123, -0.10731229, -0.12117574, 0.005033036, 0.047838476, 0.026843475, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, -1.0);
result += mat4(0.4655988, 0.05519082, 0.039515793, 0.28410903, -0.36144528, 0.13039446, 0.11338478, -0.2141387, -0.10026682, -0.07903024, -0.09410254, 0.043833878, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 0.0);
result += mat4(0.110124744, -0.024725702, 0.028102143, -0.09493807, -0.06455328, -0.15164614, 0.04425987, 0.15483347, -0.045039337, 0.07210396, -0.005390788, -0.03832707, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 1.0);
result += vec4(0.007907974, -0.035503313, 0.057224784, -0.19763541);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(VL)-Conv-4x3x3x3
//!HOOK MAIN
//!BIND MAIN
//!SAVE conv2d_tf1
//!WIDTH MAIN.w
//!HEIGHT MAIN.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (MAIN_texOff(vec2(x_off, y_off)))
vec4 hook() {
vec4 result = mat4(-0.012326053, 0.050769784, 0.1278702, -0.100782245, 0.14329414, -0.054558773, 0.023473471, 0.056829426, 0.048292916, 0.0046510273, -0.11478287, 0.0011030561, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, -1.0);
result += mat4(0.29542983, -0.55061895, -0.068554066, 0.1433222, -0.072878316, 0.30201668, -0.2223378, -0.06704077, 0.16955832, 0.3279914, 0.17619601, -0.1276919, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 0.0);
result += mat4(0.09623417, 0.30559412, 0.094622105, -0.076706685, 0.07943858, -0.084815115, 0.12472551, 0.079850115, -0.13044213, -0.21300878, -0.095747225, 0.13412355, 0.0, 0.0, 0.0, 0.0) * go_0(-1.0, 1.0);
result += mat4(0.21291664, 0.17195296, -0.20080926, 0.1064855, 0.10228669, -0.09580175, -0.11217631, -0.09740562, -0.0033135475, -0.053094357, 0.2983595, 0.035281878, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, -1.0);
result += mat4(-0.08955812, -0.45707774, -0.4606922, -0.5754473, -0.11395895, 0.33530128, 0.29705846, -0.18877256, -0.43502945, 0.114171304, -0.3750776, -0.081597246, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 0.0);
result += mat4(-0.26109028, 0.02662961, -0.10441071, 0.11199392, -0.12038989, -0.09642296, -0.061320662, -0.33058178, 0.20212512, 0.00840794, 0.14357455, -0.038080238, 0.0, 0.0, 0.0, 0.0) * go_0(0.0, 1.0);
result += mat4(-0.09533881, -0.13644339, 0.068756215, 0.079305276, -0.053370547, 0.19572955, 0.0682981, 0.14469264, 0.15582883, -0.057183057, -0.13919263, -0.016394936, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, -1.0);
result += mat4(-0.041189935, 0.39878023, 0.028704925, 0.30194348, -0.04486593, -0.33899093, -0.103968106, 0.21802065, -0.077099144, -0.07389541, 0.18069103, 0.18894517, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 0.0);
result += mat4(-0.12399862, 0.19246885, 0.034825478, -0.0044787163, 0.13121822, -0.13573012, -0.030162754, 0.1899518, 0.102326415, -0.061512686, -0.005647928, -0.0937634, 0.0, 0.0, 0.0, 0.0) * go_0(1.0, 1.0);
result += vec4(0.019286277, -0.033644073, 0.08196311, 0.0054393094);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_tf
//!BIND conv2d_tf1
//!SAVE conv2d_1_tf
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.04088509, -0.06585775, -0.3094732, 0.12059048, 0.041417453, -0.06144871, -0.06655134, 0.03308842, 0.09287731, 0.010969216, 0.10343026, -0.11185897, 0.05685865, -0.09490512, 0.040908635, 0.03501189) * go_0(-1.0, -1.0);
result += mat4(-0.04854754, -0.098667145, 0.67147833, -0.11299351, -0.022114437, -0.029202767, 0.014179382, 0.26027945, 0.22076549, -0.16490546, -0.0010764733, 0.08405975, 0.11849154, -0.19072372, -0.35719597, -0.059621073) * go_0(-1.0, 0.0);
result += mat4(0.079224996, 0.0669873, -0.1718969, -0.05002573, 0.044926763, -0.02904369, 0.017489236, 0.01144465, 0.059109706, 0.064998455, 0.14725484, -0.23879208, 0.039234288, -0.027365638, 0.26172164, -0.094598554) * go_0(-1.0, 1.0);
result += mat4(-0.07159218, -0.03181544, 0.113837324, -0.053089984, -0.098298974, -0.29500297, 0.1608509, -0.044355504, 0.050882854, -0.19417204, -0.080487266, -0.00879743, 0.0007914453, 0.16640955, 0.21786706, 0.16398212) * go_0(0.0, -1.0);
result += mat4(0.16324541, 0.19753313, -0.46597233, -0.2593132, -0.2781038, -0.21973547, -0.024623038, -0.16348499, 0.3628299, 0.044888914, 0.04054647, -0.63605887, 0.02099492, 0.060544077, -0.49359834, 0.36336297) * go_0(0.0, 0.0);
result += mat4(-0.16885692, 0.31907207, 0.020906802, 0.13290039, -0.037330728, -0.022859452, -0.020451576, -0.113437995, -0.085683934, 0.054102756, -0.044824492, 0.061346747, -0.038684413, 0.098444365, -0.06734984, -0.17084897) * go_0(0.0, 1.0);
result += mat4(-0.015821548, -0.119599186, 0.1614827, 0.08383641, -0.07933593, 0.12528986, -0.06300182, 0.09286327, -0.10199266, 0.02419403, 0.0028411683, -0.09028338, 0.07962534, -0.08676035, -0.19237737, -0.115502626) * go_0(1.0, -1.0);
result += mat4(0.09471972, 0.21153462, -0.14393018, 0.055180002, 0.1817461, 0.016607309, -0.0771979, 0.11181317, -0.5491086, -0.102757886, -0.20952754, 0.022466583, -0.075119644, -0.14725658, 0.38451517, 0.12920731) * go_0(1.0, 0.0);
result += mat4(0.0867803, 0.114654355, 0.21199988, -0.15367955, -0.01803536, 0.056378633, 0.0018388306, 0.024613786, -0.13306658, 0.017211098, 0.073351346, -0.12064064, -0.10484361, -0.067748636, 0.033206712, -0.13061953) * go_0(1.0, 1.0);
result += mat4(-0.002236411, -0.022144757, -0.04586377, 0.101181075, -0.03511624, 0.08440529, 0.18544284, -0.22786349, -0.042184375, 0.015734851, -0.038622506, 0.038529944, -0.09170703, 0.034527462, -0.07817406, 0.10547265) * go_1(-1.0, -1.0);
result += mat4(-0.12135524, -0.07412039, -0.04979351, -0.082267545, 0.13343571, 0.29196215, -0.4364121, -0.10226428, 0.060835477, -0.23307934, -0.018231759, 0.15550235, 0.09095689, 0.18164484, 0.1322021, -0.022567045) * go_1(-1.0, 0.0);
result += mat4(-0.0054531163, -0.039762255, -0.030490747, 0.04779882, -0.15290286, 0.056712102, -0.0776974, 0.04114215, 0.15946816, -0.03882117, 0.16770308, -0.026126247, -0.027203865, -0.064107865, -0.13670811, 0.1556276) * go_1(-1.0, 1.0);
result += mat4(-0.092548385, -0.027285473, 0.084179096, 0.014961629, 0.2564254, 0.07626849, 0.28534448, 0.2588713, -0.018600503, -0.2433456, 0.041392803, -0.045712482, 0.26388907, -0.053502295, 0.14522223, 0.032808404) * go_1(0.0, -1.0);
result += mat4(-0.0013780193, 0.3482449, 0.071003586, -0.30707207, -0.05122194, -0.2833618, 0.07910779, 0.051078696, 0.021535402, 0.13021478, 0.022049015, -0.533547, 0.57265025, -0.12843914, -0.14913581, -0.1433724) * go_1(0.0, 0.0);
result += mat4(0.07382619, -0.12152924, 0.13364957, 0.181974, 0.15804219, -0.10126773, 0.3029618, -0.12874149, 0.13743348, -0.23245592, -0.20119278, 0.029547188, 0.042436857, 0.04213892, -0.07975374, 0.023821082) * go_1(0.0, 1.0);
result += mat4(0.022782229, -0.08359311, -0.060623147, 0.06565042, 0.09828792, 0.044808697, -0.28872305, -0.00092168007, 0.021737702, -0.08698349, 0.1950025, 0.07931995, 0.040952396, -0.07443172, -0.021157127, 0.0056698937) * go_1(1.0, -1.0);
result += mat4(-0.09995892, -0.2047294, 0.1414849, 0.062335726, -0.22492298, 0.05269799, -0.029233055, -0.050517935, -0.12534393, -0.12194023, -0.07035469, -0.070764475, 0.18903446, 0.07691209, 0.06153371, 0.011280912) * go_1(1.0, 0.0);
result += mat4(-0.036189888, -0.07586571, -0.05888163, 0.010425367, -0.028375402, -0.18870986, -0.19146784, 0.19274063, -0.18856238, 0.0064240266, -0.14537223, -0.06971656, 0.0852742, -0.04866623, -0.031686075, 0.031702038) * go_1(1.0, 1.0);
result += mat4(0.0618941, 0.100858234, 0.2628019, -0.048507668, -0.051001363, -0.03195978, 0.035452217, -0.001991919, -0.09649028, -0.047445696, -0.09221298, 0.07602656, -0.02382384, -0.119645916, 0.085616075, -0.07076033) * go_2(-1.0, -1.0);
result += mat4(0.019222878, -0.0491929, -0.4902266, 0.18501294, 0.014529614, -0.077125326, 0.011563931, -0.20236616, -0.101982154, -0.021150962, -0.07537948, -0.1540349, 0.028949164, -0.06827332, 0.0067634755, 0.09582376) * go_2(-1.0, 0.0);
result += mat4(-0.05995539, -0.031138182, 0.01334257, 0.06827176, -0.030762246, 0.006615233, -0.03562788, 0.016249394, -0.14797118, 0.014671043, -0.09325859, 0.25653747, -0.11474991, 0.05436232, 0.031051394, 0.04179694) * go_2(-1.0, 1.0);
result += mat4(0.032279838, -0.030521005, 0.0029688699, 0.005165139, 0.15907808, -0.20421815, -0.07713175, 0.067530625, -0.08619395, 0.026114263, 0.08821273, 0.011591694, 0.018677557, 0.057708874, -0.25859246, -0.18693781) * go_2(0.0, -1.0);
result += mat4(0.10823143, -0.31875235, -0.24394153, -0.0025489891, 0.016761065, -0.19857498, -0.07858479, -0.07811158, -0.38551694, -0.049090322, -0.050053325, 0.23398961, 0.014974165, 0.17498055, 0.29105362, -0.353647) * go_2(0.0, 0.0);
result += mat4(0.05621677, -0.19492444, 0.460332, 0.055917628, -0.06404381, -0.06684098, 0.053624872, 0.057300456, -0.019248677, -0.15110065, 0.032379635, -0.12673225, 0.0068658157, -0.13001235, -0.017716292, 0.064182095) * go_2(0.0, 1.0);
result += mat4(-0.06764552, 0.004707433, -0.13827331, -0.21957871, -0.03789028, -0.04962028, 0.022955444, -0.058468018, 0.13735814, -0.031270552, -0.018490225, 0.0063876202, -0.052979283, -0.030049473, -0.004811771, -0.0044099926) * go_2(1.0, -1.0);
result += mat4(-0.028652798, -0.027029367, 0.62600744, 0.0900562, 0.03869923, -0.20111556, 0.095930666, -0.13164565, 0.5562579, 0.011937122, 0.22882107, 0.030288015, 0.09856272, 0.04736032, -0.077492185, -0.10207275) * go_2(1.0, 0.0);
result += mat4(-0.10581002, -0.16504957, -0.5688921, 0.0414545, 0.04749444, -0.052849945, -0.011017121, -0.025284614, 0.14316759, -0.08547362, -0.09654446, 0.08682504, 0.050776027, 0.0678741, -0.04913651, 0.07527552) * go_2(1.0, 1.0);
result += mat4(0.04126091, 0.0048704315, 0.041699376, -0.05820725, -0.09664279, 0.07648305, -0.17979898, 0.11698985, -0.025436765, 0.023232851, 0.010656572, 0.08157569, 0.19584864, -0.022928072, 0.053339157, 0.0039929505) * go_3(-1.0, -1.0);
result += mat4(0.040733483, 0.12260473, 0.08071146, 0.07257762, -0.016945919, -0.31637576, -0.24281953, -0.0038469466, -0.10203634, 0.13631973, 0.06505259, -0.13119389, -0.09723076, -0.139551, -0.07504509, 0.08645985) * go_3(-1.0, 0.0);
result += mat4(0.017005404, 0.049066268, -0.007544932, -0.04884536, 0.09984347, -0.04447364, 0.4902235, -0.062780835, -0.18389583, 0.07305648, -0.22014385, 0.08004685, 0.0992568, -0.08569604, 0.093966395, -0.07047139) * go_3(-1.0, 1.0);
result += mat4(0.0017705248, 0.020553982, -0.09167042, 0.0036356782, -0.11867446, -0.07055574, 0.40252638, 0.09657129, 0.0888632, 0.1031708, -0.022127641, -0.023769693, -0.0861388, 0.13420185, -0.11774454, 0.038774434) * go_3(0.0, -1.0);
result += mat4(-0.15173717, -0.13590458, -0.0891863, 0.12289548, 0.13942605, 0.22152588, -0.19292432, 0.14169839, 0.010543665, 0.07648361, -0.057333756, 0.09535759, -0.053601623, -0.026824495, 0.09365424, 0.17476946) * go_3(0.0, 0.0);
result += mat4(-0.070416056, -0.061970036, -0.039723337, -0.18874651, -0.07098426, -0.019835865, -0.5612458, 0.060437083, -0.03774378, 0.18536821, 0.28587544, 0.035555754, 0.15771326, -0.13527197, 0.13342534, -0.06564073) * go_3(0.0, 1.0);
result += mat4(-0.10967661, 0.025388904, 0.09003177, -0.04087592, 0.09531671, -0.11809294, -0.41613623, 0.038198076, 0.01019813, -0.018864965, -0.18400626, -0.038704176, 0.0105671035, 0.024449013, -0.008989595, -0.027171193) * go_3(1.0, -1.0);
result += mat4(0.16193569, -0.21445285, -0.20130903, -0.13498883, -0.008031679, 0.050757203, 0.78938776, -0.03749514, 0.11998137, 0.19368882, 0.12328945, 0.0058578993, -0.13852382, -0.033867255, -0.018267661, 0.036348555) * go_3(1.0, 0.0);
result += mat4(-0.06254118, 0.087295115, 0.031116437, 0.0416281, 0.061828617, 0.34479564, -0.15537797, -0.17144552, 0.13989387, -0.13792284, 0.056215156, 0.12714528, -0.0198865, 0.04927947, 0.013614583, -0.041810013) * go_3(1.0, 1.0);
result += vec4(-0.044073943, 0.12072677, -0.0022342638, -0.24414532);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_tf
//!BIND conv2d_tf1
//!SAVE conv2d_1_tf1
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.07115729, 0.01065505, 0.19167988, -0.02504489, -0.15064801, 0.079008736, 0.05437936, 0.027479589, -0.021383656, 0.032731537, -0.06657876, 0.022649521, -0.06501893, -0.02335689, 0.010445489, -0.05430297) * go_0(-1.0, -1.0);
result += mat4(-0.1178601, 0.07425715, 0.063272275, -0.18308601, -0.13955134, 0.005166404, -0.022591779, -0.016827974, -0.024990188, -0.13372071, -0.056342285, 0.12489847, 0.081861794, -0.07083351, 0.021897513, 0.0629395) * go_0(-1.0, 0.0);
result += mat4(0.051357627, -0.13874975, -0.09887168, -0.011908862, 0.03639772, -0.13195883, -0.05321156, 0.03913229, -0.08160194, -0.07128151, 0.043625016, 0.11966009, 0.03162217, 0.018834392, -0.0625129, 0.10726711) * go_0(-1.0, 1.0);
result += mat4(-0.15922394, -0.043482754, -0.22571066, 0.009280428, -0.3882705, 0.08418719, 0.15329506, -0.028419001, -0.011272379, 0.15897545, 0.041217074, -0.0143014155, 0.09451862, -0.056342427, -0.14568482, 0.05556279) * go_0(0.0, -1.0);
result += mat4(0.13879324, -0.23339099, -0.24573983, -0.09575104, 0.03823306, 0.4752516, -0.1696623, -0.18472373, -0.1510259, 0.23040254, 0.4196143, 0.3462817, 0.035172507, 0.18228662, 0.22475636, -0.19945027) * go_0(0.0, 0.0);
result += mat4(-0.08876766, 0.19567333, 0.25174314, -0.09637879, -0.007957943, 0.13510521, 0.030193076, -0.0018362573, -0.006884444, -0.41804117, -0.1026309, -0.053339038, -0.1283198, -0.03033918, 0.055674326, 0.094377995) * go_0(0.0, 1.0);
result += mat4(0.06780768, -0.07774435, -0.0616546, -0.046531744, -0.11723141, 0.10792474, 0.013314576, -0.031451598, -0.009870351, 0.10215877, -0.13101454, -0.19878799, -0.09712651, 0.10423937, 0.14170039, -0.03359521) * go_0(1.0, -1.0);
result += mat4(-0.020114673, -0.015194169, 0.03657608, 0.17162928, 0.070458665, -0.08041664, 0.14067306, 0.19699603, -0.28763783, -0.033556152, -0.6588468, -0.48221052, -0.123711474, -0.080758795, -0.3187303, 0.121004865) * go_0(1.0, 0.0);
result += mat4(-0.074900605, 0.09297913, -0.08621144, 0.116730206, -0.034766622, -0.10381484, 0.060793545, -0.014790814, -0.123858415, -0.0010626495, 0.20547503, -0.07206306, -0.17324795, 0.023932874, 0.017495958, -0.09924652) * go_0(1.0, 1.0);
result += mat4(-0.015568068, 0.005394868, 0.15463537, 0.06416607, -0.045670815, -0.013540727, -0.12960619, 0.0006581649, 0.09432853, 0.05575682, -0.022219105, 0.022416297, 0.0148129435, -0.067619696, 0.022989385, -0.09695771) * go_1(-1.0, -1.0);
result += mat4(-0.107209, 0.07072438, -0.10235772, -0.12078849, -0.02751833, -0.043195058, -0.17197154, 0.120612316, -0.17310137, -0.09429793, 0.06511165, 0.18072544, -0.21168593, 0.16383737, 0.25012484, -0.089589044) * go_1(-1.0, 0.0);
result += mat4(0.005439779, 0.0028433986, -0.09885586, -0.06572956, -0.0061691296, 0.15485546, -0.23724958, 0.004232802, 0.07794742, -0.012552598, 0.07554404, 0.10843201, -0.013223918, -0.08705092, -0.23228747, 0.03599732) * go_1(-1.0, 1.0);
result += mat4(-0.043396916, -0.10680695, -0.019935586, -0.06703658, -0.30075943, -0.010179525, 0.30197874, 0.04888297, 0.00779067, 0.22583807, 0.2039884, -0.0074303118, -0.19240093, -0.024718538, 0.057117213, 0.19431825) * go_1(0.0, -1.0);
result += mat4(-0.37633005, 0.043971814, -0.21423087, 0.118503235, -0.15058799, 0.115756795, -0.13719647, 0.020510519, 0.1123193, 0.14797291, 0.05467349, 0.2039607, -0.31973588, 0.1667847, -0.017739004, -0.11280262) * go_1(0.0, 0.0);
result += mat4(-0.0084394775, -0.1281101, -0.20841378, 0.01986435, -0.04122467, -0.21089631, -0.08062371, 0.11315133, 0.05693114, -0.23773515, 0.03792205, -0.008872407, 0.04554895, -0.10683658, 0.10683206, 0.06875721) * go_1(0.0, 1.0);
result += mat4(-0.103948504, -0.007483217, -0.12571928, 0.054868475, -0.030646881, -0.010098222, 0.019018777, -0.07072212, -0.10689893, 0.16498323, 0.048089568, -0.10912806, -0.027318537, -0.025491163, 0.012588013, 0.072701246) * go_1(1.0, -1.0);
result += mat4(0.14094622, -0.028118243, 0.016804086, -0.18000692, 0.33351874, 0.14980756, -0.07135749, -0.16573106, -0.17243773, 0.054617904, -0.2933543, -0.12602285, 0.08480712, -0.05704333, 0.22336398, 0.026583148) * go_1(1.0, 0.0);
result += mat4(0.046759557, -0.03100408, 0.40000245, -0.08521555, 0.19592628, -0.15150753, 0.25288078, -0.061794683, -0.047818147, -0.12249124, 0.020410215, -0.11503924, 0.046108168, 0.030459814, -0.14096366, 0.09120256) * go_1(1.0, 1.0);
result += mat4(-0.087491795, -0.024289595, -0.09060237, 0.020922959, 0.09557061, -0.08556962, -0.0503455, -0.010846053, 0.0030694185, -0.008256591, 0.08290225, -0.034981687, 0.07342003, -0.021816112, -0.13905519, -0.06265962) * go_2(-1.0, -1.0);
result += mat4(-0.08126147, -0.05866924, -0.015698025, 0.093630895, -0.02379264, 0.115918085, 0.19431724, 0.041815966, -0.051647816, 0.15277039, -0.03721037, -0.085520886, 0.041766718, 0.104392216, 0.0559556, 0.0049254233) * go_2(-1.0, 0.0);
result += mat4(-0.11176419, 0.112272635, 0.1367475, -0.010482275, -0.06719008, 0.064003386, -0.08132314, 0.015465676, 0.052741583, 0.06779717, 0.038533892, -0.16428822, 0.040990274, 0.002559234, 0.097567044, -0.058192518) * go_2(-1.0, 1.0);
result += mat4(0.17228632, 0.008296625, 0.009418271, 0.037103783, -0.0601486, 0.04531715, 0.19613501, 0.112170085, -0.02256726, -0.093685195, -0.1341531, -0.038480807, 0.109840475, 0.062418167, 0.15140085, 0.050787117) * go_2(0.0, -1.0);
result += mat4(0.15433665, 0.2104034, 0.12395812, 0.13799714, 0.14945604, 0.67457545, 0.27575177, -0.047493283, 0.24992993, -0.5305435, 0.0131732905, -0.36911693, 0.14442082, -0.18583177, -0.2861722, 0.19419897) * go_2(0.0, 0.0);
result += mat4(0.040242445, -0.13234852, 0.10056324, 0.055854917, 0.07447713, -0.023067042, 0.00021051937, -0.0495407, -0.22037992, 0.68047297, 0.05774606, -0.012461005, 0.104557075, 0.04832623, 0.010292581, -0.050617047) * go_2(0.0, 1.0);
result += mat4(-0.060079176, 0.086553656, 0.0060872175, -0.012576339, 0.025149338, -0.07379716, -0.18048704, -0.007130346, 0.007826557, -0.095655076, -0.0032888134, 0.21027069, -0.09868755, -0.1180311, 0.0081250835, -0.05078016) * go_2(1.0, -1.0);
result += mat4(0.19124818, -0.05949092, -0.36762074, -0.08203597, -0.10276991, 0.111005515, -0.2845309, 0.113985784, 0.07206471, -0.026585411, 0.20032002, 0.5691625, -0.0460136, 0.03874166, 0.09858682, -0.15913802) * go_2(1.0, 0.0);
result += mat4(-0.00397842, -0.014763085, 0.080231026, -0.09142265, 0.03637215, 0.064106315, -0.030963007, 0.0557953, 0.04173885, -0.024534896, -0.2092259, 0.06913638, 0.08103145, -0.0033994897, -0.10903093, 0.062850125) * go_2(1.0, 1.0);
result += mat4(0.01206918, 0.024855271, -0.051995132, 0.013999821, -0.021517826, 0.06216198, -0.050853133, -0.064136736, -0.047408275, -0.07858566, 0.074464396, -0.038218755, -0.13216262, 0.008905726, 0.10333, 0.03049554) * go_3(-1.0, -1.0);
result += mat4(-0.027152343, -0.069046065, -0.013017797, 0.0763, -0.08611993, -0.020867927, 0.012807627, -0.11971997, 0.025972975, 0.095127404, -0.070044935, -0.21399231, -0.22536097, -0.028828809, 0.123399965, -0.15967365) * go_3(-1.0, 0.0);
result += mat4(0.038314234, -0.014114242, 0.012115026, 0.05505015, 0.11785298, -0.08772618, 0.034408223, 0.09134674, -0.04727011, 0.020709611, -0.01780165, -0.14374214, -0.30412516, -0.011123043, -0.024216317, -0.007538433) * go_3(-1.0, 1.0);
result += mat4(-0.17673545, 0.077738725, 0.056153737, 0.028693894, 0.05688375, 0.021928595, 0.014585902, 0.019364892, 0.029056642, -0.2072201, -0.17548367, 0.085471265, 0.16439342, -0.0052957633, 0.22321554, -0.19246858) * go_3(0.0, -1.0);
result += mat4(0.1914782, -0.15620962, -0.16686897, -0.04141303, 0.07696967, -0.013115313, -0.057627093, -0.13849305, 0.08699377, -0.07339016, -0.053074118, -0.059418138, 0.19988623, -0.23852244, -0.12574267, -0.29139704) * go_3(0.0, 0.0);
result += mat4(-0.017691063, 0.18901291, 0.16250716, -0.11039392, 0.056900974, 0.036662772, -0.13399602, -0.11378214, -0.10924602, 0.2130181, -0.042094063, -0.012445028, 0.013168919, 0.119448364, -0.014406005, 0.0054324497) * go_3(0.0, 1.0);
result += mat4(0.11552786, 0.090796515, -0.11559005, -0.035706047, -0.044022456, -0.027642358, 0.08824298, 0.035067793, 0.18125483, -0.15502097, 0.094219126, 0.07493505, 0.022493582, 0.038250685, -0.076567575, -0.059311453) * go_3(1.0, -1.0);
result += mat4(-0.08612596, 0.016376335, -0.0023271537, 0.32511148, 0.03789289, 0.13106889, 0.22370385, 0.21145949, 0.1844514, -0.0766592, 0.093758754, 0.13821359, -0.062405586, 0.0028724174, -0.13588348, 0.00024406122) * go_3(1.0, 0.0);
result += mat4(-0.08991004, 0.074423954, -0.020964831, -0.070288494, -0.1192369, -0.015506713, -0.28136373, 0.042911243, 0.08215164, 0.11065419, -0.006201638, 0.057742044, 0.0014476188, -0.01443158, 0.22631277, -0.06787264) * go_3(1.0, 1.0);
result += vec4(-0.07235962, -0.019149294, 0.05072898, 0.03962245);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_1_tf
//!BIND conv2d_1_tf1
//!SAVE conv2d_2_tf
//!WIDTH conv2d_1_tf.w
//!HEIGHT conv2d_1_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_1_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_1_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.14315613, -0.031299837, -0.011195234, 0.0073360316, 0.07264984, -0.110979274, 0.06560588, -0.040463638, 0.28964168, -0.05644335, -0.060729366, -0.15811591, 0.028339373, 0.027486937, 0.0360574, 0.05856459) * go_0(-1.0, -1.0);
result += mat4(0.16211128, 0.20672597, -0.30374205, -0.056202736, -0.10893948, 0.053066984, -0.18297112, 0.028844962, 0.22754766, -0.07141921, 0.07142953, -0.1357581, 0.008053467, 0.04668908, 0.17258649, 0.22506891) * go_0(-1.0, 0.0);
result += mat4(0.07014762, 0.032112304, 0.028849715, 0.09427007, 0.008323501, -0.085777245, 0.083501115, -0.16150802, 0.24127382, -0.1305689, -0.027557204, -0.15057805, 0.09748757, 0.08182083, -0.107643455, 0.020552907) * go_0(-1.0, 1.0);
result += mat4(-0.04630706, -0.056070503, 0.058440026, -0.005662525, 0.08736355, 0.08821088, -0.049539115, 0.08171937, 0.28466523, -0.025859421, -0.0026971614, -0.15181617, -0.022231927, 0.3566104, -0.024887348, 0.12051598) * go_0(0.0, -1.0);
result += mat4(-0.20976813, -0.23778942, 0.28854275, -0.27583683, -0.27604774, -0.15861328, 0.09581984, 0.06572128, 0.092306405, -0.06962751, -0.042226445, 0.035234913, 0.084891975, -0.03846841, -0.1473667, 0.2810354) * go_0(0.0, 0.0);
result += mat4(0.028011162, 0.08945262, 0.15859836, 0.18426442, 0.10649845, -0.0918649, -0.12257575, -0.00914911, 0.23701023, -0.030067213, -0.01938559, -0.11026175, -0.5953985, 0.28875506, -0.035278864, -0.05043055) * go_0(0.0, 1.0);
result += mat4(-0.14445779, -0.06907616, 0.13078876, -0.0089114, -0.110637166, -0.123719245, -0.094949, 0.046267383, 0.4727523, 0.0073195575, -0.014788787, -0.14922102, -0.021974785, -0.10706751, 0.00049029186, 0.09215345) * go_0(1.0, -1.0);
result += mat4(-0.20936993, -0.22377276, -0.07697398, 0.039161056, 0.044213686, 0.037542075, -0.06600642, 0.017124292, 0.3406197, 0.011907687, 0.019732054, -0.22745137, -0.22178015, 0.49051985, -0.03707166, 0.14849792) * go_0(1.0, 0.0);
result += mat4(0.07833466, 0.10888627, 0.16015877, -0.049263358, 0.29002127, -0.010949114, 0.013081097, -0.071674205, 0.3532135, 0.013165473, -0.05282189, -0.16688257, 0.009552089, -0.2740816, 0.04927233, -0.37047002) * go_0(1.0, 1.0);
result += mat4(0.23682123, -0.027914839, 0.02372468, -0.07127212, 0.053436097, 0.057737537, -0.008556659, -0.025973454, 0.06468388, 0.18805866, -0.08180048, 0.058999106, -0.3058321, -0.06642967, -0.092997625, 0.10527466) * go_1(-1.0, -1.0);
result += mat4(-0.1353085, -0.016593851, 0.21518163, -0.10272456, 0.14382689, 0.05056661, -0.27799338, 0.11351653, 0.05838342, 0.28104934, -0.03777824, 0.003435516, 0.057915565, -0.17574134, -0.24437475, 0.13420977) * go_1(-1.0, 0.0);
result += mat4(0.13400255, -0.056437124, 0.11310834, 0.040429913, 0.098928474, -0.020769242, -0.079605736, 0.0494632, 0.0660877, 0.098982334, -0.055884495, -0.046533633, 0.17815505, 0.027310565, -0.24176653, -0.025550256) * go_1(-1.0, 1.0);
result += mat4(0.03637618, -0.012618673, 0.11865397, 0.19804053, -0.03522831, 0.24310908, -0.056454524, -0.44885796, 0.02212509, -0.20253624, 0.038810212, -0.17396528, 0.08970355, 0.005103078, 0.061075203, 0.44292897) * go_1(0.0, -1.0);
result += mat4(-0.25074747, -0.0015575301, -0.685015, 0.07345307, -0.08419402, 0.06640714, 0.43799296, -0.17571151, 0.0049855476, 0.09024738, 0.055744022, 0.018739637, 0.34734032, 0.114896655, 0.0404696, -0.11327049) * go_1(0.0, 0.0);
result += mat4(-0.12284062, -0.31131, -0.14712588, -0.18645866, 0.17581487, 0.1357234, 0.09913364, 0.005298711, -0.056155153, 0.042429443, 0.039454732, -0.04111384, 0.2623163, 0.09701166, 0.022825675, 0.050480727) * go_1(0.0, 1.0);
result += mat4(0.058734808, 0.038528245, -0.042670116, -0.15190329, -0.028179986, -0.05362995, 0.017090468, -0.24449602, -0.08240927, -0.033122182, 0.009938243, -0.0052937623, 0.2171439, 0.06879817, -0.10361997, 0.018995138) * go_1(1.0, -1.0);
result += mat4(0.027555468, 0.016337285, 0.19074728, 0.26690376, -0.088713005, -0.0021182299, -0.23062791, -0.32101163, -0.0040022335, 0.16835448, 0.05424022, -0.02156396, 0.24163729, 0.10243619, -0.04331782, -0.014350939) * go_1(1.0, 0.0);
result += mat4(-0.13836963, 0.053369813, 0.036432605, 0.062288612, -0.06264361, -0.049093347, -0.0315955, -0.11237456, -0.064744405, -0.0151798045, 0.044210885, 0.010166375, -0.038355727, -0.05203739, -0.075036794, 0.1664177) * go_1(1.0, 1.0);
result += mat4(-0.08583114, 0.08268218, -0.05771351, 0.10195048, -0.10128163, 0.10874855, -0.02580701, 0.028834302, 0.1950179, -0.0130183315, 0.0092119705, -0.060479227, 0.117747106, 0.061403573, -0.0028475628, -0.032362986) * go_2(-1.0, -1.0);
result += mat4(-0.05310153, -0.061091065, 0.19438389, -0.10475873, 0.00045120303, -0.24876194, 0.017168125, -0.050173752, 0.012073283, 0.035660096, -0.017562328, -0.110271364, -0.015546384, 0.17965329, 0.10068208, -0.014481325) * go_2(-1.0, 0.0);
result += mat4(0.085558474, -0.0007109211, 0.20868625, 0.150163, -0.19283043, 0.025976779, 0.08384698, 0.031011146, 0.17268184, 0.008871077, -0.04097794, -0.12868725, 0.01336166, -0.038823843, 0.1703644, -0.067780636) * go_2(-1.0, 1.0);
result += mat4(0.06480841, -0.44256654, -0.19949587, -0.030677497, -0.27930573, -0.041867044, -0.15648738, 0.11573067, 0.28664824, 0.009770385, -0.058617204, -0.06607673, -0.038160402, 0.009497089, 0.03303058, -0.079379834) * go_2(0.0, -1.0);
result += mat4(0.17752203, 0.10979527, -0.058749028, -0.30194217, 0.30484176, -0.20980492, -0.05234784, -0.2590473, 0.23003183, 0.21903595, -0.024891363, -0.14337292, -0.02971356, -0.29613075, -0.045642294, 0.23826689) * go_2(0.0, 0.0);
result += mat4(0.018211683, -0.005840598, -0.19021381, -0.096696235, 0.39998052, -0.34746838, -0.039627917, 0.087701194, 0.15526368, -0.008095372, -0.044220537, -0.08634815, -0.121496454, -0.06792033, -0.14959472, 0.078917444) * go_2(0.0, 1.0);
result += mat4(0.33109078, 0.012287281, -0.034155898, -0.04840956, 0.068748444, 0.006142039, 0.06598935, 0.024775596, 0.22379673, 0.056089353, -0.006119644, -0.018509025, 0.10084137, 0.15556572, -0.041211523, -0.21550669) * go_2(1.0, -1.0);
result += mat4(-0.058160853, 0.08899222, -0.17401625, -0.1449813, -0.015872562, -0.03780256, 0.15702572, 0.34013954, 0.1580772, 0.074823864, 0.035488904, -0.01627819, -0.15551315, -0.3638866, -0.09833458, 0.15037175) * go_2(1.0, 0.0);
result += mat4(-0.12707977, -0.19947061, -0.11524648, 0.09216174, -0.07161296, 0.05675567, 0.06843247, 0.2803306, 0.25222927, -0.044076066, 0.053775772, -0.09939824, 0.16903089, 0.11475717, -0.07015584, -0.036021322) * go_2(1.0, 1.0);
result += mat4(-0.12290332, -0.05469477, 0.02696626, 0.051133692, -0.05541504, -0.2811521, -0.13008943, 0.031793896, -0.32529324, -0.01663752, -0.0658181, 0.17300756, 0.22281154, -0.11001508, 0.09578194, -0.055437982) * go_3(-1.0, -1.0);
result += mat4(0.083753526, -0.048933715, -0.13912897, 0.10929772, -0.1789828, -0.1586524, -0.10964165, -0.08210391, -0.11568187, -0.04813496, -0.2417861, 0.24446528, 0.13570863, -0.26869404, 0.3013413, 0.11678686) * go_3(-1.0, 0.0);
result += mat4(0.21105368, 0.15749952, -0.18983693, -0.023642758, -0.1633653, 0.10107988, 0.052329395, -0.080253236, 0.15375629, -0.045091413, 0.05070866, 0.12416106, 0.16600485, -0.10412354, 0.061849747, -0.084013924) * go_3(-1.0, 1.0);
result += mat4(0.03863923, 0.03690167, -0.053106382, -0.07523278, -0.04214836, 0.53898096, 0.15308584, 0.22835171, -0.24771535, 0.1402687, 0.1000896, -0.08719167, 0.0886567, 0.15255097, 0.14695966, -0.06659865) * go_3(0.0, -1.0);
result += mat4(0.110334344, -0.12696493, 0.24256139, 0.02536166, 0.08322421, 0.022147777, -0.35030407, 0.13734557, 0.053133942, 0.43650532, -0.30170345, 0.08751837, 0.012917502, 0.27496436, 0.11422729, 0.15508565) * go_3(0.0, 0.0);
result += mat4(0.16684863, 0.26743406, 0.15951683, 0.033597723, -0.044719726, 0.1127182, 0.007923161, 0.06415458, -0.07269362, -0.07828715, 0.09216738, 0.11528897, -0.13371283, -0.124177165, 0.14804523, 0.14156726) * go_3(0.0, 1.0);
result += mat4(-0.041141883, 0.023617791, 0.11484465, 0.13131519, -0.14753738, 0.17067687, -0.017538434, 0.24042644, -0.058103643, 0.3143255, 0.02476919, -0.0024666793, -0.26759955, -0.06099211, 0.006415725, 0.10394301) * go_3(1.0, -1.0);
result += mat4(-0.04198037, 0.03277123, -0.25069895, -0.21043587, -0.27417016, 0.08047665, 0.29731026, 0.07629813, -0.15695353, -0.14299184, 0.026618432, 0.13265325, 0.07727133, 0.12872085, 0.13887435, 0.1347057) * go_3(1.0, 0.0);
result += mat4(0.039232086, 0.117847264, -0.071643315, -0.040677182, -0.029160816, -0.06968689, 0.12880929, 0.037579957, -0.036671028, -0.022678757, -0.069731854, 0.10590314, 0.028034678, -0.015759282, 0.047180142, -0.16366881) * go_3(1.0, 1.0);
result += vec4(-0.079253934, 0.001511763, 0.100159355, 0.01585197);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_1_tf
//!BIND conv2d_1_tf1
//!SAVE conv2d_2_tf1
//!WIDTH conv2d_1_tf.w
//!HEIGHT conv2d_1_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_1_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_1_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_1_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.024126908, 0.01737047, 0.04563732, 0.08303721, -0.21339902, 0.00025652428, -0.09666459, -0.07654246, -0.01201168, 0.14373912, 0.22268519, 0.049181588, -0.0751725, 0.006847365, -0.025867194, 0.19233267) * go_0(-1.0, -1.0);
result += mat4(-0.25251204, -0.34213448, -0.0022676045, 0.29270738, 0.08876456, 0.067294724, 0.2865476, -0.009144941, -0.074606106, 0.14566834, 0.14162645, 0.10980335, -0.7958991, -0.15410729, 0.038512416, -0.17033637) * go_0(-1.0, 0.0);
result += mat4(-0.115404196, -0.11004134, 0.13174473, -0.0006875606, 0.0051814034, 0.058522645, -0.0795437, 0.0011465811, -0.019500278, 0.12752724, 0.16985136, -0.054932587, 0.16734739, -0.04686017, -0.072241016, 0.054562975) * go_0(-1.0, 1.0);
result += mat4(-0.07528159, -0.113516726, 0.2081102, 0.009942251, 0.08256535, 0.050133914, 0.012745932, 0.13902397, 0.009369715, 0.083261885, 0.17366019, 0.069754004, 0.030654406, -0.045856245, -0.055254143, 0.16265897) * go_0(0.0, -1.0);
result += mat4(-0.14366727, 0.24948351, 0.12160293, 0.10929859, -0.116071545, -0.11725494, -0.13926856, -0.026759636, 0.12723772, 0.1938045, -0.02745115, -0.0644584, -0.23854719, 0.059308372, -0.446269, -0.06978486) * go_0(0.0, 0.0);
result += mat4(0.21108554, -0.1717225, 0.066633105, 0.15418948, -0.08902029, 0.047925282, 0.15817304, -0.080941506, 0.007364865, 0.10506626, 0.20205018, -0.078695655, 0.14004812, -0.3195092, 0.19157887, -0.12697977) * go_0(0.0, 1.0);
result += mat4(-0.08145032, -0.14292753, 0.066565305, -0.061348185, -0.08738346, 0.011608093, -0.0024047727, -0.024741996, -0.11547277, 0.10013328, 0.21730538, 0.05598899, -0.17741105, 0.075944185, 0.027434295, -0.2550598) * go_0(1.0, -1.0);
result += mat4(-0.026223006, 0.11214396, -0.133987, 0.1303522, 9.32011e-05, -0.14755996, -0.14002979, -0.039624512, 0.045111652, 0.17618611, 0.17764348, 0.104528464, 0.20592515, 0.07240335, -0.27604735, 0.038880046) * go_0(1.0, 0.0);
result += mat4(0.17734227, -0.002935363, 0.07505682, -0.029969893, -0.024536638, 0.11236127, 0.119374484, 0.08002781, -0.003541722, 0.1428466, 0.1729824, 0.055412393, -0.04790376, 0.18020035, 0.05376964, -0.1520942) * go_0(1.0, 1.0);
result += mat4(-0.11352182, -0.019249126, 0.10782615, 0.03079928, 0.020381734, -0.08998433, -0.09211494, -0.054406203, 0.1828849, -0.07692097, 0.004733955, -0.026685018, -0.08044814, -0.071961075, 0.029184176, -0.22562811) * go_1(-1.0, -1.0);
result += mat4(-0.34489468, -0.07447471, 0.026422959, 0.33550653, 0.22130035, 0.059709545, -0.07646962, -0.18386386, 0.33911958, -0.07534871, 0.040870134, 0.051136248, 0.32681262, 0.20612194, -0.1609581, -0.70460784) * go_1(-1.0, 0.0);
result += mat4(0.27617922, 0.09758603, 0.05103887, -0.09281693, -0.007143339, 0.006635712, -0.055270564, -0.022629099, -0.13023081, -0.013819027, -0.038695697, 0.047280338, -0.13964762, 0.09852924, -0.10056262, -0.084967695) * go_1(-1.0, 1.0);
result += mat4(0.1370323, 0.030904075, -0.033860117, 0.08926374, -0.14616281, -0.29926816, -0.23738252, -0.21374625, -0.14039646, 0.11503669, 0.082101606, -0.061717354, 0.021357644, -0.10676707, 0.03214661, 0.029967157) * go_1(0.0, -1.0);
result += mat4(-0.29881296, -0.22195289, -0.3512607, -0.2277441, 0.033705913, -0.23267402, -0.119738854, -0.18925253, 0.068823405, -0.15160555, 0.2585695, 0.10484223, -0.012574211, 0.38808516, 0.2599094, -0.4991424) * go_1(0.0, 0.0);
result += mat4(-0.07474731, 0.22742131, 0.014462262, 0.08409484, 0.09579643, -0.0519534, 0.0007793075, -0.044820115, -0.010144471, -0.040506937, 0.0056340825, 0.057767954, -0.14988829, -0.05099549, 0.007204364, -0.07094934) * go_1(0.0, 1.0);
result += mat4(-0.05736621, 0.12072876, -0.02037183, 0.05012334, -0.1173538, -0.10062993, -0.0033958228, 0.0142556345, -0.011005385, -0.0066177617, -0.058390465, 0.048240293, 0.09835053, 0.17917523, -0.06466951, 0.017518612) * go_1(1.0, -1.0);
result += mat4(0.1413101, -0.30268928, -0.17851736, -0.10797371, -0.01964573, 0.14356858, -0.06759965, 0.17416531, 0.13905385, -0.017476829, 0.06541924, -0.044690568, -0.080723755, -0.08610206, 0.095347285, -0.09233214) * go_1(1.0, 0.0);
result += mat4(-0.07254187, -0.091158785, 0.018472971, 0.03514051, 0.018888336, 0.107934274, -0.018830854, 0.10007211, -0.053966418, -0.035646267, -0.031214178, -0.05980228, -0.13045661, -0.011743741, -0.03325275, 0.071065165) * go_1(1.0, 1.0);
result += mat4(-0.037697386, 0.054388218, -0.010934479, 0.2266702, 0.049999133, 0.017648092, -0.044225454, 0.21611899, -0.03805845, 0.054236397, -0.018563407, -0.060588073, -0.031215845, 0.075081706, 0.07333242, -0.09651128) * go_2(-1.0, -1.0);
result += mat4(-0.32236508, -0.0026381002, -0.30787975, 0.2963127, -0.13276175, 0.1058753, -0.12744896, 0.09749292, -0.02683677, -0.0041124597, 0.006103888, -0.09997201, 0.092101686, -0.08375288, 0.09641652, 0.053333007) * go_2(-1.0, 0.0);
result += mat4(0.027999232, -0.060004722, -0.009207874, -0.0952888, -0.038418446, -0.13316345, 0.099323496, 0.048450433, 0.0443969, 0.056023613, 0.1156147, 0.018980766, 0.040020484, 0.07555044, 0.0039174426, -0.044098593) * go_2(-1.0, 1.0);
result += mat4(-0.101029314, 0.33333415, -0.22052327, -0.035329416, 0.17229559, 0.12564908, -0.07879576, -0.09248896, -0.03239869, 0.022611454, 0.05610472, -0.02181683, -0.06347532, -0.077292696, 0.02005389, -0.078899406) * go_2(0.0, -1.0);
result += mat4(-0.028139396, -0.04349171, -0.019393284, 0.42110333, 0.37065667, 0.5282552, 0.43816927, 0.19155908, 0.051832534, 0.02050813, 0.030795977, 0.023960136, -0.27617985, 0.19165507, -0.005492024, -0.13349663) * go_2(0.0, 0.0);
result += mat4(5.0700226e-05, 0.21293098, -0.39902148, -0.058406413, -0.06766975, 0.1129277, -0.012398328, 0.025031524, 0.03519656, 0.06486415, 0.15710293, 0.014098051, 0.057754945, 0.116186336, -0.14429826, 0.051864166) * go_2(0.0, 1.0);
result += mat4(-0.012280755, 0.043744788, -0.06420968, 0.012739398, 0.043073926, 0.031230433, 0.00036492705, -0.039208546, -0.09329152, 0.06928111, 0.11622664, -0.009106846, 0.111528054, -0.020315262, 0.036427997, 0.15881014) * go_2(1.0, -1.0);
result += mat4(-0.066635534, 0.13901882, 0.0885122, 0.1030835, 0.08539728, -0.015466482, 0.0706688, -0.1611047, 0.02179479, -0.00048529037, 0.08708685, -0.00894464, -0.13046473, -0.21456988, -0.20666413, 0.049039323) * go_2(1.0, 0.0);
result += mat4(-0.100800075, -0.03772198, -0.095183305, -0.15150243, -0.08743059, -0.24299338, -0.019315414, -0.1574107, -0.013610722, 0.064871654, 0.058439128, 0.008972897, 0.10339555, -0.027356634, 0.07666196, 0.048524544) * go_2(1.0, 1.0);
result += mat4(0.046309173, -0.03858991, -0.13260359, 0.0017626585, 0.1453724, 0.1402359, -0.079240486, 0.13017912, 0.0629575, -0.15448172, -0.1856442, -0.044694453, -0.17226808, -0.08065212, -0.008038736, -0.15994963) * go_3(-1.0, -1.0);
result += mat4(0.18369722, 0.03849556, -0.035185467, -0.20205377, 0.03879293, 0.02712859, -0.051278092, 0.14862835, 0.10261192, 0.18085574, -0.025982017, -0.029160796, 0.5301373, 0.09614058, 0.35518438, -0.014906588) * go_3(-1.0, 0.0);
result += mat4(-0.31154996, -0.06868871, -0.012681131, 0.028093819, -0.37321633, -0.14738804, 0.06060776, 0.050054748, 0.013779029, -0.020390315, -0.12487434, -0.0029474346, -0.274524, -0.09142805, 0.0132142445, 0.1577639) * go_3(-1.0, 1.0);
result += mat4(-0.02177336, -0.020817943, -0.0111796055, -0.0046033757, 0.45033064, 0.3573757, 0.55279994, 0.602122, -0.05536106, -0.33642644, -0.1851379, -0.052192084, 0.03683446, 0.13613251, 0.20098919, -0.090587094) * go_3(0.0, -1.0);
result += mat4(0.1520822, 0.37173554, -0.061298244, 0.0019386727, 0.44656134, 0.13406622, 0.39018136, 0.5722051, -0.13074401, 0.012778576, -0.2837446, 0.16098566, 0.100189455, -0.40386122, 0.17464107, -0.17862785) * go_3(0.0, 0.0);
result += mat4(-0.01217905, -0.24295084, 0.08192982, -0.14160301, -0.05936872, -0.003312342, -0.07542139, 0.13488367, -0.21560493, -0.14342502, -0.19195864, -0.09448305, -0.1038431, -0.075766176, 0.03226791, 0.06455397) * go_3(0.0, 1.0);
result += mat4(-0.076916575, -0.10891301, 0.032635316, 0.03848802, 0.15750243, 0.48169684, 0.5410635, 0.017279895, 0.012730932, -0.0059071835, 0.030766146, -0.0225503, -0.030178519, -0.05866621, 0.033593398, -0.00033098995) * go_3(1.0, -1.0);
result += mat4(-0.10757409, 0.2644168, -0.025696747, -0.0077012815, 0.31728277, 0.29771668, 0.2443613, -0.047722775, -0.083712585, -0.12742844, -0.3138776, -0.059888497, 0.12291351, -0.14435866, 0.051414594, -0.11889901) * go_3(1.0, 0.0);
result += mat4(-0.063888945, 0.002844068, -0.06129518, 0.03381495, 0.10176077, -0.11625004, -0.10745763, -0.20636752, -0.03820934, 0.01926402, -0.20310643, 0.09767577, -0.00776684, 0.13453315, -0.036967937, 0.09780335) * go_3(1.0, 1.0);
result += vec4(0.019374544, -0.050425697, -0.005817216, -0.0059976326);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_2_tf
//!BIND conv2d_2_tf1
//!SAVE conv2d_3_tf
//!WIDTH conv2d_2_tf.w
//!HEIGHT conv2d_2_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_2_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_2_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.028328063, 0.038015317, 0.14893384, 0.10103896, 0.028176744, -0.02067147, -0.10970998, 0.015726602, -0.07402682, -0.075281784, -0.012586929, 0.053476278, 0.14823362, 0.20312984, 0.24213, 0.039118115) * go_0(-1.0, -1.0);
result += mat4(0.009731573, -0.019011121, 0.016360838, -0.0073153526, 0.14594506, -0.0427664, -0.094225354, -0.013891855, -0.037061375, 0.024959227, -0.12289382, -0.21792257, -0.33579424, 0.052678566, 0.04346115, 0.07943186) * go_0(-1.0, 0.0);
result += mat4(0.0022269129, 0.013298362, -0.045071404, -0.007918287, 0.010860651, -0.073057, -0.0042394064, 0.03340809, 0.04938919, -0.024218693, -0.008147567, 0.08848061, -0.06840333, 0.10077341, -0.272586, -0.06542769) * go_0(-1.0, 1.0);
result += mat4(0.15929016, -0.1415167, 0.057084452, 0.06830724, 0.0046992986, 0.068573505, 0.22142749, -0.18493174, -0.1006019, -0.11373546, 0.17520057, -0.12888812, 0.05176946, -0.14703397, -0.20610721, 0.16611591) * go_0(0.0, -1.0);
result += mat4(-0.0069309813, 0.22358349, -0.18569049, 0.13456121, -0.21528137, 0.04618922, -0.47261322, -0.09682613, 0.5402922, 0.15818685, 0.05288464, -0.09949312, 0.21833964, 0.06652228, -0.2694682, 0.58216536) * go_0(0.0, 0.0);
result += mat4(0.040808782, 0.023110595, 0.12678777, -0.09057271, 0.03159572, 0.044006016, -0.10090222, 0.09940838, -0.08454473, -0.118349984, -0.053009644, 0.24352531, -0.103818566, 0.12536442, -0.17832974, 0.25161982) * go_0(0.0, 1.0);
result += mat4(-0.026323501, -0.14911288, -0.0073903934, 0.06902844, 0.07188603, -0.05006621, 0.06539817, -0.048083752, -0.08032579, -0.07449341, -0.015944218, 0.032426495, 0.069349505, -0.07441237, 0.055614363, 0.065174624) * go_0(1.0, -1.0);
result += mat4(-0.046432327, -0.051616143, 0.017791865, -0.047294978, 0.025944458, -0.0020909954, 0.083794415, -0.055740435, -0.3720184, 0.06654654, 0.1944378, 0.07806658, 0.00870193, 0.005404396, -0.059417505, -0.06270168) * go_0(1.0, 0.0);
result += mat4(-0.011699918, -0.03260685, 0.016413182, -0.02199741, -0.042898953, -0.018734168, -0.12387174, 0.06405199, -0.050764065, 0.07050078, 0.006969675, 0.05508108, -0.079687595, 0.12154926, 0.071177684, 0.046873443) * go_0(1.0, 1.0);
result += mat4(-0.2158498, 0.03612371, -0.05268691, -0.065594874, 0.06997431, -0.07327132, -0.03323361, -0.23306306, -0.00011140713, -0.1891967, -0.017328357, 0.15796778, -0.061359044, 0.008202449, -0.031317197, -0.020873578) * go_1(-1.0, -1.0);
result += mat4(-0.022816254, -0.014594548, 0.0064240466, 0.07976367, -0.0070318123, -0.07651652, -0.111756384, -0.2788498, 0.16634429, -0.1583179, -0.10245271, 0.10480152, 0.345051, -0.07809675, 0.046080578, -0.32139245) * go_1(-1.0, 0.0);
result += mat4(0.020630263, 0.032152038, 0.0019161701, 0.05435833, 0.078139454, -0.10090956, 0.14244889, 0.017286595, 0.0039871824, -0.026395446, 0.14158171, 0.0010112645, 0.17055373, -0.08093189, -0.049234428, -0.33473247) * go_1(-1.0, 1.0);
result += mat4(-0.10982378, 0.029386539, -0.15483, -0.04345961, -0.16869037, -0.30862433, 0.060743757, -0.032285906, 0.017884266, -0.09846199, -0.090971105, -0.1693697, -0.063690096, -0.08489718, 0.18247683, -0.19921213) * go_1(0.0, -1.0);
result += mat4(0.1898742, 0.22187345, -0.28495324, -0.42578775, 0.12833633, -0.2251503, -0.025917793, 0.6011678, -0.36586264, 0.23302059, -0.072634645, 0.0064221635, 0.56792957, -0.4684677, 0.05015159, 0.30121225) * go_1(0.0, 0.0);
result += mat4(0.10837159, 0.14743729, -0.03639783, -0.34797576, -0.18306817, -0.07957882, -0.111433275, 0.30104128, -0.102763996, -0.01020151, 0.016333267, -0.012390819, 0.11835027, -0.12597388, -0.006298998, 0.0513052) * go_1(0.0, 1.0);
result += mat4(-0.23662986, 0.23325302, -0.046104953, 0.36488137, 0.06990537, -0.06887873, -0.012611426, -0.02618366, -0.05296669, 0.195254, 0.016366778, 0.01693462, -0.08488424, -0.24656284, -0.035283253, -0.15318634) * go_1(1.0, -1.0);
result += mat4(0.061704446, -0.26930714, -0.24598889, 0.27657726, 0.05046488, -0.341884, 0.10704377, -0.15971762, 0.072999336, -0.2005826, -0.05874223, -0.053938035, -0.08284583, -0.22792995, 0.1027033, -0.012932447) * go_1(1.0, 0.0);
result += mat4(-0.029079054, 0.14774945, 0.026151389, 0.12380946, 0.08926635, -0.08387116, -0.17018612, -0.09304499, 0.086990625, -0.27579373, 0.003823722, -0.024723161, 0.08762848, -0.10080674, -0.012214886, -0.30239874) * go_1(1.0, 1.0);
result += mat4(-0.25756493, 0.2537789, 0.21723714, 0.0017929028, -0.014724892, 0.086692676, 0.11934202, -0.025869334, 0.008618976, -0.0046638376, -0.06863114, -0.07598151, -0.17309345, 0.009138105, -0.099874064, 0.07377463) * go_2(-1.0, -1.0);
result += mat4(-0.39971545, 0.16774859, 0.13102596, 0.30735064, -0.060374007, -0.036933452, 0.14408773, 0.06479284, 0.03806265, 0.045560133, 0.043136165, -0.019244662, 0.17573427, -0.11398941, -0.0751098, 0.041702736) * go_2(-1.0, 0.0);
result += mat4(-0.074492976, 0.18349282, -0.009050458, 0.0869807, -0.23123743, -0.015426683, -0.14346392, 0.005445149, -0.05322614, 0.10287576, 0.16083732, -0.09557319, -0.13891783, -0.13752605, -0.023572346, 0.13608918) * go_2(-1.0, 1.0);
result += mat4(-0.31140685, 0.40130782, 0.07704675, 0.27509958, 0.09711739, -0.18293281, -0.14500841, -0.15334702, 0.098314695, 0.22749798, 0.006017282, -0.013669673, 0.07147038, 0.022289474, -0.036797456, -0.0013958871) * go_2(0.0, -1.0);
result += mat4(0.0547557, -0.03036202, 0.65113044, 0.10668893, 0.304707, -0.1456157, 0.27668485, 0.2279428, -0.42439902, -0.0073047588, 0.045635667, 0.271637, -0.19595222, -0.28107607, 0.3905438, -0.29898256) * go_2(0.0, 0.0);
result += mat4(0.076843366, 0.037181348, 0.08652873, 0.1756985, 0.03728033, -0.22783624, 0.16810594, -0.022009399, 0.16058537, 0.24559903, 0.05266939, -0.13929726, 0.15964857, 0.0013167082, 0.015017631, 0.101646364) * go_2(0.0, 1.0);
result += mat4(-0.3022452, 0.20052882, 0.13433233, 0.04250016, -0.15248592, 0.014216527, -0.23489903, 0.13919333, 0.22891816, -0.0053335144, -0.05567782, -0.12769286, -0.05337762, -0.11429989, -0.00882089, -0.030790573) * go_2(1.0, -1.0);
result += mat4(-0.11763547, 0.1073185, 0.15810886, 0.013149736, -0.028268294, -0.24712053, 0.08592036, 0.075742744, 0.19626461, -0.10880887, -0.22599675, -0.37207767, -0.032548983, -0.011045266, -0.035218395, 0.099996395) * go_2(1.0, 0.0);
result += mat4(0.05631665, 0.029538663, 0.043909863, 0.13720988, 0.10980592, -0.047748722, 0.080308706, -0.06828442, 0.1144396, -0.12510885, -0.067976676, 0.030742755, 0.07134681, -0.06652312, -0.0642328, -0.034490924) * go_2(1.0, 1.0);
result += mat4(0.019588284, -0.15197967, -0.16797094, -0.026324488, 0.014429439, -0.028491383, 0.059453625, 0.23443304, 0.02504347, 0.08872467, 0.032782357, -0.085310735, 0.013040259, -0.09837991, 0.073533125, -0.03544458) * go_3(-1.0, -1.0);
result += mat4(0.02198588, -0.09614766, 0.024655875, 0.025384603, 0.012162857, 0.065071434, 0.018112874, 0.19828922, -0.33289856, 0.011323505, 0.13696423, 0.31772846, -0.06587399, -0.05569957, -0.16469179, -0.22545892) * go_3(-1.0, 0.0);
result += mat4(-0.009093827, 0.086783886, 0.060070645, 0.049957857, 0.041628215, 0.082412794, 0.117729135, -0.178277, 0.08326062, -0.07120824, 0.1788718, 0.050748438, -0.08952197, -0.14609487, 0.05515471, 0.14784457) * go_3(-1.0, 1.0);
result += mat4(-0.10823147, -0.05108019, 0.092807196, -0.13899301, 0.19123949, -0.044189975, 0.0030145745, 0.08935499, -0.10338727, 0.01996205, 0.15671325, -0.08229972, 0.05603653, 0.043324884, 0.13562247, -0.11487494) * go_3(0.0, -1.0);
result += mat4(-0.18872134, -0.07302765, 0.030137405, 0.30928415, -0.07689583, 0.045998566, 0.45554903, -0.1653404, 0.14705873, -0.10649596, 0.46833125, 0.17608039, -0.43967086, 0.056812476, -0.17908083, -0.40455228) * go_3(0.0, 0.0);
result += mat4(-0.08093384, 0.032636635, 0.124594346, 0.13655491, 0.16780408, -1.4671803e-05, 0.13044862, -0.397665, -0.013273644, 0.08253894, 0.16302188, -0.052874118, 0.04073075, -0.18063635, -0.00838661, -0.31084144) * go_3(0.0, 1.0);
result += mat4(0.06804371, -0.14755388, -0.12055216, -0.00437858, -0.044694718, 0.22744909, 0.012434794, 0.06245207, 0.00560859, -0.15815294, -0.19711316, 0.07711764, 0.03078979, -0.09560189, 0.10509056, 0.010651465) * go_3(1.0, -1.0);
result += mat4(-0.026342146, 0.13919179, -0.0030414977, 0.06607403, 0.071292974, 0.065464914, -0.027091878, 0.10620255, -0.052090824, 0.06840278, -0.08457357, 0.08867469, 0.2976581, -0.6702739, -0.15472057, -0.3066263) * go_3(1.0, 0.0);
result += mat4(-0.00072869845, 0.046573937, -0.08363707, 0.07867379, 0.038065, 0.01228845, 0.031746328, -0.024448024, -0.065555945, 0.1220454, 0.032151606, -0.022336006, -0.0010816467, -0.026455112, 0.112422734, -0.10285581) * go_3(1.0, 1.0);
result += vec4(0.052450567, 0.10404023, -0.059578225, 0.009724121);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_2_tf
//!BIND conv2d_2_tf1
//!SAVE conv2d_3_tf1
//!WIDTH conv2d_2_tf.w
//!HEIGHT conv2d_2_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_2_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_2_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_2_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.037506457, -0.06573841, -0.087879084, -0.06359248, -0.0017873603, -0.009097742, 0.010108622, 0.026364084, 0.012306545, 0.12607974, -0.088268295, 0.14034338, 0.24951904, 0.0983314, 0.03635719, -0.047059253) * go_0(-1.0, -1.0);
result += mat4(-0.05570699, 0.11044774, 0.04827364, -0.03185735, -0.032498132, -0.062959515, 0.2933071, 0.22244357, 0.061075654, 0.0064111133, 0.011452209, 0.11576761, 0.13969804, 0.20502032, 0.1114938, 0.022496287) * go_0(-1.0, 0.0);
result += mat4(-0.054194342, 0.000389916, -0.039589155, -0.018707246, -0.036095835, -0.06873059, -0.077109694, 0.028726012, -0.08820959, -0.109247595, -0.05745309, 0.043230128, 0.033671502, 0.16398554, 0.030398889, -0.17000203) * go_0(-1.0, 1.0);
result += mat4(-0.09218165, -0.12813722, -0.040984686, -0.016605416, 0.054269493, 0.12971285, -0.013961638, -0.17803082, -0.014683587, 0.2502267, -0.14249405, -0.025687713, -0.097426265, -0.30111355, -0.21776466, 0.008809217) * go_0(0.0, -1.0);
result += mat4(0.21033873, 0.15221386, 0.18138756, -0.08248389, -0.10091519, -0.06940753, -0.014009188, -0.3009861, -0.02452202, -0.08800422, -0.36376888, 0.18485394, 0.35076657, -0.13293292, 0.24624826, 0.39373755) * go_0(0.0, 0.0);
result += mat4(0.014170062, -0.029623963, 0.057001226, 0.09269898, -0.14630881, -0.16557585, 0.06735037, -0.015008042, -0.27238864, 0.081130914, -0.07869508, 0.098087415, 0.11217335, 0.48223323, 0.18613088, -0.035602476) * go_0(0.0, 1.0);
result += mat4(-0.21623239, -0.1125095, -0.09964635, 0.101452544, 0.11877652, 0.13471957, -0.10402355, 0.0077938605, 0.030518647, 0.22309083, -0.2115206, 0.017967062, -0.042780407, 0.099759325, -0.10465051, -0.033807248) * go_0(1.0, -1.0);
result += mat4(-0.059608232, 0.06684556, 0.00039066386, 0.08542961, 0.097183906, -0.1868667, 0.07778909, -0.06172202, 0.0021662437, -0.05387577, -0.4077133, -0.028940776, 0.110816136, -0.04154161, 0.030078325, 0.072834246) * go_0(1.0, 0.0);
result += mat4(-0.01881586, -0.06384429, -0.054874837, -0.016731417, -0.06570834, -0.13579571, 0.0033891131, -0.059161015, -0.11559389, 0.02149361, -0.08791608, -0.008113861, 0.08313892, -0.07327947, -0.013473171, 0.13254371) * go_0(1.0, 1.0);
result += mat4(-0.11458958, -0.08827364, -0.025030116, 0.12626298, 0.0070429775, 0.0337767, 0.051719055, -0.09654129, -0.04867615, -0.03609001, -0.06522421, -0.044131942, -0.048825134, 0.10652733, -0.015310965, -0.07341175) * go_1(-1.0, -1.0);
result += mat4(0.05782829, 0.014247012, 0.12126171, 0.100055166, 0.24079333, -0.20155986, 0.1640186, -0.12158374, -0.153708, -0.24445893, -0.10536192, 0.12758626, -0.19430119, -0.019024884, -0.080120996, -0.29866305) * go_1(-1.0, 0.0);
result += mat4(-0.017357074, 0.04390695, 0.12889594, 0.11451521, 0.03333342, -0.16417275, 0.10196121, 0.13059081, 0.09948873, 0.15007107, 0.22664218, 0.35449567, -0.089776486, 0.025239054, 0.12463201, -0.13109131) * go_1(-1.0, 1.0);
result += mat4(0.064875744, 0.40551752, 0.11903257, 0.14822967, 0.14993542, -0.12758526, 0.23159283, -0.06080246, -0.084577255, 0.14307548, -0.02186462, 0.05793564, -0.050965074, 0.23895216, -0.07796932, -0.1624384) * go_1(0.0, -1.0);
result += mat4(-0.15942748, 0.07191155, 0.42204422, 0.35219797, 0.23286703, -0.283381, -0.2749432, 0.25922084, 0.10494953, 0.14575887, -0.19649154, -0.14563714, -0.03709703, 0.023375817, -0.05610175, -0.32548484) * go_1(0.0, 0.0);
result += mat4(-0.04872624, -0.3592348, -0.027413938, 0.0836858, 0.046842758, -0.35193914, 0.06154142, 0.05559191, -0.22538327, -0.097689696, -0.21317257, -0.033945527, -0.23628096, -0.016477302, 0.027297588, -0.04105733) * go_1(0.0, 1.0);
result += mat4(0.11543502, -0.043297376, 0.118703, 0.15013209, 0.03191795, 0.014122794, 0.05156918, 0.023102578, 0.0808462, -0.06445798, 0.15860644, -0.062393136, -0.018691704, -0.00032888897, 0.01196705, -0.025045555) * go_1(1.0, -1.0);
result += mat4(0.08301664, 0.12298539, 0.20151077, 0.2993159, 0.16968682, -0.18196446, -0.13322797, -0.13693243, -0.0048389523, -0.057406515, 0.21409932, -0.060822334, -0.08554752, -0.19363636, -0.35241908, -0.32256603) * go_1(1.0, 0.0);
result += mat4(-0.0523748, 0.17082025, 0.08556144, 0.19181536, -0.2445756, -0.3616732, -0.01641404, -0.078599006, 0.23907976, 0.025989126, 0.07574993, -0.06859337, -0.06667767, -0.022847861, -0.037942342, -0.21112117) * go_1(1.0, 1.0);
result += mat4(0.15098672, 0.024212115, -0.19068481, -0.22606348, -0.15221487, -0.032165635, -0.06244531, -0.043535717, -0.07398802, -0.06088507, -0.013834592, -0.10145823, 0.06901983, -0.0862135, -0.05545454, 0.15514566) * go_2(-1.0, -1.0);
result += mat4(0.044767097, -0.07583697, -0.17739761, -0.25538698, 0.0966659, -0.0013492911, -0.23315248, -0.21652249, -0.14381947, 0.017784966, -0.15960035, -0.13297895, 0.009810349, -0.041348267, 0.05443229, 0.17781278) * go_2(-1.0, 0.0);
result += mat4(-0.0052824756, 0.087268956, -0.022167318, -0.09450279, 0.1254372, 0.075806946, 0.028893303, -0.09019378, 0.03488572, 0.046265777, 0.026162563, 0.003914548, -0.0632334, -0.19494742, -0.03602023, 0.113897055) * go_2(-1.0, 1.0);
result += mat4(-0.11311528, 0.2616239, 0.12303548, 0.13427438, -0.26537886, 0.015112677, -0.03641703, -0.014114427, -0.023280613, 0.03626403, 0.12833157, 0.19168468, 0.2119137, -0.02374797, 0.117919676, 0.07794395) * go_2(0.0, -1.0);
result += mat4(-0.13746078, 0.25739196, 0.008431936, -0.053867325, -0.13228695, -0.20661803, 0.026474724, 0.3205188, -0.41819036, 0.42812085, 0.17249924, -0.15810613, 0.39602605, -0.10873597, 0.1457145, -0.060503867) * go_2(0.0, 0.0);
result += mat4(0.03706167, -0.036211733, 0.06519942, -0.2123978, 0.019934088, 0.17494182, -0.017252771, -0.067341134, -0.15416612, -0.114118524, -0.00028491023, -0.08172238, -0.11722721, -0.2647645, 0.13316637, 0.13562322) * go_2(0.0, 1.0);
result += mat4(0.11832847, 0.22822993, 0.020318847, 0.0734738, -0.025950216, -0.072782144, 0.11133989, 0.18845533, -0.004584898, -0.10486471, 0.054522812, -0.14136603, 0.01940983, -0.039433163, 0.008390286, 0.013686628) * go_2(1.0, -1.0);
result += mat4(-0.042335663, 0.0035399816, -0.1813205, -0.25639042, 0.1042524, 0.07707001, -0.04922454, 0.18140413, -0.22322963, 0.030809738, -0.11041754, -0.040288754, 0.09431559, -0.08017892, -0.18317147, -0.019331435) * go_2(1.0, 0.0);
result += mat4(-0.061776266, 0.0069793356, 0.019964112, -0.14504445, -0.00070097746, -0.027107855, 0.030182542, -0.05625612, -0.04958449, 0.123165295, 0.0013953283, 0.017912487, 0.031161329, -0.31798717, 0.018331604, 0.030411277) * go_2(1.0, 1.0);
result += mat4(-0.0530594, -0.07933117, 0.024755973, 0.004785411, 0.045512546, 0.12833083, 0.023195961, -0.018028054, 0.014223835, 0.102213494, 0.052169293, -0.020509718, 0.017905682, 0.021354724, -0.0410789, -0.066523656) * go_3(-1.0, -1.0);
result += mat4(0.017061293, -0.08770806, -0.04889939, 0.01825556, -0.03228951, -0.06838898, -0.09249373, 0.18103507, 0.087000825, 0.04175679, -0.09305919, -0.2792485, 0.03405797, 0.062147446, -0.04757652, -0.021603985) * go_3(-1.0, 0.0);
result += mat4(-0.04115162, 0.02547615, 0.07033616, 0.09814065, 0.2597489, -0.0335038, 0.14097647, 0.047022782, 0.1374654, -0.27390274, 0.02080897, -0.15251215, -0.025431091, 0.08871465, -0.22243279, -0.07792812) * go_3(-1.0, 1.0);
result += mat4(-0.061674852, -0.051326606, -0.04885301, 0.08548189, -0.07100394, 0.044875987, -0.19810183, -0.09841128, -0.06628199, -0.041564234, 0.1111919, -0.044448826, 0.06980301, 0.00046094303, -0.045978926, -0.20736355) * go_3(0.0, -1.0);
result += mat4(-0.18405268, -0.28115878, -0.33536536, 0.0753763, 0.028309148, 0.0014874876, 0.28369543, -0.2133985, 0.16520546, 0.29562506, 0.109781906, 0.028433772, -0.02691105, -0.39038795, -0.12942268, -0.080103286) * go_3(0.0, 0.0);
result += mat4(-0.05387814, -0.04672615, 0.046064686, 0.2791977, 0.11359623, -0.204098, -0.018091407, 0.13550591, 0.04216003, -0.1631328, -0.043013666, -0.045698896, 0.032403514, 0.010206319, -0.25789943, -0.36328712) * go_3(0.0, 1.0);
result += mat4(0.11280466, 0.11671405, -0.02122692, 0.021664057, -0.07836575, 0.014747725, 0.030007286, -0.10128616, -0.13695373, -0.10353946, -0.043571353, 0.05922437, -0.11293257, 0.0828006, -0.07322761, -0.08197273) * go_3(1.0, -1.0);
result += mat4(-0.0010509897, -0.1674067, 0.08191839, 0.056608744, 0.061343428, 0.19574693, 0.05302967, -0.006813754, -0.016064182, 0.22949885, -0.06631832, 0.034382205, 0.12674272, 0.06583508, 0.19319807, 0.011400221) * go_3(1.0, 0.0);
result += mat4(-0.032175347, -0.021227444, -0.027698517, 0.067299634, 0.23929007, 0.20669897, 0.004856941, 0.0009404045, 0.04919408, 0.020296812, 0.012571405, -0.16185577, -0.012276781, 0.16609742, -0.15718406, -0.20344186) * go_3(1.0, 1.0);
result += vec4(0.022815697, 0.012251767, 0.045309987, -0.0879881);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_3_tf
//!BIND conv2d_3_tf1
//!SAVE conv2d_4_tf
//!WIDTH conv2d_3_tf.w
//!HEIGHT conv2d_3_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_3_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_3_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.010501252, -0.046741538, -0.0017120431, -0.04840009, 0.20547974, 0.3366821, -0.10182207, 0.17451541, -0.03404171, -0.15138055, 0.16771653, -0.07168161, 0.102572344, 0.08266354, 0.20205829, 0.13429944) * go_0(-1.0, -1.0);
result += mat4(0.05584234, 0.06844309, 0.025430907, 0.124140054, 0.36385667, 0.12099467, -0.41671994, 0.085477844, 0.19748127, -0.21473993, 0.005037813, -0.3973761, 0.04669592, -0.100342326, -0.09403772, -0.034248166) * go_0(-1.0, 0.0);
result += mat4(-0.17654696, 0.009085064, 0.028360577, 0.033909567, 0.09377573, 0.27896938, 0.103994116, 0.0008595595, 0.064523555, 0.040994007, -0.06337235, 0.05662917, 0.0037455747, 0.017608117, -0.14610702, 1.2175746e-05) * go_0(-1.0, 1.0);
result += mat4(-0.04631749, -0.14251712, -0.16420849, -0.16259338, 0.46187812, 0.17576592, 0.00049142196, 0.029193122, -0.003925961, -0.11218227, 0.007026237, -0.20583045, -0.0010964901, 0.19355829, 0.2221649, 0.1187224) * go_0(0.0, -1.0);
result += mat4(-0.041567978, -0.31510913, 0.01618704, 0.04979329, 0.101294376, 0.16356954, 0.21361789, 0.20735294, 0.1900854, -0.4151726, -0.30471593, -0.59483325, 0.033624128, 0.11495109, -0.15194787, 0.4920959) * go_0(0.0, 0.0);
result += mat4(-0.18910064, -0.06516878, -0.20508374, -0.063928686, 0.7289614, 0.26674315, 0.2929481, 0.4026098, -0.033123735, -0.090371035, -0.029094126, -0.15197921, -0.08723726, -0.060160585, -0.07908409, -0.08826931) * go_0(0.0, 1.0);
result += mat4(-0.08321312, -0.09749648, -0.08783197, -0.23072585, 0.24343425, 0.10888949, 0.17419606, 0.04136083, 0.0066000987, -0.06112787, -0.12176007, -0.20907228, -0.0008522778, -0.054704696, -0.07197735, -0.0877179) * go_0(1.0, -1.0);
result += mat4(-0.40559706, -0.3801705, 0.05970925, -0.6157092, 0.28944594, 0.1252121, 0.403247, -0.122819394, -0.096336536, -0.2324694, 0.05980106, -0.19970767, -0.16646989, -0.10164633, -0.09282806, -0.08897996) * go_0(1.0, 0.0);
result += mat4(-0.14336498, -0.12967408, -0.016268672, -0.021431219, -0.0850116, 0.37105832, -0.04093888, 0.08540873, 0.035717323, -0.07282701, -0.009123291, -0.0036565473, -0.02508944, -0.087611906, 0.03604423, -0.00089080486) * go_0(1.0, 1.0);
result += mat4(0.1373875, 0.05283984, -0.11992707, 0.102294855, 0.3305128, 0.044920854, 0.31622922, -0.04711731, 0.001336024, 0.022799017, -0.062343203, 0.017140022, -0.07556853, -0.12864219, -0.25721326, -0.20741239) * go_1(-1.0, -1.0);
result += mat4(0.22062224, 0.09266222, 0.22466063, 0.18527372, -0.06940306, 0.1317168, 0.019784274, -0.07422301, 0.04061616, 0.0022494853, 0.21723995, 0.24732308, 0.14088804, 0.0116154915, 0.102064446, 0.020701224) * go_1(-1.0, 0.0);
result += mat4(-0.025154127, 0.045180723, -0.05877639, -0.099235624, 0.13630918, 0.24653725, -0.05723323, -0.022995364, -0.10826078, 0.049667366, 0.12618053, 0.1557369, 0.037487056, -0.22215757, 0.005912914, -0.20549043) * go_1(-1.0, 1.0);
result += mat4(0.09641055, 0.098845296, -0.08192096, -0.03691394, -0.18450394, 0.29955688, -0.082493715, -0.06268039, -0.0754319, 0.21018648, -0.016580105, -0.1810546, 0.13857666, -0.0327626, 0.03161804, -0.32589525) * go_1(0.0, -1.0);
result += mat4(-0.18272439, -0.17595461, 0.047229152, 0.14596708, 0.40453747, 0.5658558, -0.17969102, 0.21557859, -0.34232348, 0.40355968, 0.53874254, 0.0012561383, 0.28154096, -0.06745097, -0.13049632, 0.42997465) * go_1(0.0, 0.0);
result += mat4(0.081179485, -0.0041369614, -0.12001932, -0.102107175, -0.050293338, 0.29165673, 0.08062538, 0.22925815, 0.19389379, 0.28463286, -0.057207666, 0.23133168, -0.07545728, 0.06729763, -0.103593476, 0.014468794) * go_1(0.0, 1.0);
result += mat4(0.069821335, -0.010299579, 0.069458775, 0.03894593, -0.054688405, 0.32758355, 0.13935772, 0.37506017, 0.24083133, -0.06105339, 0.25636867, 0.09627044, 0.08939188, 0.006728639, 0.10629504, 0.07887502) * go_1(1.0, -1.0);
result += mat4(0.10563019, 0.077379815, 0.045456886, 0.09303406, 0.11326298, 0.28762257, -0.35142374, 0.10285745, 0.28762287, 0.3592446, 0.23816557, 0.22676824, 0.030372012, -0.028023086, -0.30956736, -0.27588373) * go_1(1.0, 0.0);
result += mat4(0.110499, 0.009828844, 0.086689755, 0.1839749, 0.16656482, 0.083707325, 0.19506347, -0.01547141, 0.13804145, 0.2206598, -0.16484791, -0.0021595939, -0.06844408, -0.07861768, 0.040771082, -0.13347322) * go_1(1.0, 1.0);
result += mat4(0.02667995, 0.019265587, -0.18211095, -0.102116466, -0.042541366, -0.07700912, -0.020587347, -0.03532171, 0.14816427, -0.1672272, -0.17522137, -0.04657808, 0.013430233, -0.0021270285, 0.109880306, 0.004838907) * go_2(-1.0, -1.0);
result += mat4(0.14285165, -0.1364756, 0.017568532, -0.27690783, -0.015461915, 0.045437083, 0.018187419, 0.12473493, 0.17991658, -0.15642665, 0.10009151, -0.19040193, 0.1734127, -0.13817501, 0.0710856, -0.12921426) * go_2(-1.0, 0.0);
result += mat4(-0.14114712, -0.18893671, 0.16121174, 0.035988737, 0.17872387, -0.106395856, -0.23183517, 0.012380416, 0.043066982, -0.28539032, -0.049011275, -0.21125022, -0.11976977, -0.015564958, 0.18880925, -0.0034812456) * go_2(-1.0, 1.0);
result += mat4(-0.05894521, 0.17266215, -0.0458901, 0.08049924, 0.0156061025, -0.0047465423, 0.09714626, 0.045990974, -0.08786066, -0.37803304, -0.19629405, -0.08546443, 0.014874948, 0.16931784, 0.24799919, 0.06316819) * go_2(0.0, -1.0);
result += mat4(-0.28352743, 0.29973608, -0.014540065, 0.2865005, 0.048086923, 0.18976144, 0.22969759, 0.1643124, -0.11259408, -0.107592925, 0.184308, 0.30998367, -1.0860825, -0.29118305, -0.51242536, -0.38492215) * go_2(0.0, 0.0);
result += mat4(-0.17199941, -0.14274743, -0.14213641, -0.1691383, -0.17294803, -0.013992068, -0.12135059, 0.082377024, -0.11255549, -0.124990575, -0.32526177, -0.08199375, -0.25591666, 0.1882329, 0.07895415, 0.22012262) * go_2(0.0, 1.0);
result += mat4(0.026025832, -0.07267515, 0.09738688, 0.074536435, -0.060470507, -0.037861936, 0.0507819, -0.054857653, 0.0043173633, -0.18107842, -0.02996759, 0.04072402, -0.012617744, 0.061665237, 0.0013981885, 0.08679919) * go_2(1.0, -1.0);
result += mat4(0.27913737, 0.39656082, 0.1579819, 0.2774727, -0.007996453, 0.08704765, -0.016933938, 0.07066135, 0.12361742, -0.20802726, -0.13705719, -0.18794124, 0.037409827, -0.03351758, -0.2970392, -0.11001984) * go_2(1.0, 0.0);
result += mat4(-0.027419567, 0.043236237, -0.19843115, -0.056489736, -0.017010912, 0.070949584, -0.14881176, -0.0780235, 0.0039477753, -0.16772608, -0.009547604, -0.14060417, 0.0103197545, 0.07129672, 0.034949142, 0.014112084) * go_2(1.0, 1.0);
result += mat4(-0.06467971, 0.084101565, 0.26296136, 0.08878442, -0.11232121, -0.054373942, -0.17263442, 0.046408508, 0.032239515, 0.042490713, 0.036938053, -0.034339923, -0.07139367, 0.032505415, 0.0045828503, 0.24428385) * go_3(-1.0, -1.0);
result += mat4(0.053585388, -0.08175568, -0.04787236, 0.06061965, -0.0740297, 0.11113596, -0.12467945, 0.08229154, -0.01941305, 0.12903687, 0.09095716, -0.13062255, -0.0102068605, 0.107291475, 0.030279635, 0.07464777) * go_3(-1.0, 0.0);
result += mat4(0.11041978, -0.0123585425, 0.11147018, 0.07380536, 0.06632908, 0.011784447, 0.029638765, -0.01566135, 0.009105331, 0.05252663, -0.17972581, 0.01210126, -0.10749957, -0.028144639, -0.105761215, 0.083784826) * go_3(-1.0, 1.0);
result += mat4(-0.058018316, 0.15083058, 0.2725673, 0.024263225, -0.067711554, 0.051117413, -0.31144425, -0.15761986, 0.017503206, -0.14361219, -0.38261738, -0.20354146, -0.04211545, 0.12921454, -0.01319619, 0.35809723) * go_3(0.0, -1.0);
result += mat4(-0.107978396, 0.3230084, -0.13806303, 0.12903036, 0.039864987, -0.006241628, 0.18701774, -0.10785807, 0.30056882, -0.3092082, -0.4273322, 0.3784662, -0.026107281, 0.23165871, 0.35258314, -0.06654702) * go_3(0.0, 0.0);
result += mat4(-0.15840323, 0.15210885, 0.04086692, 0.19169305, 0.11847602, 0.0009038581, 0.095951624, 0.043941673, 0.1512248, 0.0749449, -0.027045414, -0.19729601, 0.08265063, -0.045218006, -0.10732461, 0.05197371) * go_3(0.0, 1.0);
result += mat4(0.13637526, 0.28841978, 0.10298119, -0.005948496, 0.020897362, -0.02186902, -0.16207378, -0.021084815, 0.029192554, 0.07076991, -0.07210881, -0.06752328, 0.0006557475, 0.08986717, -0.29430988, 0.21411087) * go_3(1.0, -1.0);
result += mat4(0.18667863, 0.3117322, -0.0859705, -0.038189936, 0.10214859, -0.11244034, 0.2680223, -0.072901204, -0.07434324, -0.17855306, 0.23134363, -0.055360887, -0.020968167, 0.0858459, 0.078975916, 0.13254759) * go_3(1.0, 0.0);
result += mat4(-0.15676941, 0.03476677, -0.09922334, -0.15847856, -0.0033982224, 0.020932984, 0.12874377, 0.048792202, 0.06521213, 0.12456798, 0.15958112, 0.15981804, 0.07657683, 0.1759313, 0.012727211, 0.120304115) * go_3(1.0, 1.0);
result += vec4(0.08911729, -0.027969634, -0.010653148, -0.08001697);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_3_tf
//!BIND conv2d_3_tf1
//!SAVE conv2d_4_tf1
//!WIDTH conv2d_3_tf.w
//!HEIGHT conv2d_3_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_3_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_3_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_3_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.003206617, 0.04896987, 0.049652386, 0.10869342, 0.36313584, -0.070666805, 0.93581825, -0.52484274, -0.14278883, 0.064016834, -0.05534331, 0.02961736, -0.1319316, 0.05740655, 0.2405951, -0.12313382) * go_0(-1.0, -1.0);
result += mat4(0.014092832, 0.07058761, -0.07887866, -0.27478936, -0.31456405, -0.31036922, -0.18380909, -0.11277979, -0.034889866, -0.37914017, -0.056245584, 0.24008954, -0.03414483, -0.023189066, -0.010568316, -0.004604883) * go_0(-1.0, 0.0);
result += mat4(0.15443979, -0.050161768, -0.012300917, -0.08834887, 0.082193285, 0.06878423, 0.1478042, -0.3774468, -0.18659878, 0.14238152, 0.033605397, 0.13560006, -0.032682173, -0.024561955, 0.05656941, -0.034246165) * go_0(-1.0, 1.0);
result += mat4(0.04691462, 0.064624496, -0.15950382, 0.16081297, -0.1417951, -0.109690994, -0.021205869, 0.19361454, -0.006306647, 0.3401972, -0.00014070333, 0.11619607, -0.13437814, 0.05464789, 0.37712076, -0.12470751) * go_0(0.0, -1.0);
result += mat4(-0.40016884, 0.010666597, -0.005395378, 0.51084363, -0.009875391, 0.3969395, 0.47768033, -0.3392299, -0.1509509, -0.057620626, -0.1834601, -0.09998148, 0.10095897, -0.2213528, 0.02546703, -0.28506726) * go_0(0.0, 0.0);
result += mat4(0.26652217, -0.106772706, -0.12609608, -0.0949661, -0.10869194, -0.55331933, -0.011515521, -0.27978876, -0.2124893, 0.03954004, 0.1691768, 0.05590268, 0.1539662, 0.10703386, -0.027286088, 0.2168544) * go_0(0.0, 1.0);
result += mat4(-0.04862511, 0.06919758, -0.12962708, 0.016036907, -0.030030789, -0.20159967, 0.0013158675, -0.07799172, -0.032236706, -0.0035921712, -0.085437834, -0.025374755, -0.06251374, -0.009269627, -0.07519051, -0.01884611) * go_0(1.0, -1.0);
result += mat4(0.23940067, -0.19496065, -0.05494683, 0.11601073, -0.074225076, 0.24976431, 0.41665986, 0.12029472, 0.16815041, -0.115868434, 0.06333614, 0.032145746, 0.15990137, -0.14886795, 0.034102913, -0.07727595) * go_0(1.0, 0.0);
result += mat4(0.14702639, -0.013711502, 0.011437429, -0.11201445, -0.2582659, 0.34539905, 0.058082145, -0.18346462, 0.0027891365, 0.072565466, 0.12716974, 0.050636146, 0.092657596, 0.08541754, -0.1266164, 0.027881607) * go_0(1.0, 1.0);
result += mat4(0.043362036, 0.020758621, 0.09906072, -0.22401148, -0.19104514, -0.25774476, 0.074128486, 0.08558291, -0.075419895, 0.20380639, 0.06398196, 0.015925938, 0.089786015, -0.100721814, -0.1374862, 0.26110905) * go_1(-1.0, -1.0);
result += mat4(-0.12547149, 0.08151811, -0.15953775, -0.33995447, -0.50784314, 0.46155545, 0.24986996, 0.03404644, -0.047789436, -0.12438347, -0.14143273, -0.17951359, -0.08057819, 0.023863006, -0.008539273, -0.06775414) * go_1(-1.0, 0.0);
result += mat4(0.1430169, 0.056971863, -0.021576611, -0.045342956, -0.22356391, -0.15344621, -0.0467977, -0.22970036, -0.0125351725, 0.16957329, -0.0069183917, -0.013949834, -0.048609708, 0.05261722, 0.023262242, 0.2123519) * go_1(-1.0, 1.0);
result += mat4(-0.019523792, 0.008228363, -0.04616012, -0.14341992, -0.19307113, 0.005937241, 0.24048887, -0.04279845, 0.022574252, 0.15558265, -0.035000063, 0.18318397, -0.05392528, -0.26044658, -0.13493988, 0.056433514) * go_1(0.0, -1.0);
result += mat4(-0.28926027, -0.17381874, 0.07685766, -0.0061521684, -0.47455552, -0.49213487, 0.36924496, 0.29042044, 0.201094, -0.14280887, -0.4531411, -0.52902204, -0.28123, 0.1401882, 0.32054895, -0.11357518) * go_1(0.0, 0.0);
result += mat4(0.14173324, -0.12069898, -0.07242415, 0.105665006, 0.017373435, -0.056042343, 0.07270201, 0.022111928, -0.01106541, 0.01666006, 0.013564169, -0.36628693, -0.25450787, -0.28179473, -0.04721874, -0.21912882) * go_1(0.0, 1.0);
result += mat4(-0.09464695, -0.027919646, 0.13088459, 0.17504032, -0.101641014, 0.29687008, 0.08832321, 0.020538324, -0.15108941, -0.21930224, -0.026915176, -0.07078217, 0.10723033, 0.034364715, 0.18183397, -0.119012214) * go_1(1.0, -1.0);
result += mat4(-0.21713468, -0.0846604, 0.046551514, -0.14989382, 0.08672032, -0.07933831, 0.08093595, -0.064147756, -0.15980323, 0.50000644, -0.091568656, 0.03201994, -0.1848647, -0.0646309, 0.03288009, 0.046442386) * go_1(1.0, 0.0);
result += mat4(0.053532355, -0.054523747, -0.040242642, -0.31438905, 0.06452703, -0.18785381, -0.14987698, -0.067642935, -0.19892459, -0.057256676, 0.05943023, -0.17331842, 0.02588534, 0.13134238, -0.07121775, 0.23446162) * go_1(1.0, 1.0);
result += mat4(0.20633182, 0.01686198, 0.17934167, -0.02063493, 0.042606052, -0.05289458, 0.031508356, 0.00082803797, 0.0756423, -0.047548845, 0.01456339, 0.15910533, -0.20119642, 0.029213727, 0.111036316, -0.047010012) * go_2(-1.0, -1.0);
result += mat4(0.09258436, -0.27904224, -0.086695746, 0.33095327, -0.20126075, -0.050745636, -0.048944805, -0.10536587, -0.012995092, 0.07926994, 0.15071853, -0.13644052, -0.05188447, -0.06750699, -0.14227037, 0.028751127) * go_2(-1.0, 0.0);
result += mat4(-0.18562223, 0.10250865, -0.17573993, 0.20434102, -0.05187468, -0.06441594, -0.052127104, -0.01925564, 0.02927959, -0.12711872, 0.059629507, 0.15696885, -0.010168965, 0.09971862, -0.03177664, -0.022744441) * go_2(-1.0, 1.0);
result += mat4(0.21474063, -0.15679085, 0.09609374, 0.109079376, -0.049934637, -0.07393633, 0.16688468, -0.018888129, 0.04240162, -0.31895876, -0.106516436, 0.20008606, -0.054410245, 0.028970616, -0.18008347, -0.013362003) * go_2(0.0, -1.0);
result += mat4(0.37891293, 0.042730846, -0.24735828, -0.5234527, -0.3681344, -0.06609157, -0.14993733, -0.020316398, 0.123008475, 0.29632482, 0.32149333, 0.35999274, -0.18967044, 0.46154186, -0.016041815, 0.097378336) * go_2(0.0, 0.0);
result += mat4(-0.14873263, 0.07600569, -0.051758345, 0.1803135, -0.23121934, 0.13574593, 0.043973465, -0.13992754, -0.061972607, -0.124083005, -0.049196843, -0.07700431, 0.21572952, -0.25241727, 0.1218322, -0.07773728) * go_2(0.0, 1.0);
result += mat4(0.040287063, 0.024240922, 0.021917762, -0.050616946, -0.023174169, 0.05977014, 0.018892275, 0.04014965, 0.11715485, 0.062129, 0.024620812, 0.013617107, 0.075699426, 0.1858111, -0.11769179, -0.08085602) * go_2(1.0, -1.0);
result += mat4(-0.3194255, 0.08695645, -0.09453595, 0.2564516, 0.02192303, 0.08167247, -0.06257352, 0.043801844, 0.04392246, 0.2020571, 0.045180902, 0.18857521, 0.1835961, -0.043788187, -0.08768916, -0.14755538) * go_2(1.0, 0.0);
result += mat4(-0.22074097, 0.13768476, -0.16183749, 0.059949517, -0.011375954, 0.08581876, 0.004800447, 0.019403988, 0.014646056, 0.07363176, -0.058036458, 0.0706421, 0.08082624, 0.17740329, -0.05484784, 0.050796065) * go_2(1.0, 1.0);
result += mat4(-0.032330472, -0.067666024, 0.18980837, -0.19077848, 0.1111905, 0.03855666, -0.11272314, -0.00577739, 0.17697452, -0.053044144, -0.07510145, 0.061853852, -0.024240626, 0.14846492, 0.14804313, -0.20275854) * go_3(-1.0, -1.0);
result += mat4(0.17133904, -0.16356844, 0.1978664, 0.13877816, 0.28208038, 0.031539194, 0.11313891, -0.0014802719, 0.0033749861, 0.046372313, 0.054808807, -0.0024151779, 0.0068782056, -0.16414621, -0.07545907, -0.2521294) * go_3(-1.0, 0.0);
result += mat4(-0.1746992, -0.037628956, -0.0044012754, -0.004390821, 0.0050341445, -0.112742625, 0.051241755, 0.01984483, 0.0003531837, 0.043500375, 0.030881992, 0.003503799, 0.13611782, -0.02509031, -0.007503557, -0.009321301) * go_3(-1.0, 1.0);
result += mat4(0.087250136, 0.12374122, 0.2959519, 0.11314702, 0.22080182, 0.106726184, -0.29768205, 0.14931595, 0.23356548, -0.008709153, -0.0797829, 0.046940215, -0.07027616, 0.20533602, 0.0723021, -0.1963585) * go_3(0.0, -1.0);
result += mat4(0.00609982, 0.35277408, -0.22781096, -0.28912535, 0.42393112, -0.07654207, 0.12636793, 0.049337976, -0.0967726, -0.19349189, 0.36800626, 0.09745645, 0.47663373, 0.03876107, -0.042987954, 0.016161885) * go_3(0.0, 0.0);
result += mat4(-0.047490966, -0.05823166, 0.036158644, 0.025337253, -0.046618905, 0.108276576, -0.024148034, 0.0026794411, 0.1497962, -0.09328474, -0.03160641, 0.24351281, -0.05198027, 0.030720685, 0.00014528916, -0.2224931) * go_3(0.0, 1.0);
result += mat4(-0.007338369, 0.18710312, 0.14617369, -0.0070655346, 0.10464997, -0.029674934, -0.11842202, -0.09114357, 0.08524458, -0.08082762, 0.06479597, -0.023760766, 0.07523641, 0.0067315935, 0.101266846, -0.2780903) * go_3(1.0, -1.0);
result += mat4(0.14181875, -0.19523518, 0.1068169, -0.10284853, 0.11634046, -0.117397435, 0.09113022, 0.009371062, -0.022120507, -0.1127032, 0.092574745, -0.021989716, -0.088107705, -0.13541982, 0.08130504, -0.0678927) * go_3(1.0, 0.0);
result += mat4(0.09948295, 0.23699793, -0.042369924, 0.16744529, -0.10045506, -0.045623623, 0.04871897, -0.0023967526, 0.02602692, -0.089873284, -0.050681606, -0.09332558, -0.09596149, -0.06988313, 0.0007193808, -0.11936899) * go_3(1.0, 1.0);
result += vec4(-0.04928105, -0.003357327, -0.03886671, 0.076106146);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_4_tf
//!BIND conv2d_4_tf1
//!SAVE conv2d_5_tf
//!WIDTH conv2d_4_tf.w
//!HEIGHT conv2d_4_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_4_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_4_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.13425097, -0.23487093, 0.2480183, -0.2806276, -0.041303713, 0.100773126, -0.110890545, 0.036205858, -0.331331, -0.12929262, 0.16300063, 0.3776673, -0.20316373, -0.011239426, 0.10650887, -0.027857736) * go_0(-1.0, -1.0);
result += mat4(0.09517376, -0.3004956, 0.05033304, -0.07464521, 0.009204248, -0.23034886, 0.30492118, -0.1215848, 0.15728685, -0.10430078, 0.04038878, 0.08034804, 0.04320418, -0.2929594, -0.018968396, 0.02542387) * go_0(-1.0, 0.0);
result += mat4(-0.10651935, -0.2736715, 0.19267319, -0.033337504, -0.06697293, 0.028424729, 0.047814637, 0.44929537, 0.02565344, -0.253426, -0.040931404, -0.05018104, 0.032979824, -0.035349697, -0.039578713, -0.3116414) * go_0(-1.0, 1.0);
result += mat4(0.09176126, 0.031713437, 0.24861507, 0.31351718, 0.36284143, 0.3622709, 0.16165464, 0.07319267, -0.6303202, -0.21209712, -0.02169929, 0.037275597, -0.1295319, 0.033090707, -0.029330662, 0.054679472) * go_0(0.0, -1.0);
result += mat4(0.15021572, -0.15177831, 0.1318225, 0.46864823, 0.059443284, 0.07404233, 0.22612074, 0.21105285, 0.319694, 0.09397257, 0.14277866, -0.0235649, -0.037205156, -0.40715128, -0.18572816, 0.058741573) * go_0(0.0, 0.0);
result += mat4(-0.122751735, -0.20926422, 0.2099333, -0.11627138, 0.04171681, 0.0669586, -0.03831368, 0.27334675, 0.0492008, 0.12854317, 0.03308622, 0.45236585, 0.03122829, 0.13853219, 0.05084764, -0.3965012) * go_0(0.0, 1.0);
result += mat4(-0.0019293908, -0.15562099, 0.12418126, 0.0045440597, 0.05442391, -0.15613738, 0.14828286, -0.17687118, -0.053517755, -0.33350968, -0.062314924, -0.31358472, -0.09670371, 0.043190923, 0.008150662, 0.09928506) * go_0(1.0, -1.0);
result += mat4(-0.06698031, -0.099411525, 0.24259582, -0.1073659, 0.06762824, 0.059605874, -0.20944163, -0.1598055, 0.32746908, -0.17759447, 0.2859796, -0.1274256, 0.30796206, -0.00791448, 0.114059694, 0.14775705) * go_0(1.0, 0.0);
result += mat4(0.16291203, -0.14958477, 0.14716864, 0.2056065, -0.019337546, 0.032286238, 0.0030445335, -0.08208513, -0.14208078, 0.13601872, -0.23367858, -0.19092909, -0.20207883, -0.016950991, 0.009309007, 0.1376546) * go_0(1.0, 1.0);
result += mat4(-0.11093091, -0.32362202, -0.041845415, 0.029758021, -0.07261404, -0.048653398, 0.19167832, 0.09343212, 0.030472826, -0.15078579, -0.0056376588, 0.0045257527, -0.24521805, -0.10473077, 0.11163019, -0.1724187) * go_1(-1.0, -1.0);
result += mat4(-0.08601668, 0.16612485, -0.07751539, 0.07261594, -0.19028407, 0.23896623, -0.10416726, 0.23500614, 0.1955228, 0.08699591, -0.049277775, 0.13447775, 0.19434914, -0.11481196, 0.088043146, 0.13352895) * go_1(-1.0, 0.0);
result += mat4(-0.013221233, 0.07521129, 0.042819552, -0.11163175, 0.066080205, -0.25043094, -0.010348969, -0.013258202, 0.09444396, 0.29623637, 0.025016114, 0.050744686, -0.12219596, -0.0735393, -0.024817836, -0.06897588) * go_1(-1.0, 1.0);
result += mat4(-0.25720942, 0.19861753, -0.18535058, 0.12190362, -0.33756095, -0.0038898317, 0.09739055, 0.41227046, -0.10030796, 0.025445882, -0.23542109, 0.08677691, 0.08140194, -0.22716106, 0.14016968, -0.0927231) * go_1(0.0, -1.0);
result += mat4(0.58745646, -0.12533307, 0.30129984, 0.08898194, -0.07972344, -0.37581098, 0.06863413, -0.13185541, 0.21801205, 0.31779078, -0.3804784, -0.3200699, 0.14534226, 0.05912262, 0.07938948, -0.34869507) * go_1(0.0, 0.0);
result += mat4(0.024675166, -0.067802526, 0.030065436, 0.06509131, 0.14367498, 0.022554757, 0.014991865, -0.029914752, 0.5123549, -0.012557206, -0.13014166, -0.34184244, -0.09080884, 0.13782553, -0.018931886, -0.35642785) * go_1(0.0, 1.0);
result += mat4(-0.37336427, -0.02705006, 0.14392053, 0.024049882, -0.024705589, 0.14556128, -0.12120506, -0.06275598, -0.1284325, 0.11409197, -0.08397436, -0.075944416, 0.056465942, 0.04016099, 0.096723564, -0.08359723) * go_1(1.0, -1.0);
result += mat4(0.20243345, -0.09287934, -0.11676041, 0.005206654, -0.2879361, 0.41677123, -0.16924824, 0.22429213, 0.082279116, -0.1780833, 0.20209241, 0.12970525, -0.030272234, -0.19200714, 0.0015769673, -0.1389732) * go_1(1.0, 0.0);
result += mat4(0.04211243, 0.07331798, -0.055724114, 0.04086206, -0.04635456, 0.027212424, 0.021861525, 0.12424812, 0.43009162, 0.021664696, 0.20828371, 0.11859106, 0.07390811, -0.1861182, 0.034559406, 0.18561925) * go_1(1.0, 1.0);
result += mat4(0.22596797, 0.025346763, -0.056839246, 0.09137385, 0.07363095, -0.12382036, 0.08911783, -0.012355983, -0.07869761, 0.051298574, 0.00816572, -0.044984274, 0.07962154, -0.2254524, -0.007821531, -0.04936664) * go_2(-1.0, -1.0);
result += mat4(0.06265961, -0.17783198, 0.11678783, -0.12965304, 0.014506855, -0.17513473, -0.23593299, 0.14054537, 0.1580306, 0.31872272, -0.0042505316, -0.070422255, -0.01316396, 0.0058355615, 0.062464185, -0.06086727) * go_2(-1.0, 0.0);
result += mat4(-0.079526044, 0.23932967, -0.1139716, 0.15888569, 0.06526993, -0.06958436, -0.04070066, -0.12081254, 0.026716579, 0.014887845, 0.0061467467, 0.127956, 0.040913627, -0.0032820841, 0.086145625, 0.22520025) * go_2(-1.0, 1.0);
result += mat4(0.25577608, 0.02553098, -0.14822578, -0.11907723, -0.09787419, -0.03544863, -0.08098151, -0.01305555, 0.20404844, 0.11294246, 0.10096346, 0.15795162, 0.2554626, 0.09361069, 0.001985862, -0.0051444587) * go_2(0.0, -1.0);
result += mat4(-0.24454486, -0.014714279, -0.2954907, -0.39995646, -0.15907967, 0.30107877, -0.34781745, 0.095281735, -0.12492393, -0.28375402, -0.16872306, 0.2531788, -0.52085644, 0.35986066, 0.07716912, 0.09565738) * go_2(0.0, 0.0);
result += mat4(0.2493129, 0.06395661, -0.09491958, 0.19702488, 0.109871864, -0.051376317, 0.15404263, -0.21282886, 0.1188967, 0.07824094, -0.016752928, -0.14027214, 0.10949832, -0.27629098, 0.081909016, 0.1354018) * go_2(0.0, 1.0);
result += mat4(0.18950915, -0.034574565, -0.10378051, -0.15800652, -0.06835184, -0.06987467, 0.035007782, 0.04686656, 0.054061133, 0.014833506, -0.0035361175, 0.016156103, 0.120767444, -0.10196722, 0.10668838, -0.09058739) * go_2(1.0, -1.0);
result += mat4(-0.032248627, 0.056413256, 0.042716432, 0.06681831, 0.047605485, -0.07629479, 0.14311917, -0.06909803, 0.10640394, 0.10701861, -0.0051839007, -0.15133362, -0.32146424, -0.039978918, -0.12280021, 0.0048507582) * go_2(1.0, 0.0);
result += mat4(-0.1954503, -0.09257865, 0.11023244, -0.01817947, -0.0035485283, -0.015536726, 0.0071826433, 0.042538714, -0.015454641, 0.079593316, -0.07242554, 0.031178504, 0.2319168, -0.10519467, 0.013837495, -0.040088437) * go_2(1.0, 1.0);
result += mat4(0.12625901, 0.04531166, 0.038758352, -0.05790713, -0.10029771, -0.118265375, -0.23944628, 0.11955388, 0.070732996, 0.19404806, -0.019913414, 0.04609079, 0.06262817, 0.022330387, -0.029681094, 0.03719176) * go_3(-1.0, -1.0);
result += mat4(-0.07737922, 0.0024623116, -0.037666153, -0.19271135, -0.015002153, -0.0059966356, 0.0024538909, -0.0401021, -0.18540399, -0.11140236, -0.11102473, -0.06390247, 0.016754225, 0.35000673, -0.19139731, 0.07363001) * go_3(-1.0, 0.0);
result += mat4(0.02150171, -0.2311761, -0.025124706, 0.16819553, -0.0013348719, 0.32091036, -0.061826598, 0.12579474, -0.036611024, -0.018266583, -0.11280143, 0.11073158, 0.050171874, -0.14706045, 0.029553955, 0.0052631944) * go_3(-1.0, 1.0);
result += mat4(0.19249865, -0.22854832, 0.09472751, 0.014705341, 0.059496958, 0.13427268, -0.06309558, -0.07153743, -0.31890163, -0.0657967, -0.040345218, 0.09544393, 0.07359761, 0.11245483, 0.00033233972, 0.031550154) * go_3(0.0, -1.0);
result += mat4(-0.24668917, -0.37181908, -0.50614715, -0.101197146, -0.1569055, 0.27734125, 0.17144768, -0.04336267, 0.03658949, 0.06747124, 0.30720958, 0.56301194, -0.11314631, -0.29258573, 0.16256689, 0.5221001) * go_3(0.0, 0.0);
result += mat4(-0.022761503, 0.13063031, 0.002526217, -0.03466151, -0.15225072, 0.40217137, -0.089131154, 0.19195192, -0.1379853, -0.04640692, 0.104670234, 0.12268618, -0.012009209, -0.20534724, 0.028777445, 0.22195113) * go_3(0.0, 1.0);
result += mat4(0.23697586, 0.08793654, -0.10565018, 0.013993297, -0.025932996, -0.13859354, 0.14333159, -0.099132575, -0.049601994, -0.0917448, -0.0021633878, -0.009032609, -0.034750953, -0.30761167, 0.058994945, -0.19427797) * go_3(1.0, -1.0);
result += mat4(-0.26944515, 0.30523893, -0.17787015, 0.10827742, 0.06457236, -0.12202401, 0.15371302, 0.011699893, -0.06253491, -0.10976804, -0.37283847, -0.23996784, -0.2750512, -0.024101513, -0.094127975, -0.17462716) * go_3(1.0, 0.0);
result += mat4(-0.026286924, 0.06250577, 0.095423855, -0.02849258, -0.12916361, -0.10954709, -0.05825132, -0.102924265, -0.19550376, -0.11730307, 0.032346163, -0.17682706, 0.16651174, 0.031927045, -0.004800601, -0.06323844) * go_3(1.0, 1.0);
result += vec4(0.0095873345, 0.04959374, -0.15246227, 0.0044831373);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_4_tf
//!BIND conv2d_4_tf1
//!SAVE conv2d_5_tf1
//!WIDTH conv2d_4_tf.w
//!HEIGHT conv2d_4_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_4_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_4_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_4_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(-0.021453971, -0.108874515, 0.0005208881, -0.09774453, -0.0053757126, 0.20114918, 0.24454592, 0.04932251, -0.0037210248, -0.0240578, -0.07736935, 0.27604944, -0.12430849, -0.13093218, -0.014840212, 0.13450128) * go_0(-1.0, -1.0);
result += mat4(-0.19143668, -0.23023333, -0.10232715, 0.24396868, 0.056112397, 0.14535592, -0.25882182, -0.26274678, -0.23119931, 0.07735849, -0.14785223, -0.21026523, -0.2064457, -0.34512606, -0.17808662, 0.30146623) * go_0(-1.0, 0.0);
result += mat4(0.0072161015, -0.013303738, 0.07591899, 0.027883789, 0.210858, 0.1422139, -0.027882019, 0.2618474, -0.048504543, 0.07377317, -0.05427271, -0.10014041, -0.12974857, -0.13140713, -0.02249253, 0.08203184) * go_0(-1.0, 1.0);
result += mat4(0.07855138, -0.13984342, 0.10037151, -0.056781758, 0.24686107, -0.0048190085, -0.2693424, 0.31722167, -0.28716075, -0.06422215, -0.06738793, -0.06723655, -0.08194382, -0.007975044, 0.20108353, -0.13338897) * go_0(0.0, -1.0);
result += mat4(0.35129568, 0.27930936, 0.024239251, -0.10712293, 0.48684034, -0.04380574, -0.0064479653, 0.03754327, -0.13139078, -0.44939983, -1.0460628, -0.016004754, -0.14476573, -0.07113434, 0.515311, -0.400374) * go_0(0.0, 0.0);
result += mat4(0.13104302, -0.23410062, 0.091530964, -0.003652217, 0.16696814, 0.16406855, -0.08138474, 0.047526445, 0.25358474, 0.37850454, 0.0362802, -0.046476766, -0.093869686, -0.4143772, 0.08641024, 0.115896136) * go_0(0.0, 1.0);
result += mat4(-0.04416574, -0.052188106, 0.05141859, -0.008132604, -0.013658864, 0.1021097, 0.19391364, -0.09257973, 0.15225394, -0.16920799, -0.16172324, 0.41466942, -0.07087308, 0.08632938, -0.07496043, -0.023530172) * go_0(1.0, -1.0);
result += mat4(0.09337352, 0.062108494, -0.219173, -0.046151914, 0.22507025, -0.08966131, -0.123690315, 0.08666376, -0.10731867, -0.08518657, 0.024199447, 0.17898631, 0.120247275, 0.089923285, -0.08756211, 0.1775775) * go_0(1.0, 0.0);
result += mat4(0.20326594, -0.060535498, -0.061659336, 0.113954924, -0.073462196, 0.15917051, 0.11728326, -0.072256014, -0.0752342, 0.06265616, -0.19494365, -0.25413772, -0.06641352, -0.015642308, 0.16825356, 0.0027654327) * go_0(1.0, 1.0);
result += mat4(-0.17029639, -0.05388927, -0.13159063, 0.0795609, 0.00501164, -0.0703107, -0.08229201, 0.07546247, 0.092942156, 0.059050936, -0.07987315, 0.010874322, 0.037708692, -0.0017377702, -0.030414931, 0.28946167) * go_1(-1.0, -1.0);
result += mat4(-0.2692667, 0.2258295, 0.062060453, 0.1934921, -0.023051793, -0.038611185, 0.21473692, 0.33520013, 0.029885106, 0.103782356, 0.05217351, -0.13349791, -0.034186684, -0.3015818, 0.033423528, 0.21218027) * go_1(-1.0, 0.0);
result += mat4(-0.013587494, 0.021273775, -0.022650799, -0.011939531, -0.11202949, 0.09365859, -0.042938907, -0.009910716, 0.27254924, 0.07752608, 0.029586637, 0.024899973, 0.04375618, 0.31453863, -0.006775175, 0.008228053) * go_1(-1.0, 1.0);
result += mat4(-0.49562672, -0.12472124, -0.13618441, 0.09660054, -0.2275429, -0.0902811, 0.18311924, 0.11677185, -0.13325182, -0.061613016, -0.011462703, -0.12538978, 0.054934092, 0.06742866, 0.25515345, 0.35692096) * go_1(0.0, -1.0);
result += mat4(0.5266911, -0.09655596, -0.41069564, -0.3174325, 0.1431904, -0.17732115, -0.36320353, 0.37975433, -0.5158582, -0.21019879, 0.06852925, -0.06648648, -0.18956456, -0.018139647, 0.35707653, 0.07378416) * go_1(0.0, 0.0);
result += mat4(0.04151976, -0.037361674, 0.06936584, -0.10462262, -0.22264048, -0.043842267, -0.12742832, -0.21778631, 0.0715335, -0.17921853, -0.3856251, -0.16335362, 0.21045755, -0.5026229, 0.14405337, 0.23096423) * go_1(0.0, 1.0);
result += mat4(-0.32437655, 0.07860345, -0.0021187086, 0.123870686, -0.16616751, 0.11004699, 0.04754715, -0.0075211064, -0.08026408, 0.04284957, -0.018143758, 0.032623176, 0.06614686, -0.035856936, 0.13667971, -0.15696613) * go_1(1.0, -1.0);
result += mat4(0.11260625, 0.03274457, -0.033769324, -0.11558525, -0.35377702, 0.0019119612, 0.24906515, -0.06853208, 0.0009843144, -0.0050376705, 0.063123666, 0.009872904, 0.19592324, 0.0028321196, -0.114693984, 0.16404222) * go_1(1.0, 0.0);
result += mat4(-0.03699667, 0.011842293, -0.12273219, 0.04081692, 0.008484447, -0.052331816, 0.07151068, 0.018538639, 0.077749036, 0.07189092, 0.22443593, -0.2436085, 0.023654116, -0.05127411, 0.27350748, 0.12180999) * go_1(1.0, 1.0);
result += mat4(0.16090482, 0.059198547, 0.04856637, -0.19173436, 0.12747662, -0.079715036, -0.20203276, -0.13818277, -0.123076215, -0.07168488, 0.0644838, 0.03524764, 0.0005124138, -0.06789178, 0.048645556, -0.098922126) * go_2(-1.0, -1.0);
result += mat4(0.29220074, 0.25197285, 0.09825887, 0.030363245, -0.033246458, -0.08370418, -0.12231589, -0.023000835, 0.082732, -0.16907515, -0.052518822, 0.07991363, 0.06222654, -0.06747275, -0.18931144, -0.42009747) * go_2(-1.0, 0.0);
result += mat4(0.02667354, 0.03842717, -0.012755562, 0.061840586, 0.01060547, -0.29081437, 0.010907111, 0.07930905, 0.12273201, 0.017574295, 0.051024225, 0.019036688, 0.07671181, 0.049130872, -0.09734168, -0.070569195) * go_2(-1.0, 1.0);
result += mat4(0.08517651, 0.0767222, -0.15657257, 0.18501835, -0.13749431, -0.2833894, 0.109219365, 0.033763003, 0.18988928, 0.13461404, -0.036578514, -0.13256857, -0.097819485, -0.17316358, -0.06512401, 0.1937444) * go_2(0.0, -1.0);
result += mat4(-0.32173568, -0.072075866, 0.13004705, -0.15507852, -0.23741087, -0.29364398, 0.10723945, -0.11976219, 0.20620506, 0.17970093, 0.24463713, -0.12555319, -0.021192182, -0.1374317, 0.5359718, 0.59974134) * go_2(0.0, 0.0);
result += mat4(-0.01101575, 0.040466793, -0.009630791, 0.13422947, -0.13290837, -0.24789505, -0.061713737, -0.07838521, 0.05379315, -0.14643523, -0.09155805, -0.049997047, 0.06696885, 0.20043123, -0.07542329, -0.08041673) * go_2(0.0, 1.0);
result += mat4(0.022160506, 0.01611432, -0.10189221, -0.022767285, -0.06682965, 0.047138248, 0.06860934, -0.012574086, 0.04010214, -0.041280016, -0.034621384, -0.018262599, 0.09731754, -0.059062295, 0.14786182, -0.15185094) * go_2(1.0, -1.0);
result += mat4(-0.052484483, 0.06899427, 0.18380043, -0.058414727, 0.07685985, -0.07206598, -0.101362616, -0.012002652, 0.008517392, 0.079471916, -0.30394664, 0.028600946, -0.03270232, -0.23564856, 0.045065008, -0.0034684737) * go_2(1.0, 0.0);
result += mat4(-0.049757, 0.07614825, 0.16394803, 0.027053174, 0.0451278, -0.09351286, -0.0042182617, 0.12332257, -0.025281021, -0.03843008, 0.12857373, -0.07611989, -0.0062898803, 0.022618141, -0.13122174, -0.03328411) * go_2(1.0, 1.0);
result += mat4(0.12251631, 0.047008447, 0.027589995, -0.12207328, -0.1510795, 0.06724553, 0.17066906, 0.16992114, -0.0026905634, -0.035480864, 0.033738773, 0.018674552, 0.028614907, -0.019945908, -0.0156899, -0.09562145) * go_3(-1.0, -1.0);
result += mat4(0.116588935, 0.14205505, 0.099545434, -0.045527786, -0.049273346, 0.20760757, 0.053965498, -0.12198069, -0.14654607, 0.041820496, 0.038068503, 0.24565905, 0.09786504, 0.18309233, 0.23802327, -0.085740186) * go_3(-1.0, 0.0);
result += mat4(-0.1262052, -0.011846116, -0.058820397, -0.019373653, -0.09569547, -0.08265971, -0.05178388, -0.020502446, -0.17525336, -0.22874829, 0.0075891856, -0.189923, 0.09809122, 0.109637566, -0.0005973885, -0.06477921) * go_3(-1.0, 1.0);
result += mat4(0.28209856, 0.11276813, 0.054377034, -0.00891202, -0.095922634, 0.071109876, -0.039932176, -0.047409832, -0.06504704, 0.11923986, 0.0013364811, -0.122095086, -0.20282102, -0.022717483, -0.115474045, 0.020858249) * go_3(0.0, -1.0);
result += mat4(-0.16130303, 0.072821185, -0.021358958, -0.11687897, -0.15543966, 0.05783285, 0.10317231, -0.12240756, 0.053357504, -0.090291016, -0.21943556, 0.46947235, 0.19072579, 0.017349033, -0.55443907, -0.10510661) * go_3(0.0, 0.0);
result += mat4(-0.4155687, 0.019206723, -0.20055711, 0.028732464, -0.1981807, 0.20637372, 0.03305817, -0.17949893, -0.21051097, 0.21483344, 0.0061496794, -0.48980987, -0.26750582, 0.09230394, -0.117223755, -0.07636286) * go_3(0.0, 1.0);
result += mat4(0.20611528, -0.00095511036, -0.21555157, -0.07065484, 0.06880411, 0.068082534, -0.10104979, 0.16050354, -0.07437897, -0.13145325, -0.017651044, 0.055096775, -0.05443345, -0.018634815, -0.011232755, -0.10835) * go_3(1.0, -1.0);
result += mat4(-0.2637829, 0.07681072, 0.015995527, 0.004554211, 0.07495561, 0.18873464, -0.14303622, 0.25786543, -0.14117226, -0.008715274, -0.17176823, -0.0006595096, -0.06566383, -0.19184378, -0.18945406, 0.20968987) * go_3(1.0, 0.0);
result += mat4(-0.03293623, 0.003399063, 0.08051177, -0.0072856937, -0.07375858, 0.075319655, -0.10791501, -0.002204552, -0.093564905, -0.122712255, -0.10658267, -0.015067637, -0.033247817, 0.09952069, -0.13724248, 0.068189256) * go_3(1.0, 1.0);
result += vec4(-0.001935585, 0.05018077, -0.0154469935, -0.034524206);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_5_tf
//!BIND conv2d_5_tf1
//!SAVE conv2d_6_tf
//!WIDTH conv2d_5_tf.w
//!HEIGHT conv2d_5_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_5_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_5_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.0053346683, 0.010174534, -0.050979972, -0.06134544, -0.007238652, -0.012790015, 0.036398683, -0.09181499, 0.11328388, -0.14236617, -0.17519625, -0.34661606, 0.008069393, -0.028871074, -0.02980949, -0.0853359) * go_0(-1.0, -1.0);
result += mat4(-0.05187267, -0.09381704, 0.035209883, 0.29482442, -0.0018002815, -0.029504262, 0.2609028, -0.09480671, -0.0737553, -0.070559524, 0.081991084, 0.1513024, 0.048344653, -0.09336617, 0.0034569732, 0.10530542) * go_0(-1.0, 0.0);
result += mat4(-0.06749591, 0.0065624053, 0.013237342, 0.14225115, 0.27183163, -0.15656447, 0.031672053, 0.009592649, -0.0202286, 0.26220062, 0.19387855, -0.18505628, 0.040554795, 0.07295961, -0.13291295, -0.12600344) * go_0(-1.0, 1.0);
result += mat4(0.039192002, 0.0846215, -0.06593224, 0.28147796, 0.06301313, 0.26323164, -0.16742979, 0.22004774, -0.17470881, 0.060716614, 0.15430811, 0.18970133, 0.08858931, -0.027321626, -0.037833836, 0.07344837) * go_0(0.0, -1.0);
result += mat4(0.0633813, 0.35046157, -0.101075254, 0.015974075, 0.19010352, -0.7135035, -0.24324696, -0.42072615, 0.06825536, -0.052808974, 0.28965715, -0.0015640302, -0.27062586, 0.4279925, 0.035623744, 0.46321228) * go_0(0.0, 0.0);
result += mat4(0.02639867, 0.26469797, -0.09086266, 0.07440796, -0.192054, 0.1010368, -0.04398074, 0.056824226, -0.27057743, -0.20455118, 0.19338831, -0.21843775, 0.20736177, -0.26259273, -0.07667085, -0.19504389) * go_0(0.0, 1.0);
result += mat4(-0.007056104, 0.04284205, 0.01933733, 0.07267832, 0.0012616975, -0.30140647, -0.019223223, -0.046687007, -0.037844718, -0.014929125, 0.022630794, 0.046716493, 0.057279173, -0.08055539, -0.027891241, 0.019557232) * go_0(1.0, -1.0);
result += mat4(0.035518404, -0.10087327, 0.0011048123, -0.123707846, 0.37190285, 0.43751532, -0.022599256, -0.041709043, 0.11357196, -0.029839104, -0.056960747, -0.17228557, 0.08558022, 0.046361133, 0.021548864, 0.24297418) * go_0(1.0, 0.0);
result += mat4(-0.043598346, -0.09812348, 0.056599542, -0.09833163, -0.07193007, 0.015760094, -0.053177495, -0.015448543, 0.035163186, -0.03889347, 0.121799015, 0.15738566, -0.115644835, 0.043310717, 0.060173217, -0.059635755) * go_0(1.0, 1.0);
result += mat4(-0.111604795, 0.1678389, 0.049967546, 0.045353863, -0.013896185, 0.035128903, 0.040686198, -0.16442506, 0.1149577, -0.14343217, -0.08858, 0.02656137, 0.059526477, -0.13914491, 0.12757027, 0.034920372) * go_1(-1.0, -1.0);
result += mat4(0.15849945, 0.12067003, -0.1579611, 0.30790725, -0.041249942, 0.03948043, -0.12535375, -0.02566875, 0.3150059, 0.027081972, -0.026308673, -0.25326517, 0.016824603, -0.13551097, 0.1412756, 0.037750524) * go_1(-1.0, 0.0);
result += mat4(0.1562541, -0.041948073, -0.14951487, 0.119380556, -0.21773878, -0.019281754, 0.08185942, 0.09982689, 0.017187534, -0.18181366, -0.09270861, 0.08527679, 0.051988564, 0.08686172, -0.12665209, -0.07205808) * go_1(-1.0, 1.0);
result += mat4(0.08860466, -0.17931758, 0.10191625, -0.47623265, 0.1562338, -0.2960855, 0.013664795, 0.29452285, 0.1463958, 0.17562817, -0.41623253, -0.196999, -0.049113005, 0.0556021, 0.054452494, 0.14073615) * go_1(0.0, -1.0);
result += mat4(-0.5345973, -0.069205046, 0.37001884, 0.6955835, 0.22635284, -0.09021557, -0.04693607, -0.4458824, 0.25049326, -0.06503396, 0.07584689, 0.5394811, 0.33387923, -0.010540017, 0.038980547, -0.13371105) * go_1(0.0, 0.0);
result += mat4(-0.04414677, -0.22056313, 0.05580458, 0.11914465, 0.19864987, -0.1025625, -0.010050287, 0.15919746, -0.40589634, 0.4966349, -0.47632688, -0.022637444, 0.17247641, -0.51093113, 0.21157944, -0.2890017) * go_1(0.0, 1.0);
result += mat4(-0.034673482, -0.0075900992, -0.061077584, -0.03859898, 0.32444152, -0.14619137, -0.1375446, -0.030322462, 0.029679669, 0.079344586, -0.03862258, -0.05766807, 0.104488336, 0.006179548, -0.1168102, 0.069729604) * go_1(1.0, -1.0);
result += mat4(0.08504003, 0.042162962, -0.17509954, -0.06258286, -0.45796555, -0.061748773, 0.25438437, -0.02988987, -0.06897794, 0.105180845, -0.08879189, -0.120605074, -0.1478659, -0.13201937, -0.01755498, 0.020606143) * go_1(1.0, 0.0);
result += mat4(0.08932581, 0.1453785, -0.12802933, 0.049442187, 0.045360584, 0.16079827, -0.14142223, 0.10168491, 0.20244479, -0.17981426, 0.19759466, 0.05217847, 0.04889828, 0.06941533, -0.111836776, -0.08046399) * go_1(1.0, 1.0);
result += mat4(-0.011953735, 0.11362504, -0.122588776, -0.10408559, 0.051712614, -0.05161036, -0.068698496, -0.015663281, -0.06346889, 0.06561636, 0.03783044, 0.02756004, -0.036310352, -0.16962235, -0.062494226, 0.0069608325) * go_2(-1.0, -1.0);
result += mat4(-0.16857432, -0.17322211, 0.15971284, 0.19980437, -0.007965961, -0.015480705, 0.036090557, 0.07414387, -0.2941106, -0.24430539, 0.01070864, 0.22401866, -0.34321144, 0.09537491, -0.08020218, 0.45404655) * go_2(-1.0, 0.0);
result += mat4(-0.021609096, -0.11348408, -0.01450652, -0.063170746, 0.06990935, -0.035983834, -0.038010992, -0.10578655, 0.29232737, 0.048835874, 0.054028947, -0.12924139, -0.03058583, 0.028469706, 0.09563202, 0.085674495) * go_2(-1.0, 1.0);
result += mat4(-0.01894022, 0.037628658, -0.102314636, -0.28041583, 0.07495663, -0.058895253, 0.16422969, -0.07163792, 0.039416216, -0.13800906, -0.039811566, -0.10612402, -0.047593113, -0.28491783, 0.41632858, 0.15253194) * go_2(0.0, -1.0);
result += mat4(0.26240867, -0.05335849, -0.014135048, 0.055749495, -0.020126658, 0.2952794, -0.015241771, 0.36143306, 0.43075684, 0.1921996, -0.4329065, 0.5114495, 0.7326109, -0.054901246, -0.076693356, -0.26104695) * go_2(0.0, 0.0);
result += mat4(0.14548428, 0.14578429, 0.17193514, -0.07973242, 0.011952286, -0.047767498, 0.025101405, 0.0016503566, -0.26948047, -0.16503395, -0.061791085, 0.030557185, 0.15400517, -0.054951698, -0.14611247, 0.3550633) * go_2(0.0, 1.0);
result += mat4(-0.05926111, -0.083442695, 0.046579204, -0.017723244, 0.12846185, 0.018434443, -0.17914511, -0.077696435, 0.060048338, 0.02956987, -0.11914462, 0.057770032, -0.054673657, -0.005353606, -0.39014184, 0.08306877) * go_2(1.0, -1.0);
result += mat4(0.07357362, 0.23051825, -0.22640751, 0.080715515, -0.14467078, 0.009734264, 0.054320686, 0.24534328, -0.16038458, 0.06575425, 0.058553413, 0.17755087, 0.08184439, 0.17078212, 0.148369, -0.09309279) * go_2(1.0, 0.0);
result += mat4(-0.11160211, -0.07590204, -0.01676188, -0.062253337, 0.016433533, 0.0146132, -0.040350936, 0.06749202, -0.031521842, 0.1441664, -0.09916073, 0.050578352, -0.06560962, -0.31174552, 0.056873083, -0.077912) * go_2(1.0, 1.0);
result += mat4(0.09344025, 0.075936995, -0.1627903, -0.04781558, -0.01878236, 0.045879602, -0.11507387, -0.025356822, -0.09113391, 0.07263937, 0.08232447, 0.08727616, -0.024921807, 0.051639438, 0.006532631, -0.018751068) * go_3(-1.0, -1.0);
result += mat4(0.022455849, -0.12924309, 0.26318657, -0.32464805, -0.09627585, 0.04496843, -0.09630052, -0.025761643, -0.090804085, 0.24410398, -0.03162944, -0.1961483, 0.14065808, -0.064709485, -0.0040163463, 0.05445074) * go_3(-1.0, 0.0);
result += mat4(-0.020935195, -0.1028065, 0.0012804621, 0.02302866, -0.00924972, -0.0041193594, 0.0060590385, -0.003394384, -0.23241943, -0.023235107, 0.08077456, 0.15720141, 0.06568382, -0.09971436, 0.09056065, 0.04271102) * go_3(-1.0, 1.0);
result += mat4(-0.20997737, -0.12892777, 0.4658528, 0.13622813, -0.2867294, -0.09359254, 0.18821026, 0.25550604, -0.18562363, 0.080713026, 0.13463654, 0.045504905, -0.013133853, -0.1316404, 0.08379897, -0.00047142128) * go_3(0.0, -1.0);
result += mat4(0.3276134, 0.21952826, -0.80777377, -0.69810224, 0.34190908, -0.09293263, 0.33313555, -0.27255502, -0.24287084, -0.07741488, 0.06090265, -0.10161252, -0.37684909, 0.4678029, 0.13506591, 0.42470258) * go_3(0.0, 0.0);
result += mat4(0.080790855, -0.09707547, -0.05506975, 0.027011644, -0.1434346, 0.01363872, 0.12616752, 0.16789167, 0.1656414, -0.11586835, 0.059612263, -0.074029386, -0.19813071, 0.46032718, -0.03935981, 0.0067143585) * go_3(0.0, 1.0);
result += mat4(0.10322512, 0.0822636, -0.16766444, 0.041008063, -0.027768405, 0.23103505, 0.06737122, 0.15258405, 0.04557388, -0.18179403, 0.12489025, -0.09759324, -0.05925805, 0.04869987, 0.07329833, -0.09738542) * go_3(1.0, -1.0);
result += mat4(-0.10823879, -0.403376, 0.3264802, -0.16503738, -0.057512645, -0.20902547, -0.14862378, -0.3192005, -0.046263676, 0.12744917, -0.019174274, -0.02318789, -0.085088454, -0.05723332, 0.0039772973, 0.07991316) * go_3(1.0, 0.0);
result += mat4(0.10313916, 0.04410904, 0.03286652, 0.059946325, 0.019948404, 0.070217304, -0.017572487, 0.20332281, 0.06776308, 0.029285522, -0.14116238, -0.05864782, -0.18382367, -0.06568212, 0.11855615, 0.101256005) * go_3(1.0, 1.0);
result += vec4(-0.036374483, 0.029420665, 0.04437756, -0.04474691);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(VL)-Conv-4x3x3x16
//!HOOK MAIN
//!BIND conv2d_5_tf
//!BIND conv2d_5_tf1
//!SAVE conv2d_6_tf1
//!WIDTH conv2d_5_tf.w
//!HEIGHT conv2d_5_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define go_0(x_off, y_off) (max((conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_1(x_off, y_off) (max((conv2d_5_tf1_texOff(vec2(x_off, y_off))), 0.0))
#define go_2(x_off, y_off) (max(-(conv2d_5_tf_texOff(vec2(x_off, y_off))), 0.0))
#define go_3(x_off, y_off) (max(-(conv2d_5_tf1_texOff(vec2(x_off, y_off))), 0.0))
vec4 hook() {
vec4 result = mat4(0.059325468, 0.10884231, 0.018158086, 0.031802185, 0.10368743, -0.06776637, 0.048326045, -0.06312353, -0.0025675546, 0.09309577, -0.025533969, 0.029684044, 0.017237723, 0.062099144, 0.047039766, 0.050348036) * go_0(-1.0, -1.0);
result += mat4(-0.04767078, -0.06409279, 0.112965874, 0.04621161, -0.28172916, -0.13897015, -0.022806352, 0.26966885, 0.02019569, -0.10707113, -0.43058416, -0.14103983, -0.13225646, -0.020053176, -0.17319782, -0.009653082) * go_0(-1.0, 0.0);
result += mat4(0.0031349238, -0.060933832, 0.107986666, -0.019791966, -0.23946726, -0.18045186, 0.18286318, -0.05431065, 0.11742379, -0.019123906, 0.33327517, 0.07455424, -0.035427105, 0.18659347, -0.050884776, 0.019193258) * go_0(-1.0, 1.0);
result += mat4(-0.22954239, 0.011265787, -0.026520751, -0.12629737, -0.07009803, 0.44925988, -0.15938939, 0.11956771, 0.11535644, -0.1302371, 0.1235775, 0.16483483, 0.022965495, 0.110546246, 0.00064579415, -0.12753843) * go_0(0.0, -1.0);
result += mat4(0.047553673, 0.16213869, 0.7510964, 0.21228868, 0.40994287, 0.61919236, 0.3982374, -0.016163021, 0.3291035, 0.1134356, 0.12384387, -0.31114763, 0.21338554, -0.04721641, 0.122114286, 0.2717476) * go_0(0.0, 0.0);
result += mat4(-0.06529201, -0.08936482, 0.031857736, -0.02372691, 0.0416097, 0.28484538, -0.38181338, -0.05129518, 0.40150553, -0.01970737, 0.1043854, 0.11986372, -0.2267319, 0.0014845231, -0.035269983, 0.11712099) * go_0(0.0, 1.0);
result += mat4(0.079867415, -0.09982735, 0.10313241, 0.055490237, -0.42685422, -0.3431141, -0.06037366, 0.17539841, -0.010511819, -0.09743252, 0.050748866, 0.11064108, -0.09785722, -0.10230299, -0.04106169, -0.016831731) * go_0(1.0, -1.0);
result += mat4(-0.06847075, -0.026447225, -0.123430386, 0.063637204, -0.37617612, -0.09615662, -0.26226708, -0.008175561, -0.08101131, 0.11093525, -0.13149206, -0.06363292, -0.0482858, -0.2771799, 0.10528571, 0.119109035) * go_0(1.0, 0.0);
result += mat4(0.09151277, 0.029019276, 0.041349206, -0.011239478, 0.035083957, 0.05281079, -0.0742173, -0.018509442, -0.17175299, -0.4226507, -0.118186444, -0.0771296, 0.107038856, 0.0819975, 0.12445646, 0.07091557) * go_0(1.0, 1.0);
result += mat4(0.1275357, -0.097659886, -0.0114354445, 0.023900568, -0.02511702, 0.005830569, -0.010882143, -0.04046068, -0.08638482, 0.08664022, -0.15654318, 0.03333846, -0.12521335, -0.11987078, 0.028556254, -0.020760164) * go_1(-1.0, -1.0);
result += mat4(-0.38474286, -0.15288061, 0.04925842, 0.050009686, 0.23555282, 0.054784663, -0.0971203, 0.017791113, -0.35539824, -0.08806168, 0.08992579, 0.22714761, -0.047685623, -0.17510797, 0.1137738, -0.069451034) * go_1(-1.0, 0.0);
result += mat4(-0.16623408, -0.08202571, -0.03291826, 0.0016267949, 0.20682698, 0.08788948, 0.10241089, 0.019209227, -0.14802241, 0.091788374, -0.238735, -0.06633396, 0.02360112, 0.1521805, -0.022510838, -0.08931379) * go_1(-1.0, 1.0);
result += mat4(0.034280665, -0.12431295, 0.092791, 0.15279225, -0.43373865, 0.20077267, -0.15919733, -0.27969292, -0.26948065, 0.19652127, -0.27456176, 0.04137772, 0.006545539, 0.0031402514, 0.03849979, -0.10978278) * go_1(0.0, -1.0);
result += mat4(0.62025917, -0.32462567, 0.2817292, -0.18380783, -0.3338593, -0.49056754, 0.32645953, 0.4146035, 0.3773462, 0.54346967, -0.032203436, -0.14506778, -0.30044907, 0.40134314, 0.24155408, 0.24397472) * go_1(0.0, 0.0);
result += mat4(0.089335114, -0.05529855, -0.18364899, -0.153323, -0.18347202, -0.060125064, -0.29216367, -0.2717291, 0.10592963, 0.38889876, 0.25363386, 0.33723134, -0.103703365, 0.14922962, -0.21206948, -0.20289616) * go_1(0.0, 1.0);
result += mat4(-0.035760924, 0.18820894, -0.12723185, -0.018780319, 0.124459654, 0.28909087, -0.2763883, -0.45110545, 0.098143585, 0.16052029, -0.055098705, -0.14840914, -0.0019514654, 0.07090622, -0.055036955, -0.0035953245) * go_1(1.0, -1.0);
result += mat4(-0.124669634, 0.23131305, -0.05750295, -0.056296032, 0.35691026, 0.2640789, 0.49912274, 0.26795143, -0.26460487, -0.026896512, -0.07179325, 0.17373477, -0.13186656, 0.0021319336, -0.016407885, 0.3014283) * go_1(1.0, 0.0);
result += mat4(-0.09491939, 0.11503968, -0.14077829, -0.043197304, -0.061866064, -0.1574549, 0.0054375776, 0.066160634, -0.17686372, -0.26767558, -0.038844116, 0.122724466, -0.05043839, 0.063884266, 0.0064002997, -0.13583377) * go_1(1.0, 1.0);
result += mat4(0.031301867, -0.02947819, -0.0016769855, 0.12952408, -0.025022922, 0.065425046, -0.072289295, -0.071249105, 0.14579567, -0.09058119, 0.12663712, 0.1515388, 0.44767743, 0.02971349, 0.015892735, -0.08058422) * go_2(-1.0, -1.0);
result += mat4(-0.2868111, -0.10812653, -0.29182926, -0.38444322, -0.0875354, -0.07220258, 0.05978065, 0.093328245, 0.058548283, -0.013913258, -0.20954674, -0.16400063, 0.3185215, 0.068897314, 0.15869021, 0.022877626) * go_2(-1.0, 0.0);
result += mat4(0.116845705, -0.12729645, 0.056697316, -0.21263942, -0.07000074, 0.073977455, -0.09006404, -0.029770354, -0.20823102, -0.20088868, 0.15658094, 0.24306639, -0.0453592, -0.16011035, 0.08521533, -0.032264974) * go_2(-1.0, 1.0);
result += mat4(0.1114789, -0.1083731, 0.10465276, -0.08903837, -0.06455987, 0.040030345, -0.07937248, -0.20654759, -0.26873547, -0.19390975, -0.039021965, -0.025602374, -0.5575801, -0.08876011, -0.19116728, -0.2401055) * go_2(0.0, -1.0);
result += mat4(0.37626424, -0.0912155, -0.6153361, -0.71465075, 0.018208932, -0.14997734, 0.23627761, 0.20832567, 0.07427123, -0.37869486, -0.26574427, 0.187582, -0.37201726, 0.17809474, -0.02568795, 0.23900814) * go_2(0.0, 0.0);
result += mat4(-0.085337594, -0.50634587, 0.30636734, -0.2760558, 0.01893911, -0.08425695, -0.023656169, 0.021421626, 0.16813251, -0.039550815, 0.21165498, -0.027628547, -0.123874225, 0.013802332, -0.2732087, -0.09419671) * go_2(0.0, 1.0);
result += mat4(-0.07190724, -0.019237598, 0.020249542, 0.07541295, -0.03817686, 0.09266451, -0.12214172, -0.01344174, 0.03281797, 0.057655178, -0.059896503, 0.014948791, -0.13952477, 0.18810949, -0.19016883, 0.06842416) * go_2(1.0, -1.0);
result += mat4(-0.13111524, 0.14539744, -0.10212538, -0.2169032, 0.13810973, -0.12576458, 0.124372825, 0.04992259, 0.21758182, -0.22160134, 0.24321079, 0.017698256, 0.39995426, 0.074034885, 0.120019354, -0.15522505) * go_2(1.0, 0.0);
result += mat4(0.023914235, 0.1424257, 0.010302871, 0.15150794, -0.040021677, 0.015862139, 0.14459212, 0.08632827, 0.04257336, 0.055059638, 0.0030461506, 0.011985334, -0.049230937, 0.07851301, -0.05119983, -0.111701734) * go_2(1.0, 1.0);
result += mat4(0.04485158, 0.116597414, 0.00014909732, -0.012128512, 0.15801767, 0.18273115, -0.033926453, 0.05170487, -0.040683754, -0.18606974, 0.08324687, 0.069539666, 0.07098698, -0.014132968, 0.029499048, -0.07263477) * go_3(-1.0, -1.0);
result += mat4(0.04309544, 0.089722805, -0.018306322, 0.29061043, 0.15191254, 0.15917647, 0.0073858183, 0.039199475, 0.42514518, -0.053955313, 0.10820046, -0.09134685, -0.3087313, -0.16339037, -0.05226669, 0.044995327) * go_3(-1.0, 0.0);
result += mat4(0.008636428, 0.029086163, -0.09151674, -0.36466715, -0.0128008155, 0.018820466, -0.02700147, -0.0064047636, 0.28287655, 0.02709404, -0.05233492, -0.08967187, -0.042183813, -0.13990502, -0.005085154, -0.028511493) * go_3(-1.0, 1.0);
result += mat4(0.00022532263, -0.09108507, 0.0089569865, 0.052016005, -0.19314727, -0.355347, 0.08082937, 0.2134498, 0.21036889, -0.10165983, 0.20334485, 0.14575538, 0.017676214, -0.13149881, -0.018741794, -0.019599862) * go_3(0.0, -1.0);
result += mat4(-0.20513605, 0.47578803, -0.18631598, 0.2535432, -0.049522053, -0.37224755, 0.11227206, -0.37000927, 0.19969453, -0.47287735, -0.07506754, -0.0957071, 0.82927394, -0.54057014, 0.5800732, 0.08937558) * go_3(0.0, 0.0);
result += mat4(-0.022189412, 0.14622113, -0.4772564, -0.31178755, 0.10667427, -0.07335338, 0.06144331, 0.00056827103, -0.08263861, -0.009126272, -0.22802618, -0.20760304, 0.12688845, -0.061324466, 0.33361357, 0.38350767) * go_3(0.0, 1.0);
result += mat4(0.021188622, 0.1151918, -0.10654739, -0.03341855, 0.24870358, -0.06689332, 0.11881217, -0.0045951125, -0.039464932, -0.030190004, 0.014174111, -0.025356272, 0.07469406, -0.0059695644, 0.008267219, -0.0991054) * go_3(1.0, -1.0);
result += mat4(-0.009981438, -0.36484948, 0.04801225, 0.22368562, -0.055985868, 0.229039, -0.10823553, 0.1477355, -0.0091677625, 0.06279847, 0.034393013, 0.031901076, 0.28783056, 0.086422645, 0.20860936, 0.054018307) * go_3(1.0, 0.0);
result += mat4(-0.08720452, -0.07756267, 0.018853918, -0.014108689, -0.019337144, 0.021249043, -0.05633926, -0.109904505, -0.088990815, 0.16876367, -0.13149975, -0.054357648, 0.08588134, -0.10262266, 0.12052009, 0.05154292) * go_3(1.0, 1.0);
result += vec4(-0.010602045, 0.053976092, 0.008913503, 0.0011945717);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(VL)-Conv-4x1x1x112
//!HOOK MAIN
//!BIND conv2d_tf
//!BIND conv2d_tf1
//!BIND conv2d_1_tf
//!BIND conv2d_1_tf1
//!BIND conv2d_2_tf
//!BIND conv2d_2_tf1
//!BIND conv2d_3_tf
//!BIND conv2d_3_tf1
//!BIND conv2d_4_tf
//!BIND conv2d_4_tf1
//!BIND conv2d_5_tf
//!BIND conv2d_5_tf1
//!BIND conv2d_6_tf
//!BIND conv2d_6_tf1
//!SAVE conv2d_last_tf
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define g_0 (max((conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_1 (max((conv2d_tf1_tex(conv2d_tf1_pos)), 0.0))
#define g_2 (max(-(conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_3 (max(-(conv2d_tf1_tex(conv2d_tf1_pos)), 0.0))
#define g_4 (max((conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_5 (max((conv2d_1_tf1_tex(conv2d_1_tf1_pos)), 0.0))
#define g_6 (max(-(conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_7 (max(-(conv2d_1_tf1_tex(conv2d_1_tf1_pos)), 0.0))
#define g_8 (max((conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_9 (max((conv2d_2_tf1_tex(conv2d_2_tf1_pos)), 0.0))
#define g_10 (max(-(conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_11 (max(-(conv2d_2_tf1_tex(conv2d_2_tf1_pos)), 0.0))
#define g_12 (max((conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_13 (max((conv2d_3_tf1_tex(conv2d_3_tf1_pos)), 0.0))
#define g_14 (max(-(conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_15 (max(-(conv2d_3_tf1_tex(conv2d_3_tf1_pos)), 0.0))
#define g_16 (max((conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_17 (max((conv2d_4_tf1_tex(conv2d_4_tf1_pos)), 0.0))
#define g_18 (max(-(conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_19 (max(-(conv2d_4_tf1_tex(conv2d_4_tf1_pos)), 0.0))
#define g_20 (max((conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_21 (max((conv2d_5_tf1_tex(conv2d_5_tf1_pos)), 0.0))
#define g_22 (max(-(conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_23 (max(-(conv2d_5_tf1_tex(conv2d_5_tf1_pos)), 0.0))
#define g_24 (max((conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_25 (max((conv2d_6_tf1_tex(conv2d_6_tf1_pos)), 0.0))
#define g_26 (max(-(conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_27 (max(-(conv2d_6_tf1_tex(conv2d_6_tf1_pos)), 0.0))
vec4 hook() {
vec4 result = mat4(-0.105475314, -0.07022547, -0.16326137, -0.12503424, -0.004623021, -0.0143323885, 0.042996034, 0.03422294, -0.38310882, -0.4431925, -0.28772846, -0.3213578, -0.018014904, 0.02429277, -0.07177951, -0.04458822) * g_0;
result += mat4(-0.0973233, -0.032439478, -0.08420249, -0.054693196, 0.012960555, 0.06929602, 0.004247494, 0.061315402, -0.09607745, -0.16862066, 0.01537482, -0.038459156, 0.019662246, 0.059920583, -0.1071646, -0.06478967) * g_1;
result += mat4(0.15711947, 0.0754732, 0.17891979, 0.098270796, 0.14122486, 0.14893766, 0.12408279, 0.14845194, 0.16199848, 0.14090912, 0.13496809, 0.1119815, 0.03974558, -0.057513904, 0.09213575, -0.0012252429) * g_2;
result += mat4(-0.011343602, -0.02488338, 0.07799659, 0.06503721, 0.06380687, 0.048929837, -0.05555838, -0.050519127, 0.14673206, 0.18085165, 0.07261422, 0.09738158, 0.07395791, 0.005573146, -0.05454926, -0.13565786) * g_3;
result += mat4(-0.08591514, -0.05664865, 0.23980616, 0.24876402, 0.19052829, 0.011938714, 0.21487322, 0.058656186, 0.036630988, 0.14918756, 0.013127693, 0.13092093, -0.37889576, -0.4068804, -0.27258882, -0.30605716) * g_4;
result += mat4(-0.25149816, -0.21979512, -0.24949454, -0.20483162, -0.10972783, -0.17315808, -0.08562763, -0.16086778, 0.044681527, 0.050807394, -0.019424994, -0.022418005, 0.10039492, -0.013666552, -0.22373566, -0.34493732) * g_5;
result += mat4(0.1419155, 0.081392206, -0.18103191, -0.2122926, -0.1445937, -0.015969204, -0.12368782, -0.0044421684, -0.09534078, -0.14815839, -0.1052107, -0.16341865, 0.3050403, 0.34488317, 0.16171226, 0.18700944) * g_6;
result += mat4(0.12444696, 0.08712589, 0.06266247, 0.031022022, 0.17707655, 0.24904409, 0.20961654, 0.2610619, -0.099262595, -0.06900819, -0.034567446, -0.020191457, -0.1468561, -0.04683958, 0.14910224, 0.244686) * g_7;
result += mat4(-0.002428158, -0.012889509, 0.0006541127, -0.0058380975, 0.096147396, 0.07791617, 0.119144954, 0.11699654, -0.024602454, -0.07894611, -0.00021709128, -0.03979557, 0.0028512406, -0.015790012, 0.0082511455, 0.029357092) * g_8;
result += mat4(-0.01410329, -0.004162405, -0.09005045, -0.07753674, 0.004509965, -0.024188736, 0.13799691, 0.10589621, -0.023018798, 0.0064198375, -0.103344224, -0.07463909, -0.060048997, -0.071094714, -0.13042289, -0.14482167) * g_9;
result += mat4(-0.009015246, 0.01581748, -0.035448726, -0.012348933, -0.101627484, -0.05530413, -0.14063041, -0.121775225, 0.074719116, 0.033839386, 0.045573987, -0.006698053, 0.0015141299, 0.003634417, 0.017102007, 0.0074890694) * g_10;
result += mat4(0.0042357175, 0.018735386, 0.058959343, 0.057424515, -0.021633089, -0.037194982, -0.14109972, -0.1506368, 0.004357002, -0.006871023, 0.05337361, 0.039684236, 0.087463334, 0.07772685, 0.12278512, 0.1224218) * g_11;
result += mat4(0.018359886, 0.046934873, -0.008225237, 0.020650858, -0.03961538, -0.014779162, -0.04161338, -0.00953579, 0.0017313146, 0.0068857935, -0.0024282748, 0.0047545764, 0.02635904, 0.027336216, 0.02701322, 0.029939381) * g_12;
result += mat4(-0.00067966996, 0.024480496, -0.015218739, -0.010472019, -0.03994461, -0.052318517, -0.04450191, -0.043226667, -0.03166469, -0.03799331, 0.015428865, -0.018422252, 0.00040845043, 0.03558268, -0.0099401595, -0.00054432114) * g_13;
result += mat4(-0.0032104475, 0.019604867, -0.02486679, 0.002134673, 0.014368818, -0.0013395248, 0.017318068, 0.0021403218, -0.02198377, 0.010297547, -0.041619625, -0.02740482, -0.067249276, -0.03040953, -0.021304253, -0.009557115) * g_14;
result += mat4(-0.019099236, -0.037010793, 0.013720462, 0.023708181, 0.016356282, -0.00028589502, -0.010570909, -0.009186907, 0.03493662, 0.055599142, -0.017043956, 0.004204044, -0.013573257, -0.013537684, 0.008151195, 0.0074913655) * g_15;
result += mat4(0.009309031, -0.0014795153, 0.025114728, -0.0066442797, -0.012085473, -0.0030560147, 0.002144206, 0.0009732741, 0.022301642, -0.0091133695, 0.0011837826, -0.020275833, -0.021349607, -0.011693419, -0.018912962, -0.022418445) * g_16;
result += mat4(-0.0045772395, 0.031085191, 0.01215795, 0.023887333, 0.023408212, 0.0005998807, 0.011254428, -0.004634461, 0.016601006, 0.046663348, 0.031117432, 0.04910873, -0.113230005, -0.035702843, -0.058746565, -0.053893737) * g_17;
result += mat4(-0.020218112, 0.056803435, -0.0037077996, 0.05123925, -0.016713811, -0.05551032, -0.005916611, -0.037839632, -0.007671626, -0.009099201, -0.0010055836, 0.003332688, 0.020744357, 0.01957675, 0.057906736, 0.041446246) * g_18;
result += mat4(0.022438819, 0.04616756, 0.035925094, 0.0639705, 0.0009332198, 0.020964272, -0.010805394, 0.031757344, 0.051255573, 0.032838948, 0.00055445684, -0.03195623, 0.04753827, 0.016436901, 0.04788274, 0.022093765) * g_19;
result += mat4(0.03479086, 0.035946105, 0.04343359, 0.04015664, 0.06081792, 0.061758887, 0.10128842, 0.007471392, -0.027261607, -0.01290544, -0.029938918, -0.050834358, -0.015550162, 0.0072828676, -0.04580556, -0.029642029) * g_20;
result += mat4(0.011150116, 0.029789668, -0.00354488, 0.045047592, -0.018265083, -0.020843878, 0.015457328, 0.0053232997, 0.0791804, -0.028661052, 0.079342775, -0.039631505, 0.14613943, 0.08323415, 0.049641483, 0.047863442) * g_21;
result += mat4(-0.103034586, -0.107580125, 0.00044325445, 0.007830247, -0.017059505, 0.010152936, -0.02845979, -0.01841766, -0.10722863, -0.025262646, -0.07402096, -0.025055556, 0.0013303137, 0.12574737, -0.0161103, 0.06077798) * g_22;
result += mat4(-0.0420636, -0.062703885, -0.06476972, -0.10516001, 0.018120673, 0.024305122, -0.013997766, 0.015815413, -0.06317691, -0.03968166, -0.054052643, -0.016300509, -0.08255892, -0.01612941, -0.04194852, -0.012637189) * g_23;
result += mat4(0.042659573, -0.10762496, -0.077143244, 0.12583935, -0.022020226, -0.0042312425, -0.016734738, 0.027007964, -0.06609771, -0.056038737, -0.0058528963, 0.035508137, -0.019722374, -0.055094264, 0.010977759, -0.009833099) * g_24;
result += mat4(0.063830875, -0.019885639, 0.055574782, 0.039456647, 0.01576898, -0.1389799, 0.063411795, -0.11600623, -0.013968303, -0.03318867, -0.06806915, -0.09373464, -0.022723546, -0.03329239, 0.014282872, 0.027576538) * g_25;
result += mat4(-0.018100513, 0.06204485, 0.010761461, -0.045085587, 0.009286288, 0.02310671, 0.10633246, -0.090849996, 0.13112675, -0.01639808, 0.0022725316, -0.076779045, 0.11831251, 0.1460306, -0.10849466, -0.07749171) * g_26;
result += mat4(-0.15850247, 0.118011266, -0.10121594, -0.007109052, 0.071873754, 0.06954878, 0.0377852, 0.044174008, -0.062925555, -0.01758927, 0.1416964, 0.17206357, -0.035632525, -0.04652215, 0.061932907, 0.034339) * g_27;
result += vec4(-0.11952045, -0.10779418, -0.0626279, -0.042614873);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(VL)-Conv-4x1x1x112
//!HOOK MAIN
//!BIND conv2d_tf
//!BIND conv2d_tf1
//!BIND conv2d_1_tf
//!BIND conv2d_1_tf1
//!BIND conv2d_2_tf
//!BIND conv2d_2_tf1
//!BIND conv2d_3_tf
//!BIND conv2d_3_tf1
//!BIND conv2d_4_tf
//!BIND conv2d_4_tf1
//!BIND conv2d_5_tf
//!BIND conv2d_5_tf1
//!BIND conv2d_6_tf
//!BIND conv2d_6_tf1
//!SAVE conv2d_last_tf1
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define g_0 (max((conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_1 (max((conv2d_tf1_tex(conv2d_tf1_pos)), 0.0))
#define g_2 (max(-(conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_3 (max(-(conv2d_tf1_tex(conv2d_tf1_pos)), 0.0))
#define g_4 (max((conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_5 (max((conv2d_1_tf1_tex(conv2d_1_tf1_pos)), 0.0))
#define g_6 (max(-(conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_7 (max(-(conv2d_1_tf1_tex(conv2d_1_tf1_pos)), 0.0))
#define g_8 (max((conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_9 (max((conv2d_2_tf1_tex(conv2d_2_tf1_pos)), 0.0))
#define g_10 (max(-(conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_11 (max(-(conv2d_2_tf1_tex(conv2d_2_tf1_pos)), 0.0))
#define g_12 (max((conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_13 (max((conv2d_3_tf1_tex(conv2d_3_tf1_pos)), 0.0))
#define g_14 (max(-(conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_15 (max(-(conv2d_3_tf1_tex(conv2d_3_tf1_pos)), 0.0))
#define g_16 (max((conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_17 (max((conv2d_4_tf1_tex(conv2d_4_tf1_pos)), 0.0))
#define g_18 (max(-(conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_19 (max(-(conv2d_4_tf1_tex(conv2d_4_tf1_pos)), 0.0))
#define g_20 (max((conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_21 (max((conv2d_5_tf1_tex(conv2d_5_tf1_pos)), 0.0))
#define g_22 (max(-(conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_23 (max(-(conv2d_5_tf1_tex(conv2d_5_tf1_pos)), 0.0))
#define g_24 (max((conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_25 (max((conv2d_6_tf1_tex(conv2d_6_tf1_pos)), 0.0))
#define g_26 (max(-(conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_27 (max(-(conv2d_6_tf1_tex(conv2d_6_tf1_pos)), 0.0))
vec4 hook() {
vec4 result = mat4(-0.009000901, -0.018048609, 0.013095594, 0.002321373, 0.0004716619, 0.00504148, -0.016826658, -0.014922383, 0.15059204, 0.16593806, 0.115392484, 0.12520894, 0.05049829, 0.060210057, 0.086421266, 0.07242362) * g_0;
result += mat4(0.06268658, 0.030466434, 0.07876877, 0.04129863, 0.04142328, 0.009963961, 0.051785357, 0.012811113, 0.1295883, 0.139931, 0.07733839, 0.08014211, 0.07156476, 0.0342396, 0.051614303, 0.043559864) * g_1;
result += mat4(0.00041542648, 0.016051646, -0.011512418, 0.013076814, 0.03734479, 0.02791584, 0.012426691, 0.022044811, -0.034128398, -0.027107332, -0.021998279, -0.012139807, -0.033177473, -0.016310865, -0.078221664, -0.041203145) * g_2;
result += mat4(-0.008398536, -0.010332053, -0.050231732, -0.039691273, -0.042082537, -0.030281143, -0.014039778, -0.0020190612, -0.11956351, -0.13638765, -0.09794402, -0.10228069, -0.08344795, -0.07944541, -0.004189214, -0.028206991) * g_3;
result += mat4(0.0002908945, -0.00831185, -0.06870294, -0.083311856, -0.024992501, 0.0038247898, -0.049389005, -0.020098582, -0.0135326125, -0.040408995, -0.012083491, -0.042174604, 0.16112538, 0.13720983, 0.13937058, 0.10870099) * g_4;
result += mat4(0.078961425, 0.082619205, 0.06910667, 0.06579004, -0.0077012256, -0.00038692637, 0.00015553503, -0.012561662, 0.00053048285, -0.01461681, 0.02600344, 0.024862211, -0.06958201, -0.048246548, 0.058762506, 0.036662634) * g_5;
result += mat4(-0.023527982, -0.0028001352, 0.047800142, 0.09616409, 0.049143843, 0.030836122, 0.057244994, 0.025672587, 0.027565151, 0.039868724, 0.045296676, 0.04623187, -0.124759234, -0.14106254, -0.06337279, -0.076839216) * g_6;
result += mat4(-0.0911771, -0.064436875, -0.05308137, -0.022082496, -0.0040269364, 0.0014464161, -0.0029555515, 0.016098293, -0.026650434, -0.014081368, -0.06747348, -0.05481826, 0.097423114, 0.08620988, -0.01607732, -0.015440677) * g_7;
result += mat4(-0.014001735, -0.015001655, -0.013250577, -0.009930805, 0.04885879, 0.07092224, 0.025783395, 0.03792237, -0.04332465, -0.06244993, -0.046748653, -0.07132349, -0.0053951666, -0.016514057, 0.023807624, 0.044013456) * g_8;
result += mat4(-0.009097996, -0.016898679, -0.05043909, -0.063178614, -0.016210863, -0.02157998, -0.02654472, -0.042961173, 0.012103852, 0.019015301, 0.02492281, 0.03389976, 0.015276502, 0.009577683, 0.04132527, -0.00070621347) * g_9;
result += mat4(-0.0057500796, 0.00728164, -0.003422421, 0.0038979584, -0.03127353, -0.019125199, -0.012988815, -0.031890683, 0.09352588, 0.019210607, 0.09824038, 0.016637104, 0.010692808, 0.022393884, 0.008312123, 0.014120716) * g_10;
result += mat4(0.013895599, 0.023097904, 0.009370535, 0.014099512, 0.0124661345, -0.015076684, 0.03287286, 0.005912471, -0.03944815, -0.020340785, -0.06822037, -0.059383288, 0.03634978, 0.007832939, -0.007142306, -0.0061968984) * g_11;
result += mat4(0.033002097, 0.0516016, -0.021056438, 0.005715988, -0.02223013, -0.007962324, -0.024417123, -0.0014790733, 0.002167189, 0.00043749413, -0.007284963, -0.0027283782, 0.026238248, 0.01756047, 0.008969755, 0.014201024) * g_12;
result += mat4(0.011576685, 0.02087598, 0.0026766327, -0.0041780816, -0.05277701, -0.05412841, -0.05958835, -0.050426245, -0.00662945, -0.021645393, 0.03423904, -0.0064581474, -0.030403355, 0.018391011, -0.026089542, -0.0051510665) * g_13;
result += mat4(-0.046202097, -0.0066081425, -0.03698851, 0.0034165455, -0.011859245, -0.020945566, -0.0028196946, -0.010053285, -0.011400397, 0.030595876, -0.018915813, 0.006780077, -0.060040582, -0.009586898, -0.004477886, 0.011279908) * g_14;
result += mat4(-0.028692413, -0.032535568, 0.0017473884, 0.02207169, 0.0192618, 0.008956797, -0.0033381556, 0.006326402, 0.0169569, 0.041449737, -0.02611751, 0.0006410355, 0.006233776, 0.0008467914, 0.011884985, 0.009222136) * g_15;
result += mat4(0.017076496, -0.0045380928, 0.03444613, -0.009804047, -0.004829834, -0.004889702, 0.0057807956, 0.0015014127, 0.03458368, -0.0035773432, -0.007769679, -0.032449644, -0.021396799, -0.017612215, -0.012764735, -0.025224172) * g_16;
result += mat4(-0.011824532, 0.02335273, 0.00764845, 0.019215155, 0.022186808, 0.0066053392, 0.0071694753, -0.0036117272, 0.032144524, 0.05025988, 0.03982363, 0.052400436, -0.10555114, -0.03809396, -0.05334183, -0.05524487) * g_17;
result += mat4(-0.024599254, 0.058805298, 0.00069874676, 0.06263439, -0.018460508, -0.053566024, -0.0022889362, -0.035818785, -0.0135854995, -0.015712813, 0.0012080368, 0.005957637, 0.009450094, 0.03186346, 0.059969924, 0.057706963) * g_18;
result += mat4(0.026783831, 0.05475865, 0.027565574, 0.06032707, -0.0015639095, 0.024381682, -0.010199071, 0.037544634, 0.039889377, 0.03318851, -0.016529158, -0.0343188, 0.045666486, 0.021665907, 0.042189375, 0.02444145) * g_19;
result += mat4(0.03791853, 0.043746054, 0.056224477, 0.05098111, 0.075256795, 0.074653305, 0.116220035, 0.01853866, -0.04133627, -0.009134169, -0.0420953, -0.05210053, -0.021748418, 0.004422131, -0.05422814, -0.035721727) * g_20;
result += mat4(0.013814317, 0.03149986, -0.004971173, 0.04782029, -0.01693027, -0.017984565, 0.019328078, 0.008521426, 0.0845641, -0.027555496, 0.08150416, -0.04623306, 0.16494128, 0.09300831, 0.074097835, 0.0627848) * g_21;
result += mat4(-0.10307174, -0.112654425, -0.005589254, -0.0062108496, -0.012491583, 0.011512013, -0.03142282, -0.023683488, -0.099848576, -0.031290524, -0.07236223, -0.037460987, 0.008760208, 0.1473594, -0.009216949, 0.07251379) * g_22;
result += mat4(-0.04915367, -0.07121096, -0.06572174, -0.10967046, 0.019548079, 0.023992533, -0.019842865, 0.012366459, -0.07207817, -0.04237792, -0.054463565, -0.015374731, -0.092071235, -0.020860313, -0.054475963, -0.02303954) * g_23;
result += mat4(0.04160816, -0.118427366, -0.08661791, 0.12787233, -0.01990174, 0.0012960634, -0.016121056, 0.031429946, -0.06830865, -0.057132352, -0.0022302791, 0.03845933, -0.026981276, -0.063532256, 0.011805961, -0.009616678) * g_24;
result += mat4(0.07094465, -0.022284096, 0.060676746, 0.042626668, 0.011207256, -0.14960343, 0.05866539, -0.12742221, -0.021092903, -0.039463162, -0.07879986, -0.10232898, -0.026127055, -0.038111385, 0.019167708, 0.032637425) * g_25;
result += mat4(-0.014270794, 0.07157703, 0.013714203, -0.047801998, 0.0060221693, 0.022788104, 0.10630103, -0.09606649, 0.12690987, -0.017079826, -0.0077072172, -0.082584485, 0.13256006, 0.16012523, -0.10966099, -0.07927409) * g_26;
result += mat4(-0.17171615, 0.12114435, -0.10746857, -0.0074188868, 0.07854815, 0.07759372, 0.04310874, 0.051465522, -0.05963588, -0.014506484, 0.15522978, 0.18746643, -0.03845241, -0.0489534, 0.05837339, 0.032978524) * g_27;
result += vec4(0.05825913, 0.051491056, 0.038389463, 0.03321517);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(VL)-Conv-4x1x1x112
//!HOOK MAIN
//!BIND conv2d_tf
//!BIND conv2d_tf1
//!BIND conv2d_1_tf
//!BIND conv2d_1_tf1
//!BIND conv2d_2_tf
//!BIND conv2d_2_tf1
//!BIND conv2d_3_tf
//!BIND conv2d_3_tf1
//!BIND conv2d_4_tf
//!BIND conv2d_4_tf1
//!BIND conv2d_5_tf
//!BIND conv2d_5_tf1
//!BIND conv2d_6_tf
//!BIND conv2d_6_tf1
//!SAVE conv2d_last_tf2
//!WIDTH conv2d_tf.w
//!HEIGHT conv2d_tf.h
//!COMPONENTS 4
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
#define g_0 (max((conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_1 (max((conv2d_tf1_tex(conv2d_tf1_pos)), 0.0))
#define g_2 (max(-(conv2d_tf_tex(conv2d_tf_pos)), 0.0))
#define g_3 (max(-(conv2d_tf1_tex(conv2d_tf1_pos)), 0.0))
#define g_4 (max((conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_5 (max((conv2d_1_tf1_tex(conv2d_1_tf1_pos)), 0.0))
#define g_6 (max(-(conv2d_1_tf_tex(conv2d_1_tf_pos)), 0.0))
#define g_7 (max(-(conv2d_1_tf1_tex(conv2d_1_tf1_pos)), 0.0))
#define g_8 (max((conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_9 (max((conv2d_2_tf1_tex(conv2d_2_tf1_pos)), 0.0))
#define g_10 (max(-(conv2d_2_tf_tex(conv2d_2_tf_pos)), 0.0))
#define g_11 (max(-(conv2d_2_tf1_tex(conv2d_2_tf1_pos)), 0.0))
#define g_12 (max((conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_13 (max((conv2d_3_tf1_tex(conv2d_3_tf1_pos)), 0.0))
#define g_14 (max(-(conv2d_3_tf_tex(conv2d_3_tf_pos)), 0.0))
#define g_15 (max(-(conv2d_3_tf1_tex(conv2d_3_tf1_pos)), 0.0))
#define g_16 (max((conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_17 (max((conv2d_4_tf1_tex(conv2d_4_tf1_pos)), 0.0))
#define g_18 (max(-(conv2d_4_tf_tex(conv2d_4_tf_pos)), 0.0))
#define g_19 (max(-(conv2d_4_tf1_tex(conv2d_4_tf1_pos)), 0.0))
#define g_20 (max((conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_21 (max((conv2d_5_tf1_tex(conv2d_5_tf1_pos)), 0.0))
#define g_22 (max(-(conv2d_5_tf_tex(conv2d_5_tf_pos)), 0.0))
#define g_23 (max(-(conv2d_5_tf1_tex(conv2d_5_tf1_pos)), 0.0))
#define g_24 (max((conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_25 (max((conv2d_6_tf1_tex(conv2d_6_tf1_pos)), 0.0))
#define g_26 (max(-(conv2d_6_tf_tex(conv2d_6_tf_pos)), 0.0))
#define g_27 (max(-(conv2d_6_tf1_tex(conv2d_6_tf1_pos)), 0.0))
vec4 hook() {
vec4 result = mat4(0.2006987, 0.17832398, 0.26342955, 0.23500517, -0.012297829, -0.009008417, -0.039950736, -0.039973143, 0.34800097, 0.32196492, 0.30505183, 0.29214156, -0.21410535, -0.21166423, -0.16437815, -0.19172792) * g_0;
result += mat4(-0.008804151, -0.07085123, 0.013577994, -0.05192605, -0.08981402, -0.14702585, -0.09145975, -0.14835288, -0.15882517, -0.14785844, -0.2381482, -0.22867912, 0.010898514, 0.031957507, 0.040597558, 0.078252345) * g_1;
result += mat4(-0.21658613, -0.1803885, -0.25954962, -0.20839214, -0.09597461, -0.09222647, -0.03909875, -0.03456191, -0.19723509, -0.16976605, -0.2041716, -0.1751425, 0.22901416, 0.24922715, 0.1800083, 0.23346905) * g_2;
result += mat4(0.110020064, 0.103858806, 0.052446555, 0.061105963, 0.032901537, 0.07140097, 0.11518793, 0.13860466, 0.13930707, 0.12712196, 0.19071707, 0.18399614, -0.08036458, -0.07349171, 0.021504594, 0.0024937368) * g_3;
result += mat4(0.059065036, 0.00698257, -0.099622436, -0.15676253, -0.10942482, -0.04869624, -0.13654554, -0.07341863, -0.014169851, -0.06390744, 0.016093008, -0.04540248, 0.29041344, 0.24451919, 0.26292154, 0.22136512) * g_4;
result += mat4(0.107946776, 0.097849295, 0.10266876, 0.09360328, 0.08931344, 0.08896482, 0.046495322, 0.044040844, -0.020361643, -0.030911373, 0.026598722, 0.019815676, -0.072677925, -0.042410247, 0.14127749, 0.13434973) * g_5;
result += mat4(-0.08809133, -0.03476601, 0.06420393, 0.14691353, 0.09296839, 0.06162562, 0.10992992, 0.0615685, 0.0168736, 0.06520281, 0.020010693, 0.046929173, -0.2219495, -0.21249783, -0.14622301, -0.14599061) * g_6;
result += mat4(-0.13251069, -0.08977477, -0.08930347, -0.045490693, -0.10980218, -0.09510885, -0.07299872, -0.064053826, 0.011365247, 0.014091111, -0.054976214, -0.056936122, 0.10148144, 0.07451642, -0.08138598, -0.10161657) * g_7;
result += mat4(-0.0075518745, -0.005738622, -0.007577811, -0.00032088626, 0.032614008, 0.04858922, 0.00054855715, 0.011565026, -0.022675224, -0.034442738, -0.03580643, -0.05069376, -0.0020376542, -0.01505518, 0.019388825, 0.03746554) * g_8;
result += mat4(-0.011413172, -0.016877454, -0.048923567, -0.055012885, -0.007709447, -0.016109072, -0.047132388, -0.07146396, 0.002604099, 0.00014681708, 0.03429465, 0.043265607, 0.029014807, 0.03337814, 0.07582056, 0.041660666) * g_9;
result += mat4(-0.020768544, -0.014378527, -0.01999972, -0.01385916, -0.012264676, -0.009959511, 0.0119015165, -0.016787319, 0.07266734, -0.0029914333, 0.08549183, 0.004367342, 0.008236065, 0.020370748, 0.0043428927, 0.007301017) * g_10;
result += mat4(0.011654731, 0.025318999, -0.0029306612, 0.007426217, -0.00010868774, -0.020845588, 0.041991003, 0.024147986, -0.030741083, -0.012328637, -0.06617428, -0.06103115, 0.010491518, -0.013338451, -0.04666634, -0.046481613) * g_11;
result += mat4(0.0268538, 0.043785956, -0.01799385, 0.008743307, -0.013197458, -0.015049436, -0.017189734, -0.0047999253, -0.00059730676, -0.0008936153, -0.016006093, -0.0073406673, 0.014875853, 0.011491735, 9.819833e-05, 0.0073417514) * g_12;
result += mat4(0.019930955, 0.027112626, 0.01307941, 0.005268897, -0.060213763, -0.050415818, -0.069006495, -0.051405095, 0.0036414433, -0.008606397, 0.037427194, -0.0018103109, -0.037434716, 0.010187546, -0.026227329, -0.0033639795) * g_13;
result += mat4(-0.03634798, 0.0007093891, -0.026819145, 0.009025687, -0.01750318, -0.020098133, -0.0063864207, -0.006606755, 0.0008565766, 0.028647956, -0.0024974607, 0.015250743, -0.048884176, -0.004310685, -0.0010757383, 0.00974984) * g_14;
result += mat4(-0.031253602, -0.031743724, -0.009083253, 0.0145388115, 0.02048611, 0.0058071036, -0.0038228377, 0.00049654936, 0.0059105973, 0.03437731, -0.025785418, 0.004187733, 0.009980489, -4.08268e-05, 0.009384428, 0.0019492983) * g_15;
result += mat4(0.012587245, -0.0032654977, 0.029739188, -0.009440694, -0.0018237908, -0.0080032, 0.010499013, 0.0012466761, 0.03461923, -0.0060207327, -0.008771263, -0.034545746, -0.015023473, -0.008901684, -0.011490296, -0.01976464) * g_16;
result += mat4(-0.009444331, 0.020809013, 0.009985801, 0.020350901, 0.013234775, 0.004382635, -0.0007761826, -0.005247294, 0.034115106, 0.05190378, 0.039124765, 0.050993033, -0.0898732, -0.030428126, -0.044204578, -0.052484997) * g_17;
result += mat4(-0.020434443, 0.053520404, 0.0007571144, 0.05895061, -0.018991265, -0.043982152, -0.004035192, -0.025452444, -0.012197152, -0.013770753, 0.0012919102, 0.003996682, 0.0056104586, 0.025686186, 0.05128293, 0.05105745) * g_18;
result += mat4(0.030201769, 0.052521482, 0.029641917, 0.05559941, 0.0018870027, 0.020112835, -0.0043867202, 0.035877172, 0.02961142, 0.02163634, -0.027972858, -0.040669747, 0.03393723, 0.013455979, 0.03313782, 0.01968004) * g_19;
result += mat4(0.034817442, 0.04045217, 0.054816365, 0.05092461, 0.06600807, 0.062576495, 0.09923777, 0.006663677, -0.039958935, -0.010009866, -0.041522443, -0.04959681, -0.020962957, 0.003845031, -0.04910384, -0.03233655) * g_20;
result += mat4(0.015433112, 0.028965838, -0.0055138534, 0.042267464, -0.012690953, -0.009424165, 0.017896382, 0.01186686, 0.07231686, -0.038834292, 0.07033086, -0.052548733, 0.15721905, 0.09334892, 0.07676042, 0.06701375) * g_21;
result += mat4(-0.09797534, -0.11201098, -0.0037222446, -0.008105951, -0.01152357, 0.012165641, -0.029051905, -0.021293389, -0.09600697, -0.028819272, -0.069084235, -0.035421908, 0.0054322914, 0.14168966, -0.0200274, 0.06505187) * g_22;
result += mat4(-0.05034882, -0.06622497, -0.062471002, -0.100628324, 0.018115615, 0.019867867, -0.018836644, 0.007562053, -0.06317378, -0.034458403, -0.047243826, -0.009989589, -0.08270121, -0.018645251, -0.05160367, -0.023690399) * g_23;
result += mat4(0.03897899, -0.10862529, -0.081805214, 0.1202324, -0.021866674, -0.00041882638, -0.018235246, 0.027227063, -0.0656312, -0.053432237, -0.0029235696, 0.03549672, -0.022848906, -0.057047505, 0.013400545, -0.0072439364) * g_24;
result += mat4(0.06879516, -0.018637763, 0.058062725, 0.041045032, 0.011702424, -0.13693465, 0.05674195, -0.11679955, -0.022940686, -0.03856922, -0.07531371, -0.09692582, -0.019870926, -0.032572743, 0.026138868, 0.037639033) * g_25;
result += mat4(-0.015270301, 0.06478719, 0.011016518, -0.04533957, 0.00688319, 0.024815995, 0.10159924, -0.08467507, 0.11939162, -0.01939453, -0.0058689644, -0.077881604, 0.118726775, 0.14489114, -0.10831982, -0.07972515) * g_26;
result += mat4(-0.16734359, 0.10685446, -0.102714166, -0.010225307, 0.07306756, 0.07014447, 0.040464073, 0.04688462, -0.05489714, -0.01525318, 0.14690581, 0.17514132, -0.03250648, -0.03688211, 0.05047889, 0.03078089) * g_27;
result += vec4(0.06614842, 0.045779686, 0.032838725, 0.017085627);
return result;
}
//!DESC Anime4K-v3.2-Upscale-Denoise-CNN-x2-(VL)-Depth-to-Space
//!HOOK MAIN
//!BIND MAIN
//!BIND conv2d_last_tf
//!BIND conv2d_last_tf1
//!BIND conv2d_last_tf2
//!SAVE MAIN
//!WIDTH conv2d_last_tf.w 2 *
//!HEIGHT conv2d_last_tf.h 2 *
//!WHEN OUTPUT.w MAIN.w / 1.200 > OUTPUT.h MAIN.h / 1.200 > *
vec4 hook() {
vec2 f0 = fract(conv2d_last_tf_pos * conv2d_last_tf_size);
ivec2 i0 = ivec2(f0 * vec2(2.0));
float c0 = conv2d_last_tf_tex((vec2(0.5) - f0) * conv2d_last_tf_pt + conv2d_last_tf_pos)[i0.y * 2 + i0.x];
vec2 f1 = fract(conv2d_last_tf1_pos * conv2d_last_tf1_size);
ivec2 i1 = ivec2(f1 * vec2(2.0));
float c1 = conv2d_last_tf1_tex((vec2(0.5) - f1) * conv2d_last_tf1_pt + conv2d_last_tf1_pos)[i1.y * 2 + i1.x];
vec2 f2 = fract(conv2d_last_tf2_pos * conv2d_last_tf2_size);
ivec2 i2 = ivec2(f2 * vec2(2.0));
float c2 = conv2d_last_tf2_tex((vec2(0.5) - f2) * conv2d_last_tf2_pt + conv2d_last_tf2_pos)[i2.y * 2 + i2.x];
float c3 = c2;
return vec4(c0, c1, c2, c3) + MAIN_tex(MAIN_pos);
}
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 bloc97
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+6 -16
View File
@@ -1,29 +1,19 @@
import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
import 'src/screens/connect_screen.dart';
import 'src/screens/session_screen.dart';
import 'src/screens/welcome_screen.dart';
import 'src/state/session_state.dart';
import 'src/screens/home/ui.dart';
import 'src/shell/app_shell.dart';
import 'src/widgets/app_keys.dart';
class WatchTogetherApp extends StatelessWidget {
const WatchTogetherApp({super.key});
@override
Widget build(BuildContext context) {
final session = SessionState.instance;
return MaterialApp(
title: 'Watch Together',
title: appName,
theme: ThemeData.dark(),
home: SignalBuilder(
builder: (context) {
return switch (session.route.value) {
AppRoute.welcome => const WelcomeScreen(),
AppRoute.connect => const ConnectScreen(),
AppRoute.session => const SessionScreen(),
};
},
),
shortcuts: appShortcuts,
home: const AppKeys(child: AppShell()),
);
}
}
+32 -11
View File
@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
@@ -7,19 +8,20 @@ import 'package:sync_relay_core/core.dart';
import 'package:window_manager/window_manager.dart';
import 'app.dart';
import 'src/playback/yt_dlp_app.dart';
import 'src/platform/desktop_entry.dart';
import 'src/screens/home/ui.dart';
import 'src/state/session_state.dart';
import 'src/update/updater.dart';
import 'src/state/settings.dart';
import 'src/update/update_controller.dart' show launchArgs;
Future<void> main(List<String> args) async {
launchArgs = List.unmodifiable(args);
if (args.contains('--upgrade')) {
exitCode = await runUpgrade();
exit(exitCode);
}
initLogging();
if (args.contains('--headless')) {
await runHeadless(args);
return;
}
FlutterError.onError = (details) {
debugPrint('FLUTTER ERROR: ${details.exceptionAsString()}\n${details.stack}');
FlutterError.presentError(details);
@@ -39,31 +41,50 @@ Future<void> main(List<String> args) async {
);
WidgetsFlutterBinding.ensureInitialized();
await windowManager.ensureInitialized();
await windowManager.setPreventClose(true);
windowManager.addListener(_CloseGuard());
const windowOptions = WindowOptions(
size: Size(1280, 720),
minimumSize: Size(160, 90),
center: true,
backgroundColor: Color(0xFF000000),
titleBarStyle: TitleBarStyle.hidden,
title: 'Watch Together',
title: appName,
);
windowManager.waitUntilReadyToShow(windowOptions, () async {
await windowManager.show();
await windowManager.focus();
});
await Hive.initFlutter();
await AppSettings.instance.ensureLoaded();
try {
await SessionState.instance.loadForm();
} catch (e, st) {
debugPrint('load connect form failed: $e\n$st');
}
MediaKit.ensureInitialized();
// yt-dlp for web page links: downloaded on first run, kept current.
AppYtDlp.instance.warmUp();
// Linux: the launcher entry and icon Wayland needs (release installs).
unawaited(installDesktopEntry());
runApp(const WatchTogetherApp());
}
Future<void> runHeadless(List<String> args) async {
final session = SessionState.instance;
await session.startHeadless(args);
await ProcessSignal.sigint.watch().first;
await session.stopServer();
/// Close button / Alt+F4: leave gracefully first (others see "left", a
/// hosted server shuts down), bounded so a hung socket can't keep the
/// window open.
class _CloseGuard with WindowListener {
bool _closing = false;
@override
Future<void> onWindowClose() async {
if (_closing) return;
_closing = true;
try {
await SessionState.instance.leave().timeout(const Duration(seconds: 2));
} catch (e) {
debugPrint('leave on close failed: $e');
}
await windowManager.destroy();
}
}
@@ -0,0 +1,58 @@
import 'dart:async';
import 'package:flutter/material.dart';
/// Follows whether media_kit's desktop controls around [child] are up,
/// by the same rule they use: up on mount and on any mouse movement,
/// down [hideAfter] later or when the mouse leaves. media_kit does not
/// expose its visibility; this lets the player draw a progress line while
/// they are down. Never takes input.
class ControlsHoverProbe extends StatefulWidget {
final ValueNotifier<bool> shown;
final Duration hideAfter;
final Widget child;
const ControlsHoverProbe({super.key, required this.shown, required this.hideAfter, required this.child});
@override
State<ControlsHoverProbe> createState() => _ControlsHoverProbeState();
}
class _ControlsHoverProbeState extends State<ControlsHoverProbe> {
Timer? _timer;
@override
void initState() {
super.initState();
// Not during this build: the line listening to [shown] may be built.
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) _wake();
});
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
void _wake() {
widget.shown.value = true;
_timer?.cancel();
_timer = Timer(widget.hideAfter, _hide);
}
void _hide() {
_timer?.cancel();
if (mounted) widget.shown.value = false;
}
@override
Widget build(BuildContext context) => MouseRegion(
opaque: false,
hitTestBehavior: HitTestBehavior.translucent,
onEnter: (_) => _wake(),
onHover: (_) => _wake(),
onExit: (_) => _hide(),
child: widget.child,
);
}
@@ -0,0 +1,407 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
import 'package:sync_relay_core/core.dart' show JellyfinStreamType;
import '../jellyfin/track_prefs.dart' show MpvTrack;
import '../playback/playback_controller.dart' show PlaybackController, RenderMode;
import '../shaders/shader_presets.dart';
import '../state/session_state.dart';
import '../shell/motion.dart';
import '../state/settings.dart';
import 'uosc/buttons.dart';
import 'uosc/controls.dart';
// Everything about the player, defined once: the rows of the ⚙ card in
// both control designs and of Settings → Player. Each row reads and
// writes [AppSettings] and applies to a running player right away.
/// One setting: a label, one line of help under it, the control at the
/// right ([below]: an extra line under the row, e.g. a text field).
class SettingRow extends StatelessWidget {
final String label;
final String? help;
final Widget control;
final Widget? below;
const SettingRow({super.key, required this.label, this.help, required this.control, this.below});
@override
Widget build(BuildContext context) {
final muted = Theme.of(context).colorScheme.onSurfaceVariant;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label),
if (help != null) Text(help!, style: TextStyle(color: muted, fontSize: 12)),
],
),
),
const SizedBox(width: 12),
control,
],
),
?below,
],
),
);
}
}
DropdownButton<T> _dropdown<T>({
required Key key,
required T value,
required List<(T, String)> items,
required ValueChanged<T> onChanged,
bool expanded = false,
}) =>
DropdownButton<T>(
key: key,
value: value,
isExpanded: expanded,
underline: const SizedBox.shrink(),
borderRadius: BorderRadius.circular(10),
items: [
for (final (v, label) in items)
DropdownMenuItem(value: v, child: Text(label, maxLines: 1, overflow: TextOverflow.ellipsis)),
],
onChanged: (v) {
if (v != null) onChanged(v);
},
);
/// Standard (our bars over media_kit's controls) or uosc. Live.
class PlayerDesignSetting extends StatelessWidget {
const PlayerDesignSetting({super.key});
@override
Widget build(BuildContext context) {
final s = AppSettings.instance;
return SignalBuilder(
builder: (context) => SettingRow(
label: 'Player design',
help: 'uosc is modeled on mpv: a thick timeline with chapters and what is cached',
control: _dropdown<bool>(
key: const ValueKey('player-design'),
value: s.mpvControls.value,
items: const [(false, 'Standard'), (true, 'uosc')],
onChanged: s.setMpvControls,
),
),
);
}
}
/// The 2 px line at the bottom while the controls are hidden. Live.
class ProgressLineSetting extends StatelessWidget {
const ProgressLineSetting({super.key});
@override
Widget build(BuildContext context) {
final s = AppSettings.instance;
return SignalBuilder(
builder: (context) => SettingRow(
label: 'Progress line when hidden',
help: 'A thin bar at the bottom of the video while the controls are hidden',
control: Switch(
key: const ValueKey('progress-line'),
value: s.progressLine.value,
onChanged: s.setProgressLine,
),
),
);
}
}
/// Saves a shader choice (also used on the next join) and puts it on the
/// running player, if any; the player announces the result.
void applyShaderChoice(ShaderPreset preset, ShaderQuality? quality) {
final s = AppSettings.instance;
if (quality != null) s.setShaderQuality(quality);
s.setShaderPreset(preset);
final coord = SessionState.instance.coordinator;
coord.markInput();
coord.playback.applyShaders(announce: true);
}
/// Anime4K preset (or custom files) and HQ / Fast. Live.
class ShaderPresetSetting extends StatelessWidget {
/// The running player's render mode (in the player); else the last one.
final ReadonlySignal<RenderMode>? renderMode;
const ShaderPresetSetting({super.key, this.renderMode});
@override
Widget build(BuildContext context) {
final s = AppSettings.instance;
return SignalBuilder(builder: (context) {
final preset = s.shaderPreset.value;
final quality = s.shaderQuality.value;
final hasCustom = s.customShaders.value.isNotEmpty;
final mode = (renderMode ?? PlaybackController.lastRenderMode).value;
final warning = switch (mode) {
RenderMode.software => 'Software rendering: shaders have no effect',
RenderMode.softwareGl => 'OpenGL on the CPU: shaders will be slow',
_ => null,
};
return SettingRow(
label: 'Upscaling shader',
help: 'Anime4K sharpens anime on your GPU, only you see it. '
'A for 1080p, B for 720p, C for 480p · Ctrl+1…6, Ctrl+0 off',
control: Row(
mainAxisSize: MainAxisSize.min,
children: [
_dropdown<ShaderPreset>(
key: const ValueKey('shader-preset'),
value: preset,
items: [
for (final p in ShaderPreset.values)
if (p != ShaderPreset.custom || hasCustom || preset == p) (p, p.label),
],
onChanged: (p) => applyShaderChoice(p, null),
),
if (preset.isAnime4k) ...[
const SizedBox(width: 8),
_dropdown<ShaderQuality>(
key: const ValueKey('shader-quality'),
value: quality,
items: [for (final q in ShaderQuality.values) (q, q.label)],
onChanged: (q) => applyShaderChoice(preset, q),
),
],
],
),
below: warning == null
? null
: Text(warning, style: const TextStyle(fontSize: 12, color: Colors.orange)),
);
});
}
}
/// Puts the hwdec setting on the running player (mpv switches decoders
/// on the fly). `SYNC_RELAY_HWDEC` wins, as at player start.
Future<void> applyHwdecLive() async {
if ((Platform.environment['SYNC_RELAY_HWDEC'] ?? '').isNotEmpty) return;
final playback = SessionState.instance.coordinator.playback;
if (!playback.alive.peek()) return;
try {
final dynamic platform = playback.player.platform;
await platform?.setProperty('hwdec', AppSettings.instance.hwdec.peek());
} catch (e) {
debugPrint('live hwdec failed: $e');
}
}
/// mpv's `hwdec`: auto, off, or a custom value. Live.
class HwdecSetting extends StatefulWidget {
const HwdecSetting({super.key});
@override
State<HwdecSetting> createState() => _HwdecSettingState();
}
class _HwdecSettingState extends State<HwdecSetting> {
final _s = AppSettings.instance;
late final _custom = TextEditingController(text: _s.hwdecCustom.peek());
@override
void dispose() {
_custom.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SignalBuilder(builder: (context) {
final mode = _s.hwdecMode.value;
return SettingRow(
label: 'Hardware decoding',
help: 'Decodes video on the GPU. Turn it off if the picture glitches or stays black',
control: _dropdown<HwdecMode>(
key: const ValueKey('hwdec'),
value: mode,
items: const [(HwdecMode.auto, 'Auto'), (HwdecMode.off, 'Off'), (HwdecMode.custom, 'Custom')],
onChanged: (m) {
_s.setHwdecMode(m);
applyHwdecLive();
},
),
below: mode != HwdecMode.custom
? null
: Padding(
padding: const EdgeInsets.only(top: 8),
child: TextField(
controller: _custom,
decoration: const InputDecoration(
isDense: true,
labelText: 'mpv hwdec value (Enter applies)',
hintText: 'e.g. vaapi, nvdec, d3d11va, auto-safe',
border: OutlineInputBorder(),
),
onChanged: _s.setHwdecCustom,
onSubmitted: (_) => applyHwdecLive(),
),
),
);
});
}
}
/// Audio or subtitle track of the running player (in the player only).
/// The pick is remembered for the next Jellyfin item.
class TrackSetting extends StatelessWidget {
final JellyfinStreamType type;
const TrackSetting({super.key, required this.type});
bool get _audio => type == JellyfinStreamType.audio;
@override
Widget build(BuildContext context) {
final coord = SessionState.instance.coordinator;
return SignalBuilder(builder: (context) {
final list = [
for (final t in coord.playback.tracks.value)
if (t.type == type) t,
];
// -1: subtitles off.
final selected = list.where((t) => t.selected).firstOrNull?.id ?? (_audio ? null : -1);
final items = <(int, String)>[
if (!_audio) (-1, 'Off'),
for (final MpvTrack t in list) (t.id, t.label),
];
return SettingRow(
label: _audio ? 'Audio' : 'Subtitles',
help: 'Remembered for the next episode',
control: SizedBox(
width: 190,
child: items.isEmpty || selected == null
? Text(items.isEmpty ? 'None' : 'Pick one…',
key: ValueKey(_audio ? 'audio-track' : 'subtitle-track'), textAlign: TextAlign.end)
: _dropdown<int>(
key: ValueKey(_audio ? 'audio-track' : 'subtitle-track'),
value: selected,
expanded: true,
items: items,
onChanged: (id) {
coord.markInput();
if (id == -1) {
coord.selectTrack(subtitlesOff: true);
} else if (_audio) {
coord.selectTrack(aid: id);
} else {
coord.selectTrack(sid: id);
}
},
),
),
);
});
}
}
/// The player rows of Settings → Player and the ⚙ card, in order.
List<Widget> playerSettingRows({ReadonlySignal<RenderMode>? renderMode}) => [
const PlayerDesignSetting(),
const ProgressLineSetting(),
ShaderPresetSetting(renderMode: renderMode),
const HwdecSetting(),
];
/// The ⚙ card: every player setting, plus the running player's tracks.
class PlayerSettingsCard extends StatelessWidget {
final ReadonlySignal<RenderMode>? renderMode;
/// Show the audio / subtitle pickers (a player is running).
final bool tracks;
const PlayerSettingsCard({super.key, this.renderMode, this.tracks = true});
@override
Widget build(BuildContext context) {
return Material(
key: const ValueKey('player-settings-card'),
elevation: 12,
borderRadius: BorderRadius.circular(16),
child: SizedBox(
width: 420,
child: Padding(
padding: const EdgeInsets.fromLTRB(18, 14, 18, 10),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text('Player', style: TextStyle(fontWeight: FontWeight.w700, fontSize: 16)),
const SizedBox(height: 4),
...playerSettingRows(renderMode: renderMode),
if (tracks) ...[
const Divider(),
const TrackSetting(type: JellyfinStreamType.subtitle),
const TrackSetting(type: JellyfinStreamType.audio),
],
const SizedBox(height: 4),
Text(
'Custom shader files: Settings → Player',
style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 12),
),
],
),
),
),
);
}
}
/// Opens the ⚙ card over the player, near the controls' right end. The
/// uosc controls stay up while it is open.
Future<void> showPlayerSettings(BuildContext context, {ReadonlySignal<RenderMode>? renderMode}) {
Future<void> open() => showPopover<void>(
context,
at: const Alignment(0.96, 0.5),
child: PlayerSettingsCard(renderMode: renderMode),
);
final controls = UoscControls.maybeOf(context);
return controls == null ? open() : controls.holdDuring(open);
}
/// ⚙ in the standard controls' bottom bar.
class PlayerSettingsButton extends StatelessWidget {
final FocusNode? focusNode;
final ReadonlySignal<RenderMode>? renderMode;
const PlayerSettingsButton({super.key, this.focusNode, this.renderMode});
@override
Widget build(BuildContext context) {
return IconButton(
key: const ValueKey('player-settings'),
focusNode: focusNode,
tooltip: 'Player settings',
color: Colors.white,
iconSize: 20,
visualDensity: VisualDensity.compact,
icon: const Icon(Icons.tune),
onPressed: () => showPlayerSettings(context, renderMode: renderMode),
);
}
}
/// ⚙ in the uosc controls' button row.
class UoscPlayerSettingsButton extends StatelessWidget {
final ReadonlySignal<RenderMode>? renderMode;
const UoscPlayerSettingsButton({super.key, this.renderMode});
@override
Widget build(BuildContext context) {
return UoscButton(
key: const ValueKey('uosc-player-settings'),
icon: Icons.tune_rounded,
tooltip: 'Player settings',
onPressed: () => showPlayerSettings(context, renderMode: renderMode),
);
}
}
@@ -0,0 +1,362 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
import 'package:sync_relay_core/core.dart' show JellyfinStreamType;
import '../../jellyfin/track_prefs.dart' show MpvTrack;
import '../../playback/media_marks.dart';
import '../../playback/playback_controller.dart' show RenderMode;
import '../../screens/session/player_controls.dart' show TrackPick;
import '../../shaders/shader_menu.dart';
import '../../shaders/shader_presets.dart';
import '../../state/settings.dart';
import '../volume.dart';
import 'controls.dart';
import 'menu.dart';
import 'style.dart';
/// Flat uosc button: a white icon (optionally a [label] beside it and a
/// small count [badge] in the corner), a faint highlight on hover, a dark
/// tooltip. Never takes focus (the session's keys are global).
class UoscButton extends StatefulWidget {
final IconData icon;
final String tooltip;
final VoidCallback? onPressed;
final String? label;
final int? badge;
/// Tint for an "on" state (ready, shaders on).
final Color? color;
const UoscButton({
super.key,
required this.icon,
required this.tooltip,
required this.onPressed,
this.label,
this.badge,
this.color,
});
@override
State<UoscButton> createState() => _UoscButtonState();
}
class _UoscButtonState extends State<UoscButton> {
bool _hover = false;
@override
Widget build(BuildContext context) {
final w = widget;
final color = w.onPressed == null ? UoscStyle.dim.withValues(alpha: 0.4) : (w.color ?? UoscStyle.text);
final badge = w.badge;
return UoscTooltip(
message: w.tooltip,
child: MouseRegion(
cursor: w.onPressed == null ? SystemMouseCursors.basic : SystemMouseCursors.click,
onEnter: (_) => setState(() => _hover = true),
onExit: (_) => setState(() => _hover = false),
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: w.onPressed,
child: Container(
height: UoscControls.buttonRowHeight,
constraints: const BoxConstraints(minWidth: UoscControls.buttonRowHeight),
padding: EdgeInsets.symmetric(horizontal: w.label == null ? 0 : 8),
decoration: BoxDecoration(
color: _hover && w.onPressed != null ? UoscStyle.hover : Colors.transparent,
borderRadius: BorderRadius.circular(UoscStyle.radius),
),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Stack(
clipBehavior: Clip.none,
children: [
Icon(w.icon, color: color, size: 22),
if (badge != null && badge > 0)
Positioned(
right: -8,
bottom: -5,
child: Container(
key: const ValueKey('uosc-badge'),
padding: const EdgeInsets.symmetric(horizontal: 3),
decoration: BoxDecoration(
color: UoscStyle.text,
borderRadius: BorderRadius.circular(3),
),
child: Text('$badge',
style: const TextStyle(
color: Colors.black, fontSize: 9, height: 1.3, fontWeight: FontWeight.w700)),
),
),
],
),
if (w.label != null) ...[
const SizedBox(width: 6),
Text(w.label!, style: TextStyle(color: color, fontSize: UoscStyle.fontSize)),
],
],
),
),
),
),
);
}
}
/// Badge-style text button ("HQ" in uosc): a small outlined box with a
/// short label, filled when [active].
class UoscBadgeButton extends StatefulWidget {
final String text;
final String tooltip;
final bool active;
final VoidCallback onPressed;
const UoscBadgeButton({
super.key,
required this.text,
required this.tooltip,
required this.active,
required this.onPressed,
});
@override
State<UoscBadgeButton> createState() => _UoscBadgeButtonState();
}
class _UoscBadgeButtonState extends State<UoscBadgeButton> {
bool _hover = false;
@override
Widget build(BuildContext context) {
final w = widget;
return UoscTooltip(
message: w.tooltip,
child: MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: (_) => setState(() => _hover = true),
onExit: (_) => setState(() => _hover = false),
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: w.onPressed,
child: Container(
height: UoscControls.buttonRowHeight,
padding: const EdgeInsets.symmetric(horizontal: 6),
decoration: BoxDecoration(
color: _hover ? UoscStyle.hover : Colors.transparent,
borderRadius: BorderRadius.circular(UoscStyle.radius),
),
alignment: Alignment.center,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 1),
decoration: BoxDecoration(
color: w.active ? UoscStyle.text : Colors.transparent,
border: Border.all(color: w.active ? UoscStyle.text : UoscStyle.dim, width: 1.4),
borderRadius: BorderRadius.circular(3),
),
child: Text(w.text,
style: TextStyle(
color: w.active ? Colors.black : UoscStyle.dim,
fontSize: 11,
fontWeight: FontWeight.w800,
letterSpacing: 0.3)),
),
),
),
),
);
}
}
/// Volume: click mutes / unmutes, hovering shows a [VolumeBar] up to
/// [maxVolume], the wheel steps 5 %.
class UoscVolumeButton extends StatefulWidget {
/// 0..[maxVolume].
final double volume;
final ValueChanged<double> onChanged;
const UoscVolumeButton({super.key, required this.volume, required this.onChanged});
@override
State<UoscVolumeButton> createState() => _UoscVolumeButtonState();
}
class _UoscVolumeButtonState extends State<UoscVolumeButton> {
double _beforeMute = 100;
bool _hover = false;
@override
Widget build(BuildContext context) {
final v = widget.volume;
final icon = v <= 0 ? Icons.volume_off_rounded : (v < 50 ? Icons.volume_down_rounded : Icons.volume_up_rounded);
return MouseRegion(
onEnter: (_) => setState(() => _hover = true),
onExit: (_) => setState(() => _hover = false),
child: Listener(
onPointerSignal: (e) {
if (e is PointerScrollEvent && e.scrollDelta.dy != 0) {
widget.onChanged(stepVolume(v, e.scrollDelta.dy < 0 ? 5 : -5));
}
},
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedSize(
duration: const Duration(milliseconds: 150),
child: _hover
? Padding(
padding: const EdgeInsets.only(left: 6, right: 2),
child: VolumeBar(
key: const ValueKey('uosc-volume-bar'),
volume: v,
onChanged: widget.onChanged,
color: UoscStyle.text,
trackColor: UoscStyle.dim.withValues(alpha: 0.5),
),
)
: const SizedBox.shrink(),
),
UoscButton(
key: const ValueKey('uosc-volume'),
icon: icon,
color: v > 100 ? boostColor : null,
tooltip: 'Volume ${v.round()} % (scroll to change)',
onPressed: () {
if (v > 0) {
_beforeMute = v;
widget.onChanged(0);
} else {
widget.onChanged(_beforeMute <= 0 ? 100 : _beforeMute);
}
},
),
],
),
),
);
}
}
/// Subtitles or audio: the icon with the track count; the menu lists the
/// tracks (mpv's selection active).
class UoscTracksButton extends StatelessWidget {
final JellyfinStreamType type;
final List<MpvTrack> tracks;
final void Function(TrackPick pick) onPick;
final ValueChanged<bool>? onMenuOpen;
const UoscTracksButton({super.key, required this.type, required this.tracks, required this.onPick, this.onMenuOpen});
@override
Widget build(BuildContext context) {
final audio = type == JellyfinStreamType.audio;
final count = tracks.where((t) => t.type == type).length;
return UoscButton(
key: ValueKey(audio ? 'uosc-audio' : 'uosc-subtitles'),
icon: audio ? Icons.graphic_eq_rounded : Icons.subtitles_outlined,
tooltip: audio ? 'Audio' : 'Subtitles',
badge: count,
onPressed: () async {
onMenuOpen?.call(true);
final pick = await showUoscMenu<TrackPick>(context,
title: audio ? 'Audio' : 'Subtitles', items: trackMenuItems(type, tracks));
onMenuOpen?.call(false);
if (pick != null) onPick(pick);
},
);
}
}
/// Chapters: the list with start times, the playing one active; a pick
/// jumps there. Nothing without chapters.
class UoscChaptersButton extends StatelessWidget {
final List<MediaChapter> chapters;
final int? current;
final Duration duration;
final ValueChanged<MediaChapter> onPick;
final ValueChanged<bool>? onMenuOpen;
const UoscChaptersButton({
super.key,
required this.chapters,
required this.current,
required this.duration,
required this.onPick,
this.onMenuOpen,
});
@override
Widget build(BuildContext context) {
if (chapters.isEmpty) return const SizedBox.shrink();
return UoscButton(
key: const ValueKey('uosc-chapters'),
icon: Icons.bookmarks_outlined,
tooltip: 'Chapters (PgUp / PgDn)',
onPressed: () async {
onMenuOpen?.call(true);
final pick = await showUoscMenu<MediaChapter>(context,
title: 'Chapters', items: chapterMenuItems(chapters, current, duration));
onMenuOpen?.call(false);
if (pick != null) onPick(pick);
},
);
}
}
/// Shaders as uosc's quality badge: the active Anime4K preset ("A HQ"),
/// "4K" dimmed when off. Opens the shader menu.
class UoscShaderButton extends StatelessWidget {
final ReadonlySignal<RenderMode> renderMode;
final void Function(ShaderPreset preset, ShaderQuality? quality) onPick;
final ValueChanged<bool>? onMenuOpen;
const UoscShaderButton({super.key, required this.renderMode, required this.onPick, this.onMenuOpen});
@override
Widget build(BuildContext context) {
final s = AppSettings.instance;
return SignalBuilder(builder: (context) {
final preset = s.shaderPreset.value;
final quality = s.shaderQuality.value;
final short = shaderShortLabel(preset, quality);
return Builder(
builder: (context) => UoscBadgeButton(
key: const ValueKey('uosc-shaders'),
text: short ?? '4K',
active: short != null,
tooltip: short == null
? 'Shaders: off (Ctrl+1..6)'
: 'Shaders: ${shaderLabel(preset, quality)} (Ctrl+0 off)',
onPressed: () {
final controls = UoscControls.maybeOf(context);
Future<void> open() =>
showShaderMenu(context, renderMode: renderMode, onPick: onPick, onMenuOpen: onMenuOpen);
controls == null ? open() : controls.holdDuring(open);
},
),
);
});
}
}
/// One entry of the ≡ menu.
typedef UoscAction = ({String title, String? hint, VoidCallback run});
/// ≡: the session's actions as a menu.
class UoscMainMenuButton extends StatelessWidget {
final List<UoscAction> actions;
final ValueChanged<bool>? onMenuOpen;
const UoscMainMenuButton({super.key, required this.actions, this.onMenuOpen});
@override
Widget build(BuildContext context) {
return UoscButton(
key: const ValueKey('uosc-menu-button'),
icon: Icons.menu_rounded,
tooltip: 'Menu',
onPressed: () async {
onMenuOpen?.call(true);
final pick = await showUoscMenu<UoscAction>(context, items: [
for (final a in actions) UoscMenuItem(value: a, title: a.title, hint: a.hint),
]);
onMenuOpen?.call(false);
pick?.run();
},
);
}
}
@@ -0,0 +1,265 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
import '../../state/settings.dart';
// The mpv-style (uosc) controls frame: a full-width timeline at the
// bottom with a row of flat buttons above it, the title top left and a
// corner widget top right.
// Mouse movement shows them; after [UoscControls.hideAfter] without any
// they fade out, the timeline shrinks to a 2 px line (gone when "Progress
// line when hidden" is off) and the cursor hides. Our own frame: no
// media_kit controls state to reach into.
class UoscControls extends StatefulWidget {
/// The timeline; [expanded] is false while the controls are hidden.
final Widget Function(BuildContext context, bool expanded) timeline;
/// Buttons left and right of the row above the timeline.
final List<Widget> left;
final List<Widget> right;
/// Top-right corner (chat, back to the library), shown with the controls.
final Widget? top;
/// Top left (what is playing), shown with the controls; gets the width
/// [top] leaves.
final Widget? topLeft;
final VoidCallback? onDoubleTap;
final Duration hideAfter;
/// Follows whether the controls are up (for overlays outside them).
final ValueNotifier<bool>? shown;
const UoscControls({
super.key,
required this.timeline,
this.left = const [],
this.right = const [],
this.top,
this.topLeft,
this.onDoubleTap,
this.hideAfter = const Duration(seconds: 3),
this.shown,
});
static const buttonRowHeight = 40.0;
/// The controls around [context], for widgets that need them kept up
/// (a timeline drag, an open menu).
static UoscControlsState? maybeOf(BuildContext context) =>
context.findAncestorStateOfType<UoscControlsState>();
@override
State<UoscControls> createState() => UoscControlsState();
}
class UoscControlsState extends State<UoscControls> {
bool _visible = true;
int _holds = 0;
Timer? _timer;
bool get visible => _visible;
@override
void initState() {
super.initState();
// Not during this build: an overlay listening to [shown] may be built.
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) widget.shown?.value = true;
});
_hideLater();
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
/// Show the controls and restart the hide countdown.
void wake() {
if (!_visible && mounted) {
setState(() => _visible = true);
widget.shown?.value = true;
}
_hideLater();
}
/// Keep the controls up while [on] (paired calls): a drag sends no
/// hover events and a menu route takes the mouse.
void hold(bool on) {
_holds = on ? _holds + 1 : (_holds - 1).clamp(0, 1 << 20);
wake();
}
/// Runs [open] (a menu) with the controls held up.
Future<T> holdDuring<T>(Future<T> Function() open) async {
hold(true);
try {
return await open();
} finally {
if (mounted) hold(false);
}
}
void _hideLater() {
_timer?.cancel();
_timer = Timer(widget.hideAfter, _hide);
}
void _hide() {
if (!mounted || _holds > 0 || !_visible) return;
setState(() => _visible = false);
widget.shown?.value = false;
}
@override
Widget build(BuildContext context) {
final visible = _visible;
const fade = Duration(milliseconds: 150);
return MouseRegion(
cursor: visible ? SystemMouseCursors.basic : SystemMouseCursors.none,
onEnter: (_) => wake(),
onHover: (_) => wake(),
onExit: (_) {
_timer?.cancel();
_hide();
},
child: Stack(
children: [
Positioned.fill(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onDoubleTap: widget.onDoubleTap,
),
),
Positioned(
left: 0,
right: 0,
bottom: 0,
height: 160,
child: IgnorePointer(
child: AnimatedOpacity(
duration: fade,
opacity: visible ? 1 : 0,
child: const DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0x00000000), Color(0x8C000000)],
),
),
),
),
),
),
if (widget.topLeft != null)
Positioned(
left: 0,
right: 0,
top: 0,
height: 120,
child: IgnorePointer(
child: AnimatedOpacity(
duration: fade,
opacity: visible ? 1 : 0,
child: const DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0x8C000000), Color(0x00000000)],
),
),
),
),
),
),
if (widget.top != null || widget.topLeft != null)
Positioned(
top: 0,
left: 0,
right: 0,
child: IgnorePointer(
key: const ValueKey('uosc-top'),
ignoring: !visible,
child: AnimatedOpacity(
duration: fade,
opacity: visible ? 1 : 0,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 0),
child: widget.topLeft == null
? const SizedBox.shrink()
: Align(alignment: AlignmentDirectional.centerStart, child: widget.topLeft),
),
),
?widget.top,
],
),
),
),
),
Positioned(
left: 0,
right: 0,
bottom: 0,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
IgnorePointer(
key: const ValueKey('uosc-buttons'),
ignoring: !visible,
child: AnimatedOpacity(
duration: fade,
opacity: visible ? 1 : 0,
child: Padding(
padding: const EdgeInsets.fromLTRB(6, 0, 6, 4),
child: SizedBox(
height: UoscControls.buttonRowHeight,
child: Row(children: [...widget.left, const Spacer(), ...widget.right]),
),
),
),
),
// Hidden: the collapsed timeline is the progress line,
// unless the player settings turn it off.
SignalBuilder(
builder: (context) {
final line = AppSettings.instance.progressLine.value;
return AnimatedOpacity(
key: const ValueKey('uosc-timeline-wrap'),
duration: fade,
opacity: visible || line ? 1 : 0,
child: widget.timeline(context, visible),
);
},
),
],
),
),
],
),
);
}
}
/// The session's controls: the standard ones ([material]) or, with the
/// "Player design" setting on uosc, the uosc ones ([mpv]). Switches live
/// when the setting changes.
class SessionControlsSwitch extends StatelessWidget {
final WidgetBuilder material;
final WidgetBuilder mpv;
const SessionControlsSwitch({super.key, required this.material, required this.mpv});
@override
Widget build(BuildContext context) {
return SignalBuilder(
builder: (context) =>
AppSettings.instance.mpvControls.value ? mpv(context) : material(context),
);
}
}
@@ -0,0 +1,249 @@
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:sync_relay_core/core.dart' show JellyfinStreamType;
import '../../jellyfin/track_prefs.dart' show MpvTrack;
import '../../playback/media_marks.dart';
import '../../screens/session/player_controls.dart' show TrackPick;
import 'controls.dart';
import 'style.dart';
/// One row of a uosc menu: [title], a dim [hint] on the right (a time, a
/// key), [active] = the current one (highlighted).
class UoscMenuItem<T> {
final T value;
final String title;
final String? hint;
final bool active;
final bool enabled;
const UoscMenuItem({
required this.value,
required this.title,
this.hint,
this.active = false,
this.enabled = true,
});
}
/// A flat dark menu over the controls, above the widget of [context] (the
/// button that opens it), with an optional [title] row. Resolves to the
/// picked value, or null when dismissed (click outside, Esc). Keeps the
/// controls up while open.
Future<T?> showUoscMenu<T>(
BuildContext context, {
String? title,
required List<UoscMenuItem<T>> items,
}) {
final navigator = Navigator.of(context);
final overlay = navigator.overlay!.context.findRenderObject() as RenderBox;
final box = context.findRenderObject() as RenderBox;
final anchor = Rect.fromPoints(
box.localToGlobal(Offset.zero, ancestor: overlay),
box.localToGlobal(box.size.bottomRight(Offset.zero), ancestor: overlay),
);
Future<T?> open() => navigator.push<T>(_UoscMenuRoute<T>(anchor: anchor, title: title, items: items));
final controls = UoscControls.maybeOf(context);
return controls == null ? open() : controls.holdDuring(open);
}
class _UoscMenuRoute<T> extends PopupRoute<T> {
final Rect anchor;
final String? title;
final List<UoscMenuItem<T>> items;
_UoscMenuRoute({required this.anchor, required this.title, required this.items});
@override
Color? get barrierColor => null;
@override
bool get barrierDismissible => true;
@override
String? get barrierLabel => 'Close menu';
@override
Duration get transitionDuration => Duration.zero;
@override
Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return LayoutBuilder(builder: (context, constraints) {
const width = 360.0;
final screen = constraints.biggest;
final left = anchor.left.clamp(8.0, math.max(8.0, screen.width - width - 8));
final maxHeight = math.max(120.0, anchor.top - 16);
return Stack(
children: [
Positioned(
left: left.toDouble(),
bottom: screen.height - anchor.top + 6,
width: math.min(width, screen.width - 16),
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: maxHeight),
child: UoscMenuPanel<T>(
title: title,
items: items,
onPick: (v) => Navigator.of(context).pop(v),
),
),
),
],
);
});
}
}
/// The menu's panel alone (tests pump it without a route).
class UoscMenuPanel<T> extends StatefulWidget {
final String? title;
final List<UoscMenuItem<T>> items;
final ValueChanged<T> onPick;
const UoscMenuPanel({super.key, this.title, required this.items, required this.onPick});
static const rowHeight = 36.0;
@override
State<UoscMenuPanel<T>> createState() => _UoscMenuPanelState<T>();
}
class _UoscMenuPanelState<T> extends State<UoscMenuPanel<T>> {
// Long lists open on the current row.
late final _scroll = ScrollController(
initialScrollOffset: math.max(0, widget.items.indexWhere((i) => i.active) - 3) * UoscMenuPanel.rowHeight);
@override
void dispose() {
_scroll.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final title = widget.title;
return Material(
key: const ValueKey('uosc-menu'),
color: UoscStyle.panel,
borderRadius: BorderRadius.circular(UoscStyle.radius * 2),
clipBehavior: Clip.antiAlias,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (title != null)
Container(
height: 34,
alignment: Alignment.center,
decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Color(0x22FFFFFF)))),
child: Text(title,
style: const TextStyle(color: UoscStyle.dim, fontSize: UoscStyle.fontSize, fontWeight: FontWeight.w600)),
),
Flexible(
child: ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(4),
controller: _scroll,
itemCount: widget.items.length,
itemExtent: UoscMenuPanel.rowHeight,
itemBuilder: (context, i) => _Row<T>(item: widget.items[i], onPick: widget.onPick),
),
),
],
),
);
}
}
class _Row<T> extends StatefulWidget {
final UoscMenuItem<T> item;
final ValueChanged<T> onPick;
const _Row({super.key, required this.item, required this.onPick});
@override
State<_Row<T>> createState() => _RowState<T>();
}
class _RowState<T> extends State<_Row<T>> {
bool _hover = false;
@override
Widget build(BuildContext context) {
final item = widget.item;
final color = item.active
? UoscStyle.fill
: _hover && item.enabled
? UoscStyle.hover
: Colors.transparent;
final textColor = item.enabled ? UoscStyle.text : UoscStyle.dim.withValues(alpha: 0.4);
return MouseRegion(
cursor: item.enabled ? SystemMouseCursors.click : SystemMouseCursors.basic,
onEnter: (_) => setState(() => _hover = true),
onExit: (_) => setState(() => _hover = false),
child: GestureDetector(
key: ValueKey('uosc-menu-item-${item.title}'),
behavior: HitTestBehavior.opaque,
onTap: item.enabled ? () => widget.onPick(item.value) : null,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(UoscStyle.radius)),
child: Row(
children: [
Expanded(
child: Text(item.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: textColor,
fontSize: UoscStyle.fontSize,
fontWeight: item.active ? FontWeight.w600 : FontWeight.normal)),
),
if (item.hint != null) ...[
const SizedBox(width: 12),
Text(item.hint!,
style: TextStyle(
color: item.active ? UoscStyle.text : UoscStyle.dim,
fontSize: 12,
fontFeatures: const [FontFeature.tabularFigures()])),
],
],
),
),
),
);
}
}
/// Chapters with their start times, the playing one active.
List<UoscMenuItem<MediaChapter>> chapterMenuItems(List<MediaChapter> chapters, int? current, Duration duration) {
final withHours = duration >= const Duration(hours: 1);
return [
for (final c in chapters)
UoscMenuItem(
value: c,
title: c.title.isEmpty ? 'Chapter ${c.index + 1}' : c.title,
hint: formatUoscTime(c.start, withHours: withHours),
active: c.index == current,
),
];
}
/// The audio or subtitle tracks ("Japanese · E-AC3 2.0 · title"), mpv's
/// selection active; subtitles start with Off.
List<UoscMenuItem<TrackPick>> trackMenuItems(JellyfinStreamType type, List<MpvTrack> tracks) {
final audio = type == JellyfinStreamType.audio;
final list = tracks.where((t) => t.type == type).toList();
return [
if (!audio)
UoscMenuItem(
value: (aid: null, sid: null, subtitlesOff: true),
title: 'Off',
active: !list.any((t) => t.selected),
),
for (final t in list)
UoscMenuItem(
value: audio ? (aid: t.id, sid: null, subtitlesOff: false) : (aid: null, sid: t.id, subtitlesOff: false),
title: t.label,
hint: '#${t.id}',
active: t.selected,
),
];
}
@@ -0,0 +1,79 @@
import 'package:flutter/material.dart';
// Look of the experimental mpv-style controls, after the uosc theme as the
// owner runs it: flat black panels, white icons, a muted blue fill.
abstract final class UoscStyle {
/// Timeline background and the boxes on it.
static const bar = Color(0xCC000000);
/// Played part of the timeline; also the active menu row.
static const fill = Color(0xFF4C6A8E);
/// Menus and tooltips.
static const panel = Color(0xF0101010);
static const hover = Color(0x24FFFFFF);
static const text = Colors.white;
static const dim = Color(0xA6FFFFFF);
static const radius = 4.0;
static const fontSize = 13.0;
}
/// uosc's clock: "01:51", "1:02:03" style but zero-padded ("01:02:03")
/// once [withHours] (the media is an hour or longer).
String formatUoscTime(Duration d, {bool withHours = false}) {
final t = d.isNegative ? Duration.zero : d;
String two(int n) => n.toString().padLeft(2, '0');
final h = t.inHours, m = t.inMinutes % 60, s = t.inSeconds % 60;
return withHours || h > 0 ? '${two(h)}:${two(m)}:${two(s)}' : '${two(m)}:${two(s)}';
}
/// Dark rounded hover tip ("Subtitles"), shown above its child.
class UoscTooltip extends StatelessWidget {
final String message;
final Widget child;
const UoscTooltip({super.key, required this.message, required this.child});
@override
Widget build(BuildContext context) {
if (message.isEmpty) return child;
return Tooltip(
message: message,
preferBelow: false,
verticalOffset: 26,
waitDuration: const Duration(milliseconds: 250),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: UoscStyle.panel,
borderRadius: BorderRadius.circular(UoscStyle.radius),
),
textStyle: const TextStyle(color: UoscStyle.text, fontSize: 12),
child: child,
);
}
}
/// A small dark box with one line of text (the timeline's times, the
/// hover chapter title).
class UoscBox extends StatelessWidget {
final String text;
final Color color;
final TextStyle style;
const UoscBox(
this.text, {
super.key,
this.color = UoscStyle.panel,
this.style = const TextStyle(color: UoscStyle.text, fontSize: UoscStyle.fontSize),
});
@override
Widget build(BuildContext context) {
return Container(
constraints: const BoxConstraints(maxWidth: 360),
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3),
decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(UoscStyle.radius)),
child: Text(text,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: style.copyWith(fontFeatures: const [FontFeature.tabularFigures()])),
);
}
}
@@ -0,0 +1,287 @@
import 'dart:math' as math;
import 'package:flutter/material.dart';
import '../../playback/media_marks.dart';
import 'controls.dart';
import 'style.dart';
/// Fraction of [total] at [d], 0..1 (0 without a duration).
double timelineFraction(Duration d, Duration total) {
final t = total.inMicroseconds;
if (t <= 0) return 0;
return (d.inMicroseconds / t).clamp(0.0, 1.0);
}
/// The cached-ahead part of a timeline of [size]: from the playing
/// position to the end of mpv's cache. Null when nothing is cached ahead.
Rect? cacheRect(Size size, {required Duration position, required Duration buffered, required Duration duration}) {
final from = timelineFraction(position, duration) * size.width;
final to = timelineFraction(buffered, duration) * size.width;
if (to - from < 0.5) return null;
return Rect.fromLTRB(from, 0, to, size.height);
}
/// The hatching of [r]: 45° strokes from bottom-left to top-right,
/// [spacing] apart along x, across all of [r] (the painter clips them).
/// The first starts half a step in, so none just grazes the corner.
List<(Offset, Offset)> hatchLines(Rect r, {double spacing = 6}) {
if (r.isEmpty || spacing <= 0) return const [];
final h = r.height;
return [
for (var x = r.left - h + spacing / 2; x < r.right; x += spacing) (Offset(x, r.bottom), Offset(x + h, r.top)),
];
}
/// The uosc timeline: full width, elapsed part filled, the cached range
/// ahead hatched, chapter starts as white diamonds on the top edge, the
/// current time in a box at the left and the duration at the right.
/// Hover shows the chapter title over the hover time. When the controls
/// hide ([expanded] false) it shrinks to a thin line with chapter ticks.
/// Like the media_kit bar it reports a seek on every drag move and on
/// release ([onSeek]); the session sends those through the user-seek path.
class UoscTimeline extends StatefulWidget {
final Duration position;
final Duration duration;
/// End of the cached range ([PlaybackController.buffered]).
final Duration buffered;
final List<MediaChapter> chapters;
final bool expanded;
final ValueChanged<Duration> onSeek;
const UoscTimeline({
super.key,
required this.position,
required this.duration,
this.buffered = Duration.zero,
this.chapters = const [],
this.expanded = true,
required this.onSeek,
});
static const height = 30.0;
static const collapsedHeight = 2.0;
@override
State<UoscTimeline> createState() => _UoscTimelineState();
}
class _UoscTimelineState extends State<UoscTimeline> {
double? _hoverX;
bool _dragging = false;
double _dragFraction = 0;
Duration _at(double fraction) => widget.duration * fraction.clamp(0.0, 1.0);
void _seekTo(double x, double width) {
if (widget.duration <= Duration.zero || width <= 0) return;
_dragFraction = (x / width).clamp(0.0, 1.0);
widget.onSeek(_at(_dragFraction));
}
void _drag(bool on) {
if (_dragging == on) return;
setState(() => _dragging = on);
UoscControls.maybeOf(context)?.hold(on);
}
@override
void didUpdateWidget(UoscTimeline old) {
super.didUpdateWidget(old);
if (!widget.expanded && old.expanded) _hoverX = null;
}
@override
void dispose() {
// A drag cut short by a rebuild must not keep the controls up.
if (_dragging) UoscControls.maybeOf(context)?.hold(false);
super.dispose();
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
final width = constraints.maxWidth;
final expanded = widget.expanded;
final h = expanded ? UoscTimeline.height : UoscTimeline.collapsedHeight;
final played = _dragging ? _dragFraction : timelineFraction(widget.position, widget.duration);
final hoverX = expanded ? _hoverX : null;
final hoverAt = hoverX == null || width <= 0 ? null : _at(hoverX / width);
final hoverChapter = hoverAt == null ? null : chapterIndexAt(widget.chapters, hoverAt);
final hoverTitle = hoverChapter == null ? '' : widget.chapters[hoverChapter].title;
final withHours = widget.duration >= const Duration(hours: 1);
final marks = [
for (final c in widget.chapters)
if (c.start > Duration.zero && widget.duration > Duration.zero) c,
];
final bar = SizedBox(
key: const ValueKey('uosc-timeline'),
width: width,
height: h,
child: Stack(
clipBehavior: Clip.none,
children: [
Positioned.fill(
child: CustomPaint(
painter: UoscTimelinePainter(
played: played,
cache: (size) => cacheRect(size,
position: widget.position, buffered: widget.buffered, duration: widget.duration),
cacheKey: (widget.position, widget.buffered, widget.duration),
hoverX: _dragging ? null : hoverX,
hatched: expanded,
),
),
),
for (final c in marks)
if (expanded)
Positioned(
key: ValueKey('uosc-chapter-${c.index}'),
left: width * timelineFraction(c.start, widget.duration) - 4,
top: -4,
width: 8,
height: 8,
child: IgnorePointer(
child: Transform.rotate(
angle: math.pi / 4,
child: Container(
margin: const EdgeInsets.all(1.2),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.black54, width: 0.6),
),
),
),
),
)
else
Positioned(
key: ValueKey('uosc-chapter-tick-${c.index}'),
left: width * timelineFraction(c.start, widget.duration) - 1,
top: -2,
width: 2,
height: h + 2,
child: const IgnorePointer(child: ColoredBox(color: Colors.white70)),
),
if (expanded) ...[
Positioned(
left: 4,
top: 0,
bottom: 0,
child: IgnorePointer(
child: Center(
child: UoscBox(formatUoscTime(_at(played), withHours: withHours),
key: const ValueKey('uosc-time'), color: UoscStyle.bar),
),
),
),
Positioned(
right: 4,
top: 0,
bottom: 0,
child: IgnorePointer(
child: Center(
child: UoscBox(formatUoscTime(widget.duration, withHours: withHours),
key: const ValueKey('uosc-duration'), color: UoscStyle.bar),
),
),
),
],
if (hoverAt != null)
Positioned(
// Kept clear of the window edges (the tip is centred).
left: hoverX!.clamp(math.min(60.0, width / 2), math.max(width - 60, width / 2)),
bottom: h + 8,
child: IgnorePointer(
child: FractionalTranslation(
translation: const Offset(-0.5, 0),
child: Column(
mainAxisSize: MainAxisSize.min,
spacing: 4,
children: [
if (hoverTitle.isNotEmpty) UoscBox(hoverTitle, key: const ValueKey('uosc-hover-chapter')),
UoscBox(formatUoscTime(hoverAt, withHours: withHours),
key: const ValueKey('uosc-hover-time')),
],
),
),
),
),
],
),
);
if (!expanded) return IgnorePointer(child: bar);
return MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: (e) => setState(() => _hoverX = e.localPosition.dx.clamp(0, width)),
onHover: (e) => setState(() => _hoverX = e.localPosition.dx.clamp(0, width)),
onExit: (_) => setState(() => _hoverX = null),
child: Listener(
behavior: HitTestBehavior.opaque,
onPointerDown: (e) {
_dragFraction = width <= 0 ? 0 : (e.localPosition.dx / width).clamp(0.0, 1.0);
_drag(true);
},
onPointerMove: (e) {
setState(() => _hoverX = e.localPosition.dx.clamp(0, width));
_seekTo(e.localPosition.dx, width);
},
onPointerUp: (e) {
_seekTo(e.localPosition.dx, width);
_drag(false);
},
onPointerCancel: (_) => _drag(false),
child: bar,
),
);
});
}
}
/// Background, cached range (hatched, or a faint band when collapsed),
/// the played part and the hover line.
class UoscTimelinePainter extends CustomPainter {
final double played;
final Rect? Function(Size size) cache;
/// What [cache] depends on (repaint when it changes).
final Object cacheKey;
final double? hoverX;
final bool hatched;
const UoscTimelinePainter({
required this.played,
required this.cache,
required this.cacheKey,
this.hoverX,
this.hatched = true,
});
@override
void paint(Canvas canvas, Size size) {
final all = Offset.zero & size;
canvas.drawRect(all, Paint()..color = UoscStyle.bar);
final c = cache(size);
if (c != null) {
if (hatched) {
canvas.save();
canvas.clipRect(c);
final stroke = Paint()
..color = const Color(0x40FFFFFF)
..strokeWidth = 1.5;
for (final (a, b) in hatchLines(c)) {
canvas.drawLine(a, b, stroke);
}
canvas.restore();
} else {
canvas.drawRect(c, Paint()..color = const Color(0x40FFFFFF));
}
}
canvas.drawRect(Rect.fromLTWH(0, 0, size.width * played, size.height), Paint()..color = UoscStyle.fill);
final x = hoverX;
if (x != null) {
canvas.drawRect(Rect.fromLTWH(x - 0.5, 0, 1, size.height), Paint()..color = Colors.white70);
}
}
@override
bool shouldRepaint(UoscTimelinePainter old) =>
old.played != played || old.cacheKey != cacheKey || old.hoverX != hoverX || old.hatched != hatched;
}
+146
View File
@@ -0,0 +1,146 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:media_kit/media_kit.dart';
import '../playback/playback_controller.dart' show PlaybackController;
/// The player's top volume (mpv's `volume-max`): past 100 % mpv amplifies.
const maxVolume = PlaybackController.maxVolume * 1.0;
/// The part of the volume bar above 100 %.
const boostColor = Color(0xFFFFB74D);
/// [volume] moved by [delta] (wheel, keys), kept within 0..[maxVolume].
double stepVolume(double volume, double delta) => (volume + delta).clamp(0.0, maxVolume);
/// A thin volume track over 0..[maxVolume]: filled up to the volume, the
/// part above 100 % in [boostColor], a tick at 100 %. Click or drag sets
/// it.
class VolumeBar extends StatelessWidget {
final double volume;
final ValueChanged<double> onChanged;
final double width;
final Color color;
final Color trackColor;
const VolumeBar({
super.key,
required this.volume,
required this.onChanged,
this.width = 64,
this.color = Colors.white,
this.trackColor = Colors.white30,
});
@override
Widget build(BuildContext context) {
void at(double x) => onChanged((x / width * maxVolume).clamp(0.0, maxVolume).roundToDouble());
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTapDown: (e) => at(e.localPosition.dx),
onHorizontalDragUpdate: (e) => at(e.localPosition.dx),
child: SizedBox(
width: width,
height: 24,
child: CustomPaint(painter: _VolumePainter(volume.clamp(0.0, maxVolume), color, trackColor)),
),
);
}
}
class _VolumePainter extends CustomPainter {
final double volume;
final Color color, trackColor;
_VolumePainter(this.volume, this.color, this.trackColor);
@override
void paint(Canvas canvas, Size size) {
final y = size.height / 2;
double x(double v) => size.width * v / maxVolume;
final line = Paint()
..strokeWidth = 3
..strokeCap = StrokeCap.round;
canvas.drawLine(Offset(0, y), Offset(size.width, y), line..color = trackColor);
final full = x(100);
canvas.drawLine(Offset(full, y - 5), Offset(full, y + 5), Paint()
..color = trackColor
..strokeWidth = 1.5);
if (volume > 0) canvas.drawLine(Offset(0, y), Offset(x(volume.clamp(0, 100)), y), line..color = color);
if (volume > 100) canvas.drawLine(Offset(full, y), Offset(x(volume), y), line..color = boostColor);
canvas.drawCircle(Offset(x(volume), y), 5, Paint()..color = volume > 100 ? boostColor : color);
}
@override
bool shouldRepaint(_VolumePainter old) =>
old.volume != volume || old.color != color || old.trackColor != trackColor;
}
/// Volume for the standard bar (instead of media_kit's, which stops at
/// 100): the icon mutes / unmutes, hovering shows a [VolumeBar], the
/// wheel steps 5 %.
class VolumeButton extends StatefulWidget {
final Player player;
final FocusNode? focusNode;
const VolumeButton({super.key, required this.player, this.focusNode});
@override
State<VolumeButton> createState() => _VolumeButtonState();
}
class _VolumeButtonState extends State<VolumeButton> {
bool _hover = false;
double _beforeMute = 100;
@override
Widget build(BuildContext context) {
final player = widget.player;
return StreamBuilder<double>(
stream: player.stream.volume,
initialData: player.state.volume,
builder: (context, snap) {
final v = snap.data ?? 100;
return MouseRegion(
onEnter: (_) => setState(() => _hover = true),
onExit: (_) => setState(() => _hover = false),
child: Listener(
onPointerSignal: (e) {
if (e is PointerScrollEvent && e.scrollDelta.dy != 0) {
player.setVolume(stepVolume(v, e.scrollDelta.dy < 0 ? 5 : -5));
}
},
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(width: 4),
IconButton(
key: const ValueKey('volume-button'),
focusNode: widget.focusNode,
tooltip: 'Volume ${v.round()} %',
iconSize: 22,
color: Colors.white,
onPressed: () {
if (v > 0) {
_beforeMute = v;
player.setVolume(0);
} else {
player.setVolume(_beforeMute <= 0 ? 100 : _beforeMute);
}
},
icon: Icon(v <= 0 ? Icons.volume_off : (v < 50 ? Icons.volume_down : Icons.volume_up)),
),
AnimatedSize(
duration: const Duration(milliseconds: 150),
child: _hover
? Padding(
padding: const EdgeInsets.only(left: 4, right: 12),
child: VolumeBar(volume: v, onChanged: player.setVolume),
)
: const SizedBox.shrink(),
),
],
),
),
);
},
);
}
}
@@ -0,0 +1,84 @@
import 'package:flutter/foundation.dart';
import 'package:hive_ce/hive.dart';
import 'package:signals/signals_flutter.dart';
// Files source state, one Hive box: the folders the user added and the
// local files played last. Same shape as AppSettings: signals start empty,
// take the stored values once [FolderStore.ensureLoaded] finishes, and
// mutators write through.
const filesBoxName = 'files';
const _keyFolders = 'folders';
const _keyRecent = 'recent';
/// Recent files kept, most recent first.
const maxRecentFiles = 30;
class FolderStore {
static final FolderStore instance = FolderStore();
/// Watched folders, in the order they were added.
final folders = signal<List<String>>(const []);
/// Local files played, most recent first, no duplicates.
final recent = signal<List<String>>(const []);
Future<void>? _load;
Box? _box;
Future<void> ensureLoaded() => _load ??= _loadNow();
Future<void> _loadNow() async {
try {
final box = _box = await Hive.openBox(filesBoxName);
final f = box.get(_keyFolders);
if (f is List) folders.value = List.unmodifiable(f.whereType<String>());
final r = box.get(_keyRecent);
if (r is List) {
recent.value = List.unmodifiable(
r.whereType<String>().toSet().take(maxRecentFiles));
}
} catch (e, st) {
debugPrint('load files store failed: $e\n$st');
}
}
/// False when [dir] is already watched.
bool addFolder(String dir) {
if (folders.value.contains(dir)) return false;
_setFolders([...folders.value, dir]);
return true;
}
void removeFolder(String dir) =>
_setFolders([...folders.value.where((d) => d != dir)]);
/// Moves [path] to the top of [recent].
void addRecent(String path) => _setRecent(
[path, ...recent.value.where((p) => p != path)].take(maxRecentFiles));
void removeRecent(String path) =>
_setRecent(recent.value.where((p) => p != path));
void clearRecent() => _setRecent(const []);
void _setFolders(List<String> list) {
folders.value = List.unmodifiable(list);
_put(_keyFolders, [...list]);
}
void _setRecent(Iterable<String> list) {
recent.value = List.unmodifiable(list);
_put(_keyRecent, [...recent.value]);
}
Future<void> _put(String key, Object value) async {
try {
final box = _box ??
(Hive.isBoxOpen(filesBoxName) ? Hive.box(filesBoxName) : null);
await box?.put(key, value);
} catch (e, st) {
debugPrint('save files store $key failed: $e\n$st');
}
}
}
+196
View File
@@ -0,0 +1,196 @@
// Release file names -> series / season / episode, anitomy-style but small:
// enough to group a downloads folder into shows. Unknown shapes fall back to
// the cleaned name with no episode, never to a wrong number.
/// What a release file name says.
class ParsedName {
/// Title without group, season marker, episode, year or release tags.
final String series;
final int? season;
final int? episode;
/// Release group: leading `[Group]` or scene-style trailing `-GRP`.
final String? group;
/// Release year, taken out of [series] (movies, `Title (2019)`).
final int? year;
/// Non-episode marker (`NCOP1`, `NCED`, `OVA 2`, `Special`): such files
/// carry no [episode] so they never sort among the real ones.
final String? extra;
const ParsedName(
this.series, {
this.season,
this.episode,
this.group,
this.year,
this.extra,
});
/// `S01E03`, `E25`, the [extra] marker, `S02` for a season pack, else ''.
String get label {
final s = season == null ? '' : 'S${season.toString().padLeft(2, '0')}';
if (episode != null) return '${s}E${episode.toString().padLeft(2, '0')}';
return extra ?? s;
}
/// Grouping key: case, punctuation and spacing do not split a series.
String get seriesKey => normalizeSeries(series);
@override
String toString() => 'ParsedName($series, s=$season, e=$episode, '
'group=$group, year=$year, extra=$extra)';
}
/// Lowercase letters and digits of [s], single spaces between words.
String normalizeSeries(String s) => s
.toLowerCase()
.replaceAll(RegExp(r"['’`]"), '')
.replaceAll(RegExp(r'[^\p{L}\p{N}]+', unicode: true), ' ')
.trim();
// Release tags: the title ends where the first of these starts.
final _junk = RegExp(
r'\b(?:2160p|1440p|1080[pi]|720p|576p|480p|4K|UHD|[xh]\.?26[45]|HEVC|AVC|'
r'AV1|VP9|WEB(?:-?DL|-?Rip)?|BD(?:Rip|Remux)?|Blu-?Ray|BRRip|DVD(?:Rip)?|'
r'HDTV|HDR(?:10)?|DV|REMUX|AAC(?:\d\.\d)?|FLAC|Opus|DTS(?:-HD)?|E?-?AC-?3|'
r'DDP?\d?|TrueHD|Atmos|10-?bit|8-?bit|Hi10P?|PROPER|REPACK|Dual[ -]Audio|'
r'Multi(?:ple)?[ -]Subs?(?:titles?)?|AMZN|NF|CR|DSNP|HMAX|ATVP|'
r'Batch|Complete)\b',
caseSensitive: false,
);
final _ext = RegExp(r'\.(?=[a-z0-9]*[a-z])[a-z0-9]{2,4}$', caseSensitive: false);
final _leadingGroup = RegExp(r'^\s*\[([^\]]+)\]');
final _sceneGroup = RegExp(r'-([A-Za-z0-9]+)$');
final _bracketYear = RegExp(r'[(\[]((?:19|20)\d{2})[)\]]');
final _brackets = RegExp(r'\[[^\]]*\]|\([^)]*\)|\{[^}]*\}|【[^】]*】');
// S01E02, S01.E02, S1 E2; a batch `S01E01-E02` / `S01E01E02` keeps the first.
final _sxe = RegExp(r'\bS(\d{1,2})[ .]?E(\d{1,4})(?:v\d)?(?!\d)',
caseSensitive: false);
final _nxn = RegExp(r'\b(\d{1,2})x(\d{2,3})\b');
// Anime: `Title - 25`, `- 03v2`, `- 01-02` (batch keeps the first).
final _dashNumber = RegExp(r'\s[-–]\s(\d{1,4})(?:v\d)?(?=$|[\s\-~.])');
final _episodeWord = RegExp(
r'\b(?:Episode|Ep\.?|E)\s?(\d{1,4})(?:v\d)?\b',
caseSensitive: false,
);
// Extras: unambiguous words anywhere, short ones only right after the dash.
final _extraAnywhere = RegExp(
r'(?:^|\s)(NCOP|NCED|OVA|OAD|ONA)\s?(\d{1,2})?(?:v\d)?(?=$|\s)',
caseSensitive: false,
);
final _extraAfterDash = RegExp(
r'\s[-–]\s(OP|ED|SP|Specials?|Extras?|Recap|Preview|PV|Menu)\s?(\d{1,2})?'
r'(?:v\d)?(?=$|\s)',
caseSensitive: false,
);
final _seasonInTitle = RegExp(
r'\s(?:S(\d{1,2})|Season\s?(\d{1,2})|(\d{1,2})(?:st|nd|rd|th)\s+Season)\s*$',
caseSensitive: false,
);
final _trailingYear = RegExp(r'\s((?:19|20)\d{2})$');
/// Parses a file name (with or without the extension, no directories).
ParsedName parseReleaseName(String fileName) {
var s = fileName.replaceFirst(_ext, '').trim();
final original = s;
String? group = _leadingGroup.firstMatch(s)?.group(1)?.trim();
int? year;
final by = _bracketYear.firstMatch(s);
if (by != null) year = int.parse(by.group(1)!);
s = s.replaceAll(_brackets, ' ');
// Dots and underscores as separators (scene style), unless the name
// already has spaces and only the odd `Dr. Stone` dot.
final dotted = !s.contains(' ') || RegExp(r'\w\.\w').allMatches(s).length > 2;
if (dotted) {
// `...H264-GRP`, but not `...WEB-DL`.
final tags = _junk.allMatches(s);
if (group == null && tags.isNotEmpty && tags.last.end < s.length) {
final g = _sceneGroup.firstMatch(s);
if (g != null) {
group = g.group(1);
s = s.substring(0, g.start);
}
}
s = s.replaceAll('.', ' ');
}
s = s.replaceAll('_', ' ').replaceAll(RegExp(r'\s+'), ' ').trim();
// Release tags end the interesting part.
final junk = _junk.firstMatch(s);
if (junk != null && junk.start > 0) s = s.substring(0, junk.start).trim();
int? season, episode;
String? extra;
var cut = s.length;
final extraMatch =
_extraAfterDash.firstMatch(s) ?? _extraAnywhere.firstMatch(s);
final sxe = _sxe.firstMatch(s);
if (extraMatch != null) {
final word = extraMatch.group(1)!;
final upper = word.length <= 4 ? word.toUpperCase() : _title(word);
// `OVA - 02`: the number may follow the dash instead.
final n = extraMatch.group(2) ??
_dashNumber.firstMatch(s.substring(extraMatch.end))?.group(1);
extra = n == null
? upper
: (upper.startsWith('NC') || upper.length <= 2)
? '$upper${int.parse(n)}'
: '$upper ${int.parse(n)}';
cut = extraMatch.start;
if (sxe != null && sxe.start < cut) {
season = int.parse(sxe.group(1)!);
cut = sxe.start;
}
} else if (sxe != null) {
season = int.parse(sxe.group(1)!);
episode = int.parse(sxe.group(2)!);
cut = sxe.start;
} else {
final nxn = _nxn.firstMatch(s);
if (nxn != null) {
season = int.parse(nxn.group(1)!);
episode = int.parse(nxn.group(2)!);
cut = nxn.start;
} else {
final m = _dashNumber.firstMatch(s) ?? _episodeWord.firstMatch(s);
if (m != null) {
episode = int.parse(m.group(1)!);
cut = m.start;
}
}
}
var series = s.substring(0, cut);
// Season pack or season marker kept in the title: `Title S2`,
// `Title Season 2`, `Title 2nd Season`, `Title.S01.1080p`.
final sIn = _seasonInTitle.firstMatch(series);
if (sIn != null) {
season ??= int.parse(sIn.group(1) ?? sIn.group(2) ?? sIn.group(3)!);
series = series.substring(0, sIn.start);
}
series = series.replaceAll(RegExp(r'[\s\-–:,~]+$'), '').trim();
// A trailing year is the release year, unless it is the whole title or
// too far ahead to be one (`Blade Runner 2049`).
final ty = _trailingYear.firstMatch(series);
if (ty != null) {
final y = int.parse(ty.group(1)!);
if (y <= DateTime.now().year + 1) {
year ??= y;
series = series.substring(0, ty.start).trim();
}
}
series = series.replaceAll(RegExp(r'[\s\-–:,~]+$'), '').trim();
if (series.isEmpty) {
series = original.replaceAll(RegExp(r'[._]+'), ' ').trim();
}
return ParsedName(
series,
season: season,
episode: episode,
group: group,
year: year,
extra: extra,
);
}
String _title(String w) => w[0].toUpperCase() + w.substring(1).toLowerCase();
+257
View File
@@ -0,0 +1,257 @@
import 'dart:io';
import 'dart:isolate';
import 'name_parser.dart';
// Folder scan for the Files source: videos with their parsed names,
// sidecar subtitles, everything else counted and hidden. The walk is
// synchronous I/O, so it runs in its own isolate.
const videoExtensions = {
'mkv', 'mp4', 'webm', 'avi', 'mov', 'm4v', 'ts', 'm2ts', 'wmv', 'flv', //
};
const subtitleExtensions = {'ass', 'ssa', 'srt', 'vtt', 'sup'};
/// Files looked at per scan; past this the result is [FolderScan.truncated].
const maxScannedFiles = 20000;
/// Samples and trailers under this size are hidden next to the real thing.
const _sampleMaxBytes = 300 << 20;
enum FileKind { video, subtitle, other }
FileKind kindOf(String path) {
final ext = _extension(_baseName(path));
if (videoExtensions.contains(ext)) return FileKind.video;
if (subtitleExtensions.contains(ext)) return FileKind.subtitle;
return FileKind.other;
}
class VideoFile {
final String path;
final int size;
final ParsedName parsed;
final DateTime modified;
const VideoFile(this.path, this.size, this.parsed, this.modified);
String get name => _baseName(path);
}
class FolderScan {
final List<VideoFile> videos;
/// Non-video files and samples (subtitles and dot-entries not counted).
final int hiddenCount;
/// Up to 6 of the hidden file names, for "and N other files" hints.
final List<String> hiddenExamples;
/// Video path -> subtitle files next to it with the same base name
/// (`Ep 01.mkv` -> `Ep 01.srt`, `Ep 01.en.ass`).
final Map<String, List<String>> subtitles;
/// Stopped after [maxScannedFiles] files.
final bool truncated;
const FolderScan({
required this.videos,
required this.hiddenCount,
required this.hiddenExamples,
required this.subtitles,
required this.truncated,
});
static const empty = FolderScan(
videos: [],
hiddenCount: 0,
hiddenExamples: [],
subtitles: {},
truncated: false,
);
}
/// Scans [dir] recursively off the UI isolate. Symlinks are not followed,
/// hidden (dot) files and folders are skipped, unreadable subfolders are
/// left out; never throws (a missing folder is [FolderScan.empty]).
Future<FolderScan> scanFolder(String dir, {int maxFiles = maxScannedFiles}) =>
Isolate.run(() => scanFolderSync(dir, maxFiles: maxFiles))
.catchError((Object _) => FolderScan.empty);
/// The walk behind [scanFolder]; blocks, so call it from an isolate.
FolderScan scanFolderSync(String dir, {int maxFiles = maxScannedFiles}) {
final videos = <VideoFile>[];
final subs = <String>[];
final examples = <String>[];
var hidden = 0, seen = 0;
var truncated = false;
final pending = [Directory(dir)];
walk:
while (pending.isNotEmpty) {
final List<FileSystemEntity> entries;
try {
entries = pending.removeLast().listSync(followLinks: false);
} catch (_) {
continue;
}
entries.sort((a, b) => a.path.compareTo(b.path));
for (final e in entries) {
final name = _baseName(e.path);
if (name.startsWith('.')) continue;
if (e is Directory) {
pending.add(e);
continue;
}
if (e is! File) continue; // links
if (++seen > maxFiles) {
truncated = true;
break walk;
}
final kind = kindOf(name);
if (kind == FileKind.subtitle) {
subs.add(e.path);
continue;
}
FileStat? stat;
if (kind == FileKind.video) {
try {
stat = e.statSync();
} catch (_) {}
}
final size = stat?.size ?? 0;
final lower = name.toLowerCase();
final sample = kind == FileKind.video &&
(lower.contains('sample') || lower.contains('trailer')) &&
size < _sampleMaxBytes;
if (kind != FileKind.video || sample) {
hidden++;
if (examples.length < 6) examples.add(name);
continue;
}
videos.add(VideoFile(e.path, size, parseReleaseName(name),
stat?.modified ?? DateTime.fromMillisecondsSinceEpoch(0)));
}
}
return FolderScan(
videos: videos,
hiddenCount: hidden,
hiddenExamples: examples,
subtitles: _sidecars(videos, subs),
truncated: truncated,
);
}
/// Subtitles in the same folder whose name is the video's base name,
/// optionally followed by `.lang` / `.forced` style suffixes.
Map<String, List<String>> _sidecars(List<VideoFile> videos, List<String> subs) {
final byStem = <String, List<String>>{};
for (final s in subs) {
final stem = _stripExtension(s);
byStem.putIfAbsent(stem, () => []).add(s);
// `Ep 01.en.ass`, `Ep 01.en.forced.ass`: also index the shorter stems.
var shorter = stem;
for (var i = 0; i < 2; i++) {
final dot = shorter.lastIndexOf('.');
if (dot <= shorter.lastIndexOf(RegExp(r'[/\\]')) + 1) break;
final suffix = shorter.substring(dot + 1);
// Language / flag tags only (en, eng, pt-BR, forced, sdh): a suffix
// with digits is part of the name (`Show.S01E01.srt` is not `Show`'s).
if (!RegExp(r'^[A-Za-z]{2,8}(-[A-Za-z]{2,4})?$').hasMatch(suffix)) break;
shorter = shorter.substring(0, dot);
byStem.putIfAbsent(shorter, () => []).add(s);
}
}
return {
for (final v in videos)
if (byStem[_stripExtension(v.path)] case final list?)
v.path: (list.toSet().toList()..sort()),
};
}
/// One show: its episodes in season/episode order plus its extras
/// (NCOP, OVA...) in name order.
class SeriesGroup {
/// Display title: the most common spelling among the files.
final String title;
final String key;
final List<VideoFile> episodes;
final List<VideoFile> extras;
const SeriesGroup(this.title, this.key, this.episodes, this.extras);
/// Distinct seasons, ascending; files without one count as season 1.
List<int> get seasons =>
({for (final e in episodes) e.parsed.season ?? 1}.toList()..sort());
}
/// [groupBySeries] result: shows, and videos that are not episodes.
class GroupedVideos {
final List<SeriesGroup> series;
final List<VideoFile> loose;
const GroupedVideos(this.series, this.loose);
}
/// Groups videos with an episode number (or an extra marker) by
/// [ParsedName.seriesKey]; the rest (movies, clips) stay loose, by name.
/// An extra whose series has no episodes in the list is loose too.
GroupedVideos groupBySeries(Iterable<VideoFile> videos) {
final episodes = <String, List<VideoFile>>{};
final extras = <String, List<VideoFile>>{};
final loose = <VideoFile>[];
for (final v in videos) {
final p = v.parsed;
final key = p.seriesKey;
if (p.episode != null && key.isNotEmpty) {
episodes.putIfAbsent(key, () => []).add(v);
} else if (p.extra != null && key.isNotEmpty) {
extras.putIfAbsent(key, () => []).add(v);
} else {
loose.add(v);
}
}
for (final e in extras.entries) {
if (!episodes.containsKey(e.key)) loose.addAll(e.value);
}
int byName(VideoFile a, VideoFile b) =>
a.name.toLowerCase().compareTo(b.name.toLowerCase());
final groups = [
for (final e in episodes.entries)
SeriesGroup(
_commonTitle(e.value),
e.key,
e.value..sort(_byEpisode),
(extras[e.key] ?? [])..sort(byName),
),
]..sort((a, b) => a.title.toLowerCase().compareTo(b.title.toLowerCase()));
return GroupedVideos(groups, loose..sort(byName));
}
int _byEpisode(VideoFile a, VideoFile b) {
final pa = a.parsed, pb = b.parsed;
final s = (pa.season ?? 1).compareTo(pb.season ?? 1);
if (s != 0) return s;
final e = pa.episode!.compareTo(pb.episode!);
if (e != 0) return e;
// Same episode twice (v2, another group): newest release first.
return b.name.compareTo(a.name);
}
String _commonTitle(List<VideoFile> files) {
final counts = <String, int>{};
for (final f in files) {
counts.update(f.parsed.series, (n) => n + 1, ifAbsent: () => 1);
}
return counts.entries
.reduce((a, b) => b.value > a.value ? b : a)
.key;
}
String _baseName(String path) {
final i = path.lastIndexOf(RegExp(r'[/\\]'));
return i < 0 ? path : path.substring(i + 1);
}
String _extension(String name) {
final dot = name.lastIndexOf('.');
return dot <= 0 ? '' : name.substring(dot + 1).toLowerCase();
}
String _stripExtension(String path) {
final dot = path.lastIndexOf('.');
final sep = path.lastIndexOf(RegExp(r'[/\\]'));
return dot > sep + 1 ? path.substring(0, dot) : path;
}
+181
View File
@@ -0,0 +1,181 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:crypto/crypto.dart';
import 'package:flutter/foundation.dart';
import 'package:media_kit/media_kit.dart';
import 'package:path_provider/path_provider.dart';
// Poster frames for the Files source, made by mpv itself (no ffmpeg on
// users' machines; Windows ships only libmpv). A hidden player per file
// with `vo=null`: mpv still keeps the last decoded frame, so
// `screenshot-to-file` encodes it with mpv's own JPEG writer. `start=25%`
// makes the first decoded frame the one we want, `vf=scale` keeps it
// small. Results (and failures) are cached on disk by path+size+mtime.
class Thumbnail {
/// JPEG on disk, at most [thumbnailWidth] wide.
final String imagePath;
final Duration duration;
const Thumbnail(this.imagePath, this.duration);
}
const thumbnailWidth = 480;
/// Where thumbnails are cached; swapped in tests.
Future<Directory> Function() thumbnailCacheRoot = getApplicationCacheDirectory;
/// Generations running at once: each is a full decoder.
const _maxConcurrent = 2;
const _timeout = Duration(seconds: 20);
int _running = 0;
final _waiting = <Completer<void>>[];
final _inFlight = <String, Future<Thumbnail?>>{};
/// The cached or freshly made thumbnail of the video at [path], or null
/// when it can't be made (not a video, unreadable, timed out, cancelled).
/// Never throws. [cancelled] is checked while queued and before the
/// expensive steps: a scrolled-away tile returns null without decoding.
Future<Thumbnail?> thumbnailFor(String path, {bool Function()? cancelled}) {
final existing = _inFlight[path];
if (existing != null) return existing;
final f = _thumbnailFor(path, cancelled ?? () => false).catchError((
Object e,
StackTrace st,
) {
debugPrint('thumbnail $path failed: $e\n$st');
return null;
});
_inFlight[path] = f;
f.whenComplete(() => _inFlight.remove(path));
return f;
}
/// Cache key: the same file at the same size and mtime reuses its frame.
String thumbnailKey(String path, int size, DateTime modified) => sha1
.convert(utf8.encode('$path|$size|${modified.millisecondsSinceEpoch}'))
.toString();
Future<Thumbnail?> _thumbnailFor(String path, bool Function() cancelled) async {
final stat = await File(path).stat();
if (stat.type != FileSystemEntityType.file) return null;
final dir = Directory(
'${(await thumbnailCacheRoot()).path}${Platform.pathSeparator}thumbnails');
await dir.create(recursive: true);
final base =
'${dir.path}${Platform.pathSeparator}${thumbnailKey(path, stat.size, stat.modified)}';
final cached = await _readCache(base);
if (cached != null) return cached.$1;
if (cancelled()) return null;
await _acquire();
try {
if (cancelled()) return null;
final made = await _generate(path, '$base.jpg', cancelled)
.timeout(_timeout, onTimeout: () => null);
if (made == null) {
// Remember broken files, not cancellations.
if (!cancelled()) await File('$base.json').writeAsString('{}');
return null;
}
await File('$base.json')
.writeAsString(jsonEncode({'ms': made.duration.inMilliseconds}));
return made;
} finally {
_release();
}
}
/// (thumbnail or null for a remembered failure), or null when not cached.
Future<(Thumbnail?,)?> _readCache(String base) async {
try {
final meta = File('$base.json');
if (!await meta.exists()) return null;
final ms = (jsonDecode(await meta.readAsString()) as Map)['ms'];
if (ms is! int) return (null,);
final jpg = File('$base.jpg');
if (!await jpg.exists()) return null;
return (Thumbnail(jpg.path, Duration(milliseconds: ms)),);
} catch (_) {
return null;
}
}
Future<void> _acquire() async {
if (_running < _maxConcurrent) {
_running++;
return;
}
final c = Completer<void>();
_waiting.add(c);
await c.future; // the releaser hands its slot over
}
void _release() {
if (_waiting.isNotEmpty) {
_waiting.removeAt(0).complete();
} else {
_running--;
}
}
Future<Thumbnail?> _generate(
String path,
String out,
bool Function() cancelled,
) async {
final player = Player(
configuration: const PlayerConfiguration(
vo: 'null',
muted: true,
osc: false,
libass: false,
title: 'thumbnail',
bufferSize: 4 << 20,
),
);
try {
final mpv = player.platform as NativePlayer;
for (final (k, v) in [
('ao', 'null'),
('aid', 'no'),
('sid', 'no'),
('vid', 'auto'), // media_kit starts with vid=no
('hwdec', 'no'),
('cache', 'no'),
('hr-seek', 'no'), // nearest keyframe is fine for a poster
('start', '25%'),
('vf', 'scale=w=$thumbnailWidth:h=-2'),
('screenshot-format', 'jpg'),
('screenshot-jpeg-quality', '85'),
('screenshot-tag-colorspace', 'no'),
]) {
await mpv.setProperty(k, v);
}
await player.open(Media(path), play: false);
final target = File(out);
final deadline = DateTime.now().add(_timeout);
while (DateTime.now().isBefore(deadline)) {
if (cancelled()) return null;
// Unreadable or no video (with aid=no, audio-only stops too): mpv
// drops the file and goes idle at once.
if (await mpv.getProperty('idle-active') == 'yes') return null;
// A frame is up once the video has a size and the seek is done.
final w = await mpv.getProperty('video-params/w');
final seeking = await mpv.getProperty('seeking');
if (w.isNotEmpty && w != '0' && seeking != 'yes') {
await mpv.command(['screenshot-to-file', out, 'video']);
if (await target.exists() && await target.length() > 0) {
final d = double.tryParse(await mpv.getProperty('duration')) ?? 0;
return Thumbnail(out, Duration(milliseconds: (d * 1000).round()));
}
}
await Future.delayed(const Duration(milliseconds: 50));
}
return null;
} finally {
await player.dispose();
}
}
@@ -0,0 +1,330 @@
import 'dart:io';
import 'package:flutter/material.dart';
import '../../shell/avatar.dart';
import '../name_parser.dart';
import '../scanner.dart';
import 'thumb.dart';
// The Files page's rows and cards: a series header, an episode row (also
// used for Recent), a loose-video card, and the empty states.
/// What a play control is doing right now.
enum PlayState { idle, starting, playing }
/// Last path segment, ignoring trailing separators (`/media/anime/`).
String baseName(String path) {
final parts = path.split(RegExp(r'[/\\]')).where((p) => p.isNotEmpty).toList();
return parts.isEmpty ? path : parts.last;
}
/// Name of the folder [path] is in.
String parentName(String path) => baseName(File(path).parent.path);
/// Cleaned title of a loose video: `Title (2019)`.
String cleanTitle(ParsedName p) => p.year == null ? p.series : '${p.series} (${p.year})';
class SeriesHeader extends StatelessWidget {
final SeriesGroup group;
final ThumbCache thumbs;
/// Its episodes are listed below; [onTap] opens or closes them.
final bool expanded;
final VoidCallback? onTap;
const SeriesHeader(
{super.key, required this.group, required this.thumbs, this.expanded = true, this.onTap});
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final first = group.episodes.first;
final n = group.episodes.length;
final extras = group.extras.length;
final detail = [
'$n ${n == 1 ? 'episode' : 'episodes'}',
if (extras > 0) '$extras ${extras == 1 ? 'extra' : 'extras'}',
?first.parsed.group,
].join(' · ');
return Padding(
padding: const EdgeInsets.only(top: 14),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(10),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: ThumbBuilder(
cache: thumbs,
path: first.path,
builder: (context, t, _) => ThumbImage(t, width: 96, height: 54),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(group.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.titleMedium),
Text(detail,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: scheme.onSurfaceVariant, fontSize: 12)),
],
),
),
if (onTap != null)
AnimatedRotation(
turns: expanded ? 0.5 : 0,
duration: const Duration(milliseconds: 200),
child: Icon(Icons.expand_more, color: scheme.onSurfaceVariant),
),
const SizedBox(width: 8),
],
),
),
),
);
}
}
/// One file as a row: an episode under its series header, or a Recent
/// entry ([title] given, [indent] off).
class FileRow extends StatelessWidget {
final String path;
final String title;
final String subtitle;
/// Size text; null while unknown.
final String? size;
final PlayState state;
final ThumbCache thumbs;
/// Null disables the row.
final VoidCallback? onTap;
final bool indent;
final Widget? trailing;
const FileRow({
super.key,
required this.path,
required this.title,
required this.subtitle,
required this.size,
required this.state,
required this.thumbs,
required this.onTap,
this.indent = true,
this.trailing,
});
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final playing = state == PlayState.playing;
final enabled = onTap != null;
final muted = scheme.onSurfaceVariant;
final icon = switch (state) {
PlayState.starting => const SizedBox.square(
dimension: 16, child: CircularProgressIndicator(strokeWidth: 2)),
PlayState.playing => Icon(Icons.graphic_eq, size: 20, color: scheme.primary),
PlayState.idle => Icon(Icons.play_circle_outline,
size: 20, color: enabled ? scheme.primary : scheme.onSurface.withValues(alpha: 0.38)),
};
return ThumbBuilder(
cache: thumbs,
path: path,
builder: (context, t, _) {
final duration = durationText(t);
final meta = [?duration, ?size].join(' · ');
return Material(
color: playing ? scheme.primary.withValues(alpha: 0.08) : Colors.transparent,
borderRadius: BorderRadius.circular(10),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(10),
child: Padding(
padding: EdgeInsets.fromLTRB(indent ? 20 : 4, 5, 8, 5),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(6),
child: ThumbImage(t, width: 80, height: 45),
),
const SizedBox(width: 10),
SizedBox(width: 20, child: Center(child: icon)),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Flexible(
child: Text(
title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.w600,
color: playing ? scheme.primary : null,
),
),
),
if (playing) ...[
const SizedBox(width: 8),
Pill('now playing', scheme.primary),
],
],
),
Text(subtitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: muted, fontSize: 12)),
],
),
),
if (meta.isNotEmpty) ...[
const SizedBox(width: 12),
Text(meta, style: TextStyle(color: muted, fontSize: 12)),
],
?trailing,
],
),
),
),
);
},
);
}
}
/// Text under a [VideoCard]'s 16:9 frame (title up to two lines, size).
const videoCardTextHeight = 66.0;
/// A loose video (movie, clip): 16:9 frame with its duration, then the
/// cleaned title and size.
class VideoCard extends StatelessWidget {
final VideoFile video;
final PlayState state;
final ThumbCache thumbs;
final VoidCallback? onTap;
const VideoCard({
super.key,
required this.video,
required this.state,
required this.thumbs,
required this.onTap,
});
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final playing = state == PlayState.playing;
return ThumbBuilder(
cache: thumbs,
path: video.path,
builder: (context, t, _) {
final duration = durationText(t);
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AspectRatio(
aspectRatio: 16 / 9,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: playing ? scheme.primary : Colors.transparent,
width: 2,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Stack(
fit: StackFit.expand,
children: [
ThumbImage(t),
if (duration != null)
Positioned(right: 6, bottom: 6, child: _Badge(duration)),
if (state != PlayState.idle)
Container(
color: Colors.black38,
alignment: Alignment.center,
child: state == PlayState.playing
? const Icon(Icons.graphic_eq, color: Colors.white, size: 32)
: const SizedBox.square(
dimension: 28,
child: CircularProgressIndicator(
strokeWidth: 3, color: Colors.white)),
),
],
),
),
),
),
const SizedBox(height: 6),
Flexible(
child: Text(
cleanTitle(video.parsed),
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 13,
color: playing ? scheme.primary : null,
),
),
),
Text(formatSize(video.size),
style: TextStyle(color: scheme.onSurfaceVariant, fontSize: 12)),
],
),
);
},
);
}
}
class _Badge extends StatelessWidget {
final String text;
const _Badge(this.text);
@override
Widget build(BuildContext context) => Container(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 1),
decoration: BoxDecoration(color: Colors.black87, borderRadius: BorderRadius.circular(4)),
child: Text(text, style: const TextStyle(color: Colors.white, fontSize: 11)),
);
}
class FilesEmpty extends StatelessWidget {
final IconData icon;
final String title;
final String detail;
final Widget? action;
const FilesEmpty(this.icon, this.title, this.detail, {super.key, this.action});
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return Padding(
padding: const EdgeInsets.only(top: 60, bottom: 20),
child: Column(
children: [
Icon(icon, size: 44, color: scheme.onSurfaceVariant),
const SizedBox(height: 10),
Text(title, style: Theme.of(context).textTheme.titleMedium, textAlign: TextAlign.center),
const SizedBox(height: 4),
Text(detail, style: TextStyle(color: scheme.onSurfaceVariant), textAlign: TextAlign.center),
if (action != null) ...[const SizedBox(height: 14), action!],
],
),
);
}
}
@@ -0,0 +1,749 @@
import 'dart:async';
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
import '../../shell/motion.dart';
import '../folder_store.dart';
import '../name_parser.dart';
import '../scanner.dart';
import 'file_tiles.dart';
import 'thumb.dart';
export 'thumb.dart' show ThumbnailLoader;
// The Files source: watched folders on the left (chips on top when
// narrow), the open one on the right grouped into series and loose
// videos, or Recent. A folder is scanned when first opened and on Rescan;
// scans are kept for the app session. Series start collapsed; the ones
// opened stay open for the app session. Playing goes through [onPlay].
/// Below this width the sidebar turns into chips above the list.
const filesNarrowWidth = 700.0;
/// Scans kept for the app session, by folder.
final _scans = <String, FolderScan>{};
final _running = <String, Future<FolderScan>>{};
String? _lastOpen;
/// Series opened this session, by `folder\nseries key`.
final _expanded = <String>{};
final _showRequests = ValueNotifier(0);
/// Opens [dir] (null: Recent) in the Files page: now if one is showing,
/// else when it next opens.
void showFilesFolder(String? dir) {
_lastOpen = dir;
_showRequests.value++;
}
/// Forgets the session's scans, open series and the open folder.
@visibleForTesting
void resetFilesPageSession() {
_scans.clear();
_running.clear();
_expanded.clear();
_lastOpen = null;
}
class FilesPage extends StatefulWidget {
/// Nobody else watches (offline or solo): no relay notice.
final bool solo;
/// The file will stream from this computer through the server.
final bool relayed;
/// Plays [path] in the session; returns an error text, or null.
final Future<String?> Function(String path) onPlay;
final String? nowPlayingPath;
/// Defaults to [thumbnailFor].
final ThumbnailLoader? thumbnails;
/// Defaults to [scanFolder].
final Future<FolderScan> Function(String dir)? scan;
/// Defaults to [FolderStore.instance].
final FolderStore? store;
/// Default: the system folder / file dialogs.
final Future<String?> Function()? pickFolder;
final Future<String?> Function()? pickVideo;
const FilesPage({
super.key,
this.solo = false,
required this.relayed,
required this.onPlay,
this.nowPlayingPath,
this.thumbnails,
this.scan,
this.store,
this.pickFolder,
this.pickVideo,
});
@override
State<FilesPage> createState() => _FilesPageState();
}
/// A scan grouped for display, made once per scan.
class _Library {
final GroupedVideos grouped;
final int subtitleFiles;
_Library(FolderScan scan)
: grouped = groupBySeries(scan.videos),
subtitleFiles = {for (final l in scan.subtitles.values) ...l}.length;
/// Series rows in order: a header (video null), then, for the series
/// [open] says, its episodes and extras.
List<(SeriesGroup, VideoFile?)> rows(bool Function(SeriesGroup g) open) => [
for (final g in grouped.series) ...[
(g, null),
if (open(g))
for (final v in [...g.episodes, ...g.extras]) (g, v),
],
];
}
final _libraries = Expando<_Library>();
class _FilesPageState extends State<FilesPage> {
late final ThumbCache _thumbs =
widget.thumbnails == null ? ThumbCache.shared : ThumbCache(widget.thumbnails!);
FolderStore get _store => widget.store ?? FolderStore.instance;
/// Null: Recent.
String? _open = _lastOpen;
bool _showHidden = false;
String? _starting;
String? _error;
@override
void initState() {
super.initState();
_showRequests.addListener(_shown);
_store.ensureLoaded().then((_) {
if (!mounted) return;
final open = _open;
if (open != null && !_store.folders.value.contains(open)) {
_select(null);
} else if (open != null) {
_scan(open);
}
});
}
@override
void dispose() {
_showRequests.removeListener(_shown);
super.dispose();
}
void _shown() => _select(_lastOpen);
Future<FolderScan> _runScan(String dir) => (widget.scan ?? scanFolder)(dir);
void _select(String? dir) {
setState(() {
_open = _lastOpen = dir;
_showHidden = false;
});
if (dir != null && !_scans.containsKey(dir)) _scan(dir);
}
Future<void> _scan(String dir, {bool rescan = false}) async {
var f = _running[dir];
if (f == null || rescan) {
f = _running[dir] = _runScan(dir);
if (rescan) _thumbs.forget([for (final v in _scans[dir]?.videos ?? const <VideoFile>[]) v.path]);
}
if (mounted) setState(() {});
FolderScan result;
try {
result = await f;
} catch (_) {
result = FolderScan.empty;
}
if (_running[dir] == f) {
_running.remove(dir);
_scans[dir] = result;
}
if (mounted) setState(() {});
}
Future<void> _addFolder() async {
String? dir;
try {
dir = await (widget.pickFolder ??
() => FilePicker.getDirectoryPath(dialogTitle: 'Add a folder with videos'))();
} catch (e) {
_showError('Folder dialog unavailable (${e.runtimeType})');
}
if (dir == null || !mounted) return;
_store.addFolder(dir);
_select(dir);
}
Future<void> _browse() async {
String? path;
try {
path = await (widget.pickVideo ?? _pickVideo)();
} catch (e) {
_showError('File dialog unavailable (${e.runtimeType})');
}
if (path != null && mounted) await _play(path);
}
static Future<String?> _pickVideo() async {
final files = await FilePicker.pickFiles(
dialogTitle: 'Pick a video',
type: FileType.custom,
allowedExtensions: videoExtensions.toList(),
);
return files.isEmpty ? null : files.first.path;
}
void _removeFolder(String dir) {
_store.removeFolder(dir);
_scans.remove(dir);
if (_open == dir) _select(null);
}
Future<void> _play(String path) async {
if (_starting != null) return;
setState(() {
_starting = path;
_error = null;
});
String? error;
try {
error = await widget.onPlay(path);
} catch (e) {
error = '$e';
}
if (error == null) _store.addRecent(path);
if (!mounted) return;
setState(() {
_starting = null;
_error = error;
});
}
void _showError(String text) {
if (mounted) setState(() => _error = text);
}
PlayState _stateOf(String path) => path == _starting
? PlayState.starting
: path == widget.nowPlayingPath
? PlayState.playing
: PlayState.idle;
VoidCallback _tapOf(String path) => () => _play(path);
@override
Widget build(BuildContext context) => LayoutBuilder(
builder: (context, box) {
final narrow = box.maxWidth < filesNarrowWidth;
return SignalBuilder(builder: (context) {
final folders = _store.folders.value;
final recent = _store.recent.value;
final open = _open != null && folders.contains(_open) ? _open : null;
final main = fadeThrough(
KeyedSubtree(
key: ValueKey(open ?? ''),
child: _main(context, narrow, folders, recent, open),
),
);
if (narrow) return main;
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(width: 260, child: _sidebar(context, folders, open)),
Expanded(child: main),
],
);
});
},
);
Widget _browseButton(Widget Function(VoidCallback? onPressed) build) => build(_browse);
Widget _sidebar(BuildContext context, List<String> folders, String? open) {
final scheme = Theme.of(context).colorScheme;
final shape = RoundedRectangleBorder(borderRadius: BorderRadius.circular(10));
return ListView(
padding: const EdgeInsets.fromLTRB(20, 20, 8, 20),
children: [
const Text('Folders', style: TextStyle(fontWeight: FontWeight.w600)),
const SizedBox(height: 8),
ListTile(
dense: true,
selected: open == null,
shape: shape,
leading: const Icon(Icons.history, size: 18),
title: const Text('Recent'),
onTap: () => _select(null),
),
for (final f in folders)
ListTile(
key: ValueKey('folder:$f'),
dense: true,
selected: open == f,
shape: shape,
leading: const Icon(Icons.folder_outlined, size: 18),
title: Text(baseName(f), maxLines: 1, overflow: TextOverflow.ellipsis),
subtitle: switch (_folderStatus(f)) {
final s? => Text(s, style: TextStyle(color: scheme.onSurfaceVariant, fontSize: 11)),
null => null,
},
trailing: IconButton(
tooltip: 'Remove folder',
icon: const Icon(Icons.close, size: 16),
onPressed: () => _removeFolder(f),
),
onTap: () => _select(f),
),
Align(
alignment: Alignment.centerLeft,
child: TextButton.icon(
onPressed: _addFolder,
icon: const Icon(Icons.add, size: 18),
label: const Text('Add folder'),
),
),
const Divider(),
_browseButton((onPressed) => OutlinedButton.icon(
onPressed: onPressed,
icon: const Icon(Icons.folder_open, size: 18),
label: const Text('Browse…'),
)),
const SizedBox(height: 6),
Text(
'The system file picker, for anything outside your folders.',
style: TextStyle(color: scheme.onSurfaceVariant, fontSize: 11),
),
],
);
}
/// "scanning…", "N videos", or null before the first scan.
String? _folderStatus(String dir) {
if (_running.containsKey(dir)) return 'scanning…';
final n = _scans[dir]?.videos.length;
return n == null ? null : '$n ${n == 1 ? 'video' : 'videos'}';
}
Widget _main(
BuildContext context,
bool narrow,
List<String> folders,
List<String> recent,
String? open,
) {
final scan = open == null ? null : _scans[open];
final scanning = open != null && _running.containsKey(open);
final top = <Widget>[
_header(context, open),
if (narrow) _chips(folders, open),
if (!widget.solo && widget.relayed) const _RelayNotice(),
AnimatedSize(
duration: easeTime,
curve: ease,
alignment: Alignment.topLeft,
child: _error == null
? const SizedBox(width: double.infinity)
: _ErrorBanner(_error!, onClose: () => setState(() => _error = null)),
),
];
final slivers = <Widget>[
SliverList.list(children: top),
if (open == null)
..._recent(context, recent)
else if (scan == null)
const SliverToBoxAdapter(
child: Padding(
padding: EdgeInsets.only(top: 60),
child: Center(child: CircularProgressIndicator()),
),
)
else
..._folder(context, open, scan, scanning),
];
return CustomScrollView(
slivers: [
SliverPadding(
padding: EdgeInsets.fromLTRB(narrow ? 20 : 12, 20, 20, 20),
sliver: SliverMainAxisGroup(slivers: slivers),
),
],
);
}
Widget _header(BuildContext context, String? open) {
final scheme = Theme.of(context).colorScheme;
return Padding(
padding: const EdgeInsets.only(bottom: 14),
child: Row(
children: [
Icon(open == null ? Icons.history : Icons.folder, color: scheme.primary),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
open == null ? 'Recent' : baseName(open),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.headlineSmall,
),
if (open != null)
Text(
open,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: scheme.onSurfaceVariant, fontSize: 12),
),
],
),
),
],
),
);
}
Widget _chips(List<String> folders, String? open) => Padding(
padding: const EdgeInsets.only(bottom: 14),
child: Wrap(
spacing: 8,
runSpacing: 8,
children: [
ChoiceChip(
label: const Text('Recent'),
selected: open == null,
onSelected: (_) => _select(null),
),
for (final f in folders)
// InputChip: selectable like the others, plus the remove x
// the sidebar has.
InputChip(
key: ValueKey('chip:$f'),
label: Text(baseName(f)),
selected: open == f,
onSelected: (_) => _select(f),
onDeleted: () => _removeFolder(f),
deleteButtonTooltipMessage: 'Remove folder',
),
ActionChip(
avatar: const Icon(Icons.add, size: 16),
label: const Text('Folder'),
onPressed: _addFolder,
),
_browseButton((onPressed) => ActionChip(
avatar: const Icon(Icons.folder_open, size: 16),
label: const Text('Browse…'),
onPressed: onPressed,
)),
],
),
);
List<Widget> _recent(BuildContext context, List<String> recent) {
if (recent.isEmpty) {
return [
SliverToBoxAdapter(
child: FilesEmpty(
Icons.video_library_outlined,
'Nothing played yet',
'Add a folder with your videos, or Browse for a single file.',
action: FilledButton.icon(
onPressed: _addFolder,
icon: const Icon(Icons.create_new_folder_outlined),
label: const Text('Add folder'),
),
),
),
];
}
return [
SliverList.builder(
itemCount: recent.length,
itemBuilder: (context, i) {
final path = recent[i];
return _RecentRow(
key: ValueKey(path),
path: path,
state: _stateOf(path),
thumbs: _thumbs,
onTap: _tapOf(path),
onRemove: () => _store.removeRecent(path),
);
},
),
];
}
List<Widget> _folder(BuildContext context, String dir, FolderScan scan, bool scanning) {
final scheme = Theme.of(context).colorScheme;
final lib = _libraries[scan] ??= _Library(scan);
final series = lib.grouped.series.length;
String count(int n, String one, String many) => '$n ${n == 1 ? one : many}';
final summary = [
count(scan.videos.length, 'video', 'videos') + (series > 0 ? ' in $series series' : ''),
if (scan.hiddenCount > 0)
'${count(scan.hiddenCount, 'other file', 'other files')} hidden',
if (lib.subtitleFiles > 0) count(lib.subtitleFiles, 'subtitle file', 'subtitle files'),
if (scan.truncated) 'stopped after $maxScannedFiles files',
].join(' · ');
final loose = lib.grouped.loose;
String seriesId(SeriesGroup g) => '$dir\n${g.key}';
final rows = lib.rows((g) => _expanded.contains(seriesId(g)));
return [
SliverToBoxAdapter(
child: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Icon(Icons.filter_alt_outlined, size: 16, color: scheme.onSurfaceVariant),
const SizedBox(width: 6),
Text(summary, style: TextStyle(color: scheme.onSurfaceVariant, fontSize: 12)),
if (scan.hiddenCount > 0) ...[
const SizedBox(width: 8),
TextButton(
onPressed: () => setState(() => _showHidden = !_showHidden),
child: Text(_showHidden ? 'Hide them' : 'Show some'),
),
],
TextButton.icon(
onPressed: scanning ? null : () => _scan(dir, rescan: true),
icon: scanning
? const SizedBox.square(
dimension: 14, child: CircularProgressIndicator(strokeWidth: 2))
: const Icon(Icons.refresh, size: 16),
label: Text(scanning ? 'Scanning…' : 'Rescan'),
),
],
),
),
SliverToBoxAdapter(
child: AnimatedSize(
duration: easeTime,
curve: ease,
alignment: Alignment.topLeft,
child: !_showHidden
? const SizedBox(width: double.infinity)
: Column(
children: [
for (final f in scan.hiddenExamples)
ListTile(
dense: true,
enabled: false,
leading: const Icon(Icons.insert_drive_file_outlined, size: 18),
title: Text(f, maxLines: 1, overflow: TextOverflow.ellipsis),
trailing: Text(
kindOf(f) == FileKind.video ? 'sample' : 'not a video',
style: const TextStyle(fontSize: 12),
),
),
if (scan.hiddenCount > scan.hiddenExamples.length)
ListTile(
dense: true,
enabled: false,
title: Text('and ${scan.hiddenCount - scan.hiddenExamples.length} more'),
),
],
),
),
),
if (scan.videos.isEmpty)
SliverToBoxAdapter(
child: Directory(dir).existsSync()
? const FilesEmpty(
Icons.search_off,
'No videos here',
'Only video files are listed (mkv, mp4, webm…).',
)
: const FilesEmpty(
Icons.folder_off_outlined,
'Folder not found',
'It may have been moved, renamed or be on a drive that is not connected.',
),
),
// Series: files whose names carry an episode number; a header
// click opens or closes its episodes.
SliverList.builder(
itemCount: rows.length,
itemBuilder: (context, i) {
final (group, v) = rows[i];
if (v == null) {
final id = seriesId(group);
return SeriesHeader(
key: ValueKey('series:${group.key}'),
group: group,
thumbs: _thumbs,
expanded: _expanded.contains(id),
onTap: () => setState(() => _expanded.contains(id) ? _expanded.remove(id) : _expanded.add(id)),
);
}
return FileRow(
key: ValueKey(v.path),
path: v.path,
title: v.parsed.label.isEmpty ? v.parsed.series : v.parsed.label,
subtitle: v.name,
size: formatSize(v.size),
state: _stateOf(v.path),
thumbs: _thumbs,
onTap: _tapOf(v.path),
);
},
),
// Everything else (movies, clips) as a thumbnail grid.
if (loose.isNotEmpty) ...[
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(top: 22, bottom: 10),
child: Text(
series == 0 ? 'Videos' : 'Other videos',
style: Theme.of(context).textTheme.titleMedium,
),
),
),
SliverLayoutBuilder(builder: (context, c) {
const gap = 12.0;
final w = c.crossAxisExtent;
final cols = (w / 240).floor().clamp(2, 8);
final cardW = (w - gap * (cols - 1)) / cols;
return SliverGrid.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: cols,
mainAxisSpacing: gap,
crossAxisSpacing: gap,
mainAxisExtent: cardW * 9 / 16 + videoCardTextHeight,
),
itemCount: loose.length,
itemBuilder: (context, i) {
final v = loose[i];
return VideoCard(
key: ValueKey(v.path),
video: v,
state: _stateOf(v.path),
thumbs: _thumbs,
onTap: _tapOf(v.path),
);
},
);
}),
],
];
}
}
/// A Recent entry: `Series · S01E03` over the file name, or the cleaned
/// title over its folder; greyed when the file is gone.
class _RecentRow extends StatefulWidget {
final String path;
final PlayState state;
final ThumbCache thumbs;
final VoidCallback? onTap;
final VoidCallback onRemove;
const _RecentRow({
super.key,
required this.path,
required this.state,
required this.thumbs,
required this.onTap,
required this.onRemove,
});
@override
State<_RecentRow> createState() => _RecentRowState();
}
class _RecentRowState extends State<_RecentRow> {
FileStat? _stat;
@override
void initState() {
super.initState();
File(widget.path).stat().then((s) {
if (mounted) setState(() => _stat = s);
}, onError: (Object _) {});
}
@override
Widget build(BuildContext context) {
final path = widget.path;
final p = parseReleaseName(baseName(path));
final missing = _stat?.type == FileSystemEntityType.notFound;
final episode = p.episode != null || p.extra != null;
return FileRow(
path: path,
title: episode ? '${p.series} · ${p.label}' : cleanTitle(p),
subtitle: missing ? 'File not found' : (episode ? baseName(path) : parentName(path)),
size: _stat == null || missing ? null : formatSize(_stat!.size),
state: widget.state,
thumbs: widget.thumbs,
onTap: missing ? null : widget.onTap,
indent: false,
trailing: IconButton(
tooltip: 'Remove from recent',
icon: const Icon(Icons.close, size: 16),
onPressed: widget.onRemove,
),
);
}
}
class _RelayNotice extends StatelessWidget {
const _RelayNotice();
@override
Widget build(BuildContext context) => Container(
padding: const EdgeInsets.all(10),
margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration(
color: Colors.lightBlueAccent.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(10),
),
child: const Row(
children: [
Icon(Icons.cell_tower, size: 18, color: Colors.lightBlueAccent),
SizedBox(width: 10),
Expanded(
child: Text(
'Files stream from this computer through the server. '
'Keep the app open while it plays.',
),
),
],
),
);
}
class _ErrorBanner extends StatelessWidget {
final String text;
final VoidCallback onClose;
const _ErrorBanner(this.text, {required this.onClose});
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return Container(
padding: const EdgeInsets.fromLTRB(10, 4, 4, 4),
margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration(
color: scheme.errorContainer,
borderRadius: BorderRadius.circular(10),
),
child: Row(
children: [
Icon(Icons.error_outline, size: 18, color: scheme.onErrorContainer),
const SizedBox(width: 10),
Expanded(child: Text(text, style: TextStyle(color: scheme.onErrorContainer))),
IconButton(
tooltip: 'Dismiss',
icon: Icon(Icons.close, size: 16, color: scheme.onErrorContainer),
onPressed: onClose,
),
],
),
);
}
}
+213
View File
@@ -0,0 +1,213 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import '../../shell/motion.dart';
import '../thumbnails.dart';
// Lazy poster frames for the Files page. A tile asks for its frame only
// after it has stayed on screen for a moment, so a fast scroll requests
// nothing; tiles sharing a file share one request, and the request is
// cancelled once no tile on screen wants it any more.
/// [thumbnailFor]'s shape; swapped in tests.
typedef ThumbnailLoader = Future<Thumbnail?> Function(String path, {bool Function()? cancelled});
/// A tile waits this long on screen before asking for its frame.
const thumbRequestDelay = Duration(milliseconds: 120);
/// Results of a [ThumbnailLoader] and who is waiting for them.
class ThumbCache {
final ThumbnailLoader load;
ThumbCache(this.load);
final _done = <String, Thumbnail?>{};
final _pending = <String, Future<Thumbnail?>>{};
final _waiting = <String, int>{};
/// Shared by every page using [thumbnailFor].
static final shared = ThumbCache(thumbnailFor);
bool has(String path) => _done.containsKey(path);
Thumbnail? cached(String path) => _done[path];
/// Pair every call with one [release].
Future<Thumbnail?> request(String path) {
_waiting.update(path, (n) => n + 1, ifAbsent: () => 1);
if (_done.containsKey(path)) return Future.value(_done[path]);
return _pending[path] ??= _start(path);
}
void release(String path) {
final n = (_waiting[path] ?? 0) - 1;
if (n > 0) {
_waiting[path] = n;
} else {
_waiting.remove(path);
}
}
/// Drops results, so the next request asks the loader again (rescan).
void forget(Iterable<String> paths) {
for (final p in paths) {
_done.remove(p);
}
}
Future<Thumbnail?> _start(String path) {
var gaveUp = false;
return load(path, cancelled: () => gaveUp = !_waiting.containsKey(path))
.catchError((Object _) => null)
.then((t) {
if (t == null && gaveUp) {
// Cancelled: not a result. Someone may have come back meanwhile.
if (_waiting.containsKey(path)) return _start(path);
_pending.remove(path);
return null;
}
_pending.remove(path);
_done[path] = t;
return t;
});
}
}
/// Builds with the frame of [path] (null while loading or when there is
/// none); [loaded] is false until the request finished.
class ThumbBuilder extends StatefulWidget {
final ThumbCache cache;
final String path;
final Widget Function(BuildContext context, Thumbnail? thumb, bool loaded) builder;
const ThumbBuilder({super.key, required this.cache, required this.path, required this.builder});
@override
State<ThumbBuilder> createState() => _ThumbBuilderState();
}
class _ThumbBuilderState extends State<ThumbBuilder> {
Thumbnail? _thumb;
bool _loaded = false;
Timer? _delay;
// The request held right now: its path, and a token so a late answer to
// an older request is ignored.
String? _holding;
Object? _token;
@override
void initState() {
super.initState();
_begin();
}
@override
void didUpdateWidget(ThumbBuilder old) {
super.didUpdateWidget(old);
if (old.path != widget.path || old.cache != widget.cache) {
_end(old.cache);
_begin();
}
}
@override
void dispose() {
_end(widget.cache);
super.dispose();
}
void _begin() {
final cache = widget.cache, path = widget.path;
_loaded = cache.has(path);
_thumb = cache.cached(path);
if (_loaded) return;
_delay = Timer(thumbRequestDelay, () {
_delay = null;
_holding = path;
final token = _token = Object();
cache.request(path).then((t) {
if (_token != token) return;
_holding = _token = null;
cache.release(path);
if (!mounted) return;
setState(() {
_thumb = t;
_loaded = true;
});
});
});
}
void _end(ThumbCache cache) {
_delay?.cancel();
_delay = null;
final held = _holding;
_holding = _token = null;
if (held != null) cache.release(held);
}
@override
Widget build(BuildContext context) => widget.builder(context, _thumb, _loaded);
}
/// The frame, fading in over a placeholder.
class ThumbImage extends StatelessWidget {
final Thumbnail? thumb;
final double? width, height;
const ThumbImage(this.thumb, {super.key, this.width, this.height});
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final placeholder = Container(
width: width,
height: height,
color: scheme.surfaceContainerHighest,
alignment: Alignment.center,
child: Icon(Icons.movie_outlined, color: scheme.onSurfaceVariant, size: 20),
);
final t = thumb;
if (t == null) return placeholder;
final dpr = MediaQuery.devicePixelRatioOf(context);
return Stack(
fit: width == null ? StackFit.expand : StackFit.loose,
children: [
placeholder,
Image.file(
File(t.imagePath),
width: width,
height: height,
fit: BoxFit.cover,
cacheWidth: width == null ? null : (width! * dpr).round(),
gaplessPlayback: true,
errorBuilder: (_, _, _) => const SizedBox.shrink(),
frameBuilder: (context, child, frame, sync) => sync
? child
: AnimatedOpacity(
opacity: frame == null ? 0 : 1,
duration: easeTime,
curve: ease,
child: child,
),
),
],
);
}
}
/// `1:02:03` / `24:05`.
String formatDuration(Duration d) {
final h = d.inHours, m = d.inMinutes % 60, s = d.inSeconds % 60;
String two(int n) => n.toString().padLeft(2, '0');
return h > 0 ? '$h:${two(m)}:${two(s)}' : '${two(m)}:${two(s)}';
}
/// `1.4 GB` / `350 MB` / `12 KB`.
String formatSize(int bytes) {
if (bytes >= 1 << 30) return '${(bytes / (1 << 30)).toStringAsFixed(1)} GB';
if (bytes >= 1 << 20) return '${(bytes / (1 << 20)).toStringAsFixed(0)} MB';
return '${(bytes / 1024).ceil()} KB';
}
/// The frame's duration, or null when unknown (no frame, 0 length).
String? durationText(Thumbnail? t) =>
t == null || t.duration <= Duration.zero ? null : formatDuration(t.duration);
+555
View File
@@ -0,0 +1,555 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:hive_ce/hive.dart';
import 'package:sync_relay_core/core.dart';
import '../screens/home/picked_media.dart';
// Linked Jellyfin servers, one account per ServerId, in their own Hive box.
//
// Only tokens this app obtained itself (Quick Connect or password through
// [JellyfinSignIn]) are ever stored or used: Jellyfin overwrites a token's
// device name and app version with whatever header comes with it, so
// borrowing another client's token would rename that client's device.
const jellyfinBoxName = 'jellyfin';
const _keyDeviceId = 'deviceId';
const _keyAccounts = 'accounts';
const _keyDefault = 'defaultServer';
const _keyLastPick = 'lastPick';
const _keyBrowsePrefix = 'browse:';
const _keyDismissedPrefix = 'dismissed:';
/// One signed-in user on one server.
class JellyfinAccount {
final String serverId;
final String serverName;
final String serverVersion;
/// Addresses this server was reached at, preferred first.
final List<String> urls;
final String userId;
final String userName;
final String? userImageTag;
final String token;
const JellyfinAccount({
required this.serverId,
required this.serverName,
this.serverVersion = '',
required this.urls,
required this.userId,
required this.userName,
this.userImageTag,
required this.token,
});
String get baseUrl => urls.first;
JellyfinUser get user =>
JellyfinUser(id: userId, name: userName, serverId: serverId, primaryImageTag: userImageTag);
/// "Example Server" or the host when the server has no name.
String get displayName =>
serverName.isNotEmpty ? serverName : (Uri.tryParse(baseUrl)?.host ?? baseUrl);
JellyfinAccount copyWith({
String? serverName,
String? serverVersion,
List<String>? urls,
String? userName,
String? userImageTag,
}) =>
JellyfinAccount(
serverId: serverId,
serverName: serverName ?? this.serverName,
serverVersion: serverVersion ?? this.serverVersion,
urls: urls ?? this.urls,
userId: userId,
userName: userName ?? this.userName,
userImageTag: userImageTag ?? this.userImageTag,
token: token,
);
Map<String, Object?> toJson() => {
'serverId': serverId,
'serverName': serverName,
'serverVersion': serverVersion,
'urls': urls,
'userId': userId,
'userName': userName,
'userImageTag': ?userImageTag,
'token': token,
};
static JellyfinAccount? fromJson(Object? j) {
if (j is! Map) return null;
final sid = j['serverId'], uid = j['userId'], token = j['token'], urls = j['urls'];
if (sid is! String || uid is! String || token is! String || urls is! List) return null;
final list = urls.whereType<String>().toList();
if (sid.isEmpty || token.isEmpty || list.isEmpty) return null;
String s(Object? v) => v is String ? v : '';
return JellyfinAccount(
serverId: sid,
serverName: s(j['serverName']),
serverVersion: s(j['serverVersion']),
urls: list,
userId: uid,
userName: s(j['userName']),
userImageTag: j['userImageTag'] is String ? j['userImageTag'] as String : null,
token: token,
);
}
}
enum JellyfinAccountStatus {
/// Not checked yet this run.
unknown,
checking,
online,
/// Unreachable (network error); the token may still be fine.
offline,
/// The server rejected the token: sign in again ("Reconnect").
expired,
}
/// Key-value persistence behind [JellyfinAccounts]; tests use
/// [MemoryJellyfinStore].
abstract class JellyfinStore {
Future<void> open();
Object? get(String key);
Future<void> put(String key, Object? value);
}
class HiveJellyfinStore implements JellyfinStore {
HiveJellyfinStore({this.boxName = jellyfinBoxName});
final String boxName;
Box? _box;
@override
Future<void> open() async => _box ??= await Hive.openBox(boxName);
@override
Object? get(String key) => _box?.get(key);
@override
Future<void> put(String key, Object? value) async {
final box = _box;
if (box == null) return;
value == null ? await box.delete(key) : await box.put(key, value);
}
}
class MemoryJellyfinStore implements JellyfinStore {
final Map<String, Object?> data;
MemoryJellyfinStore([Map<String, Object?>? data]) : data = data ?? {};
@override
Future<void> open() async {}
@override
Object? get(String key) => data[key];
@override
Future<void> put(String key, Object? value) async =>
value == null ? data.remove(key) : data[key] = value;
}
/// All linked Jellyfin servers, their live status and one authenticated
/// [JellyfinClient] each. [ensureLoaded] reads the box and then checks every
/// token in the background; a rejected token turns the account
/// [JellyfinAccountStatus.expired] (shown as "Reconnect"), never a silent
/// sign-out.
class JellyfinAccounts extends ChangeNotifier {
static final JellyfinAccounts instance = JellyfinAccounts();
JellyfinAccounts({
JellyfinStore? store,
this.httpFactory,
this.deviceName,
}) : _store = store ?? HiveJellyfinStore();
final JellyfinStore _store;
/// Transport for every client made here; null = real HTTP. Tests fake it.
final JellyfinHttp Function()? httpFactory;
/// Device name sent to Jellyfin; null = the hostname.
final String? deviceName;
List<JellyfinAccount> _accounts = const [];
final _status = <String, JellyfinAccountStatus>{};
final _errors = <String, String>{};
final _clients = <String, JellyfinClient>{};
final _memo = <String, ({DateTime at, Future<Object?> value})>{};
String? _defaultServerId;
String _deviceId = '';
JellyfinMedia? _lastPick;
Future<void>? _load;
bool _loaded = false;
bool _disposed = false;
List<JellyfinAccount> get accounts => _accounts;
bool get loaded => _loaded;
bool get isEmpty => _accounts.isEmpty;
JellyfinAccount? account(String serverId) =>
_accounts.where((a) => a.serverId == serverId).firstOrNull;
/// The chosen default, else the first linked server.
JellyfinAccount? get defaultAccount =>
(_defaultServerId == null ? null : account(_defaultServerId!)) ?? _accounts.firstOrNull;
String? get defaultServerId => defaultAccount?.serverId;
JellyfinAccountStatus statusOf(String serverId) =>
_status[serverId] ?? JellyfinAccountStatus.unknown;
/// Last failure text for an offline account.
String? errorOf(String serverId) => _errors[serverId];
/// The last Jellyfin pick put up, restored in the Library on the next start.
JellyfinMedia? get lastPick => _lastPick;
/// Stable per install: Jellyfin keys devices and tokens by it.
String get deviceId => _deviceId;
JellyfinClientInfo get clientInfo => JellyfinClientInfo(
// The old name on purpose: Jellyfin lists devices under it.
client: 'Watch Together',
device: deviceName ?? _hostname(),
deviceId: _deviceId.isEmpty ? 'unknown' : _deviceId,
version: appVersion,
);
static String _hostname() {
try {
final h = Platform.localHostname;
return h.isEmpty ? 'Desktop' : h;
} catch (_) {
return 'Desktop';
}
}
Future<void> ensureLoaded({bool validate = true}) =>
_load ??= _loadNow(validate: validate);
Future<void> _loadNow({required bool validate}) async {
try {
await _store.open();
final id = _store.get(_keyDeviceId);
if (id is String && id.isNotEmpty) {
_deviceId = id;
} else {
_deviceId = _newDeviceId();
await _store.put(_keyDeviceId, _deviceId);
}
_accounts = _decodeAccounts(_store.get(_keyAccounts));
final def = _store.get(_keyDefault);
_defaultServerId = def is String ? def : null;
final pick = _store.get(_keyLastPick);
_lastPick = pick is String ? JellyfinMedia.fromJson(_tryJson(pick)) : null;
} catch (e, st) {
debugPrint('load jellyfin accounts failed: $e\n$st');
if (_deviceId.isEmpty) _deviceId = _newDeviceId();
}
_loaded = true;
_notify();
if (validate) unawaited(validateAll());
}
static String _newDeviceId() {
final r = Random.secure();
return [for (var i = 0; i < 16; i++) r.nextInt(256).toRadixString(16).padLeft(2, '0')].join();
}
static Object? _tryJson(String s) {
try {
return jsonDecode(s);
} catch (_) {
return null;
}
}
static List<JellyfinAccount> _decodeAccounts(Object? raw) {
if (raw is! String) return const [];
final list = _tryJson(raw);
if (list is! List) return const [];
final out = <JellyfinAccount>[];
for (final j in list) {
final a = JellyfinAccount.fromJson(j);
if (a != null && out.every((o) => o.serverId != a.serverId)) out.add(a);
}
return out;
}
Future<void> _save() async {
try {
await _store.put(_keyAccounts, jsonEncode([for (final a in _accounts) a.toJson()]));
await _store.put(_keyDefault, _defaultServerId);
} catch (e, st) {
debugPrint('save jellyfin accounts failed: $e\n$st');
}
}
void _notify() {
if (!_disposed) notifyListeners();
}
// ------------------------------------------------------------- clients
/// A client without a token, for probing an address and signing in.
/// The caller closes it.
JellyfinClient anonymousClient(String baseUrl) =>
JellyfinClient(baseUrl: baseUrl, clientInfo: clientInfo, http: httpFactory?.call());
/// The signed-in client of a linked server (null if not linked). Shared;
/// do not close it.
JellyfinClient? clientFor(String serverId) {
final a = account(serverId);
if (a == null) return null;
final c = _clients[serverId];
if (c != null && c.baseUrl == a.baseUrl && c.accessToken == a.token) return c;
c?.close();
return _clients[serverId] = JellyfinClient(
baseUrl: a.baseUrl,
clientInfo: clientInfo,
accessToken: a.token,
userId: a.userId,
http: httpFactory?.call(),
);
}
// ---------------------------------------------------------- validation
Future<void> validateAll() => Future.wait([for (final a in _accounts) validate(a.serverId)]);
/// Checks the saved token. Tries each known address in order and moves
/// the first that answers to the front.
Future<JellyfinAccountStatus> validate(String serverId) async {
final start = account(serverId);
if (start == null) return JellyfinAccountStatus.unknown;
_status[serverId] = JellyfinAccountStatus.checking;
_notify();
Object? lastError;
for (final url in start.urls) {
final client = JellyfinClient(
baseUrl: url,
clientInfo: clientInfo,
accessToken: start.token,
userId: start.userId,
http: httpFactory?.call(),
);
try {
final user = await client.validateToken();
if (user == null) return _setStatus(serverId, JellyfinAccountStatus.expired);
final a = account(serverId);
if (a == null) return JellyfinAccountStatus.unknown;
_replace(a.copyWith(
userName: user.name.isEmpty ? null : user.name,
userImageTag: user.primaryImageTag,
urls: [url, ...a.urls.where((u) => u != url)],
));
unawaited(_save());
_errors.remove(serverId);
return _setStatus(serverId, JellyfinAccountStatus.online);
} catch (e) {
lastError = e;
} finally {
client.close();
}
}
_errors[serverId] = _short(lastError);
return _setStatus(serverId, JellyfinAccountStatus.offline);
}
static String _short(Object? e) {
final s = '$e'.replaceFirst('JellyfinException: ', '');
return s.length > 160 ? '${s.substring(0, 160)}…' : s;
}
JellyfinAccountStatus _setStatus(String serverId, JellyfinAccountStatus s) {
_status[serverId] = s;
_notify();
return s;
}
/// A browse call got 401: the token is gone, ask to reconnect.
void markExpired(String serverId) {
if (statusOf(serverId) == JellyfinAccountStatus.expired) return;
_setStatus(serverId, JellyfinAccountStatus.expired);
}
void _replace(JellyfinAccount a) {
_accounts = [for (final o in _accounts) o.serverId == a.serverId ? a : o];
}
// ------------------------------------------------------- link / unlink
/// Stores a sign-in made by this app. Signing in again to a linked server
/// replaces its account (and best-effort revokes the old token).
Future<JellyfinAccount> link({
required String baseUrl,
required JellyfinServerInfo server,
required JellyfinAuthResult auth,
}) async {
await ensureLoaded(validate: false);
final serverId = auth.serverId.isNotEmpty ? auth.serverId : server.id;
final old = account(serverId);
final a = JellyfinAccount(
serverId: serverId,
serverName: server.name,
serverVersion: server.version,
urls: [baseUrl, ...?old?.urls.where((u) => u != baseUrl)],
userId: auth.user.id,
userName: auth.user.name,
userImageTag: auth.user.primaryImageTag,
token: auth.accessToken,
);
if (old != null && old.token != a.token) unawaited(_revoke(old));
_accounts = old == null ? [..._accounts, a] : [for (final o in _accounts) o.serverId == serverId ? a : o];
_defaultServerId ??= serverId;
_status[serverId] = JellyfinAccountStatus.online;
_errors.remove(serverId);
_clearMemo(serverId);
await _save();
_notify();
return a;
}
Future<void> _revoke(JellyfinAccount a) async {
final c = JellyfinClient(
baseUrl: a.baseUrl,
clientInfo: clientInfo,
accessToken: a.token,
userId: a.userId,
http: httpFactory?.call());
try {
await c.logout();
} catch (_) {
} finally {
c.close();
}
}
/// Revokes the token on the server (best effort: an offline server or an
/// already dead token still unlinks) and forgets the account.
Future<void> signOut(String serverId) async {
final a = account(serverId);
if (a == null) return;
final c = _clients.remove(serverId);
c?.close();
await _revoke(a).timeout(const Duration(seconds: 8), onTimeout: () {});
_accounts = [for (final o in _accounts) if (o.serverId != serverId) o];
_status.remove(serverId);
_errors.remove(serverId);
_clearMemo(serverId);
if (_defaultServerId == serverId) _defaultServerId = _accounts.firstOrNull?.serverId;
if (_lastPick?.serverId == serverId) await rememberPick(null);
await _save();
_notify();
}
Future<void> setDefault(String serverId) async {
if (account(serverId) == null || _defaultServerId == serverId) return;
_defaultServerId = serverId;
_notify();
await _save();
}
Future<void> rememberPick(JellyfinMedia? pick) async {
_lastPick = pick;
try {
await _store.put(_keyLastPick, pick == null ? null : jsonEncode(pick.toJson()));
} catch (e) {
debugPrint('save jellyfin pick failed: $e');
}
}
/// The browser's last location on [serverId] (encoded route stack, see
/// `browse_scope.dart`), restored by every browser and across restarts.
Object? browseLocation(String serverId) {
try {
final v = _store.get('$_keyBrowsePrefix$serverId');
return v is String ? _tryJson(v) : null;
} catch (_) {
return null;
}
}
Future<void> saveBrowseLocation(String serverId, Object? location) async {
try {
await _store.put(
'$_keyBrowsePrefix$serverId', location == null ? null : jsonEncode(location));
} catch (e) {
debugPrint('save browse location failed: $e');
}
}
/// Continue-row entries removed by hand on [serverId] (key -> when), so a
/// next-up series (which Jellyfin cannot drop) stays out until it is
/// played again. Keys are `continueKey`s.
Map<String, DateTime> continueDismissed(String serverId) {
try {
final v = _store.get('$_keyDismissedPrefix$serverId');
final j = v is String ? _tryJson(v) : null;
if (j is! Map) return const {};
return {
for (final e in j.entries)
if (e.key is String && e.value is int)
e.key as String: DateTime.fromMillisecondsSinceEpoch(e.value as int, isUtc: true),
};
} catch (_) {
return const {};
}
}
/// Keeps [key] out of the Continue row on [serverId] until it is played
/// after [at] (default now). The newest 200 are kept.
Future<void> dismissContinue(String serverId, String key, {DateTime? at}) async {
final all = {...continueDismissed(serverId), key: (at ?? DateTime.now()).toUtc()};
final newest = all.entries.toList()..sort((a, b) => b.value.compareTo(a.value));
try {
await _store.put('$_keyDismissedPrefix$serverId', jsonEncode({
for (final e in newest.take(200)) e.key: e.value.millisecondsSinceEpoch,
}));
} catch (e) {
debugPrint('save dismissed continue failed: $e');
}
}
// --------------------------------------------------------------- cache
/// Short-lived memo for browse calls, so going back does not refetch.
/// Failures are not kept; a 401 marks the account expired.
Future<T> cached<T>(String serverId, String key, Future<T> Function() load,
{Duration ttl = const Duration(minutes: 3)}) {
final k = '$serverId|$key';
final hit = _memo[k];
if (hit != null && DateTime.now().difference(hit.at) < ttl) {
return hit.value.then((v) => v as T);
}
final f = load();
_memo[k] = (at: DateTime.now(), value: f);
f.catchError((Object e) {
if (identical(_memo[k]?.value, f)) _memo.remove(k);
if (e is JellyfinException && e.isUnauthorized) markExpired(serverId);
throw e;
}).ignore();
return f;
}
void _clearMemo(String serverId) =>
_memo.removeWhere((k, _) => k.startsWith('$serverId|'));
/// Drops cached browse results (refresh button, after a pick).
void invalidate(String serverId) => _clearMemo(serverId);
@override
void dispose() {
_disposed = true;
for (final c in _clients.values) {
c.close();
}
_clients.clear();
super.dispose();
}
}
@@ -0,0 +1,207 @@
import 'package:flutter/material.dart';
import '../screens/home/ui.dart';
import 'accounts.dart';
import 'browser.dart' show JellyfinAvatar, StatusDot, showReconnectDialog;
import 'sign_in_panel.dart';
/// Integrations > Jellyfin: linked servers (avatar, status, default,
/// reconnect, sign out) and the inline add-server flow.
class JellyfinAccountsPanel extends StatefulWidget {
final JellyfinAccounts accounts;
final Duration pollInterval;
const JellyfinAccountsPanel({
super.key,
required this.accounts,
this.pollInterval = const Duration(seconds: 3),
});
@override
State<JellyfinAccountsPanel> createState() => _JellyfinAccountsPanelState();
}
class _JellyfinAccountsPanelState extends State<JellyfinAccountsPanel> {
bool _adding = false;
@override
void initState() {
super.initState();
widget.accounts.ensureLoaded();
}
Future<void> _signOut(JellyfinAccount a) async {
final ok = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text('Sign out of ${a.displayName}?'),
content: const Text(
'This app\'s sign-in is revoked on the server and the server is unlinked. '
'You can add it again any time.'),
actions: [
TextButton(onPressed: () => Navigator.pop(context, false), child: const Text('Cancel')),
FilledButton(
key: const ValueKey('jf-sign-out-confirm'),
onPressed: () => Navigator.pop(context, true),
child: const Text('Sign out')),
],
),
);
if (ok == true) await widget.accounts.signOut(a.serverId);
}
@override
Widget build(BuildContext context) {
final t = Theme.of(context).textTheme;
return ListenableBuilder(
listenable: widget.accounts,
builder: (context, _) {
final list = widget.accounts.accounts;
return Panel(
key: const ValueKey('jf-accounts'),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(Icons.video_library_outlined,
size: 32, color: Theme.of(context).colorScheme.primary),
const SizedBox(width: kGap),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Jellyfin', style: t.titleMedium),
const SizedBox(height: 4),
const Text(
'Browse your libraries in the Library tab and put up movies and episodes. '
'Sign in with Quick Connect or your password; several servers are fine.',
style: TextStyle(color: Colors.grey),
),
],
),
),
const SizedBox(width: kGap),
if (!_adding)
FilledButton.icon(
key: const ValueKey('jf-add-server'),
onPressed: () => setState(() => _adding = true),
icon: const Icon(Icons.add, size: 18),
label: const Text('Add server'),
),
],
),
if (list.isNotEmpty) const SizedBox(height: kGap),
for (final a in list)
_AccountTile(
accounts: widget.accounts,
account: a,
isDefault: widget.accounts.defaultServerId == a.serverId,
showDefault: list.length > 1,
onSignOut: () => _signOut(a),
),
if (_adding) ...[
const SizedBox(height: kGap),
const Divider(height: 1),
const SizedBox(height: kGap),
JellyfinSignInPanel(
accounts: widget.accounts,
pollInterval: widget.pollInterval,
onCancel: () => setState(() => _adding = false),
onDone: (_) => setState(() => _adding = false),
),
],
],
),
);
},
);
}
}
class _AccountTile extends StatelessWidget {
final JellyfinAccounts accounts;
final JellyfinAccount account;
final bool isDefault;
final bool showDefault;
final VoidCallback onSignOut;
const _AccountTile({
required this.accounts,
required this.account,
required this.isDefault,
required this.showDefault,
required this.onSignOut,
});
@override
Widget build(BuildContext context) {
final t = Theme.of(context).textTheme;
final status = accounts.statusOf(account.serverId);
final statusText = switch (status) {
JellyfinAccountStatus.online => 'Connected',
JellyfinAccountStatus.offline => 'Offline',
JellyfinAccountStatus.expired => 'Sign-in expired',
JellyfinAccountStatus.checking => 'Checking…',
JellyfinAccountStatus.unknown => 'Not checked',
};
return Padding(
key: ValueKey('jf-account-${account.serverId}'),
padding: const EdgeInsets.symmetric(vertical: 6),
child: Row(
children: [
JellyfinAvatar(accounts: accounts, account: account),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('${account.userName} on ${account.displayName}', style: t.titleSmall),
Row(children: [
StatusDot(status: status),
const SizedBox(width: 6),
Flexible(
child: Text(
[
statusText,
if (account.serverVersion.isNotEmpty) 'Jellyfin ${account.serverVersion}',
account.baseUrl,
].join(' · '),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: t.bodySmall?.copyWith(color: Colors.grey),
),
),
]),
],
),
),
if (showDefault)
IconButton(
key: ValueKey('jf-default-${account.serverId}'),
tooltip: isDefault ? 'Default server' : 'Make default',
onPressed: () => accounts.setDefault(account.serverId),
icon: Icon(isDefault ? Icons.star : Icons.star_border,
color: isDefault ? Theme.of(context).colorScheme.primary : null),
),
if (status == JellyfinAccountStatus.expired)
FilledButton.tonal(
key: ValueKey('jf-reconnect-${account.serverId}'),
onPressed: () => showReconnectDialog(context, accounts, account),
child: const Text('Reconnect'),
)
else if (status == JellyfinAccountStatus.offline)
TextButton(
onPressed: () => accounts.validate(account.serverId),
child: const Text('Retry'),
),
const SizedBox(width: 4),
TextButton(
key: ValueKey('jf-sign-out-${account.serverId}'),
onPressed: onSignOut,
child: const Text('Sign out'),
),
],
),
);
}
}
@@ -0,0 +1,429 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:sync_relay_core/core.dart';
import 'accounts.dart';
import 'format.dart';
/// One level of the library browser's back stack.
sealed class BrowseRoute {
const BrowseRoute();
String get label;
/// Identity for keeping a page's state while it is in the stack.
String get key;
}
final class HomeRoute extends BrowseRoute {
const HomeRoute();
@override
String get label => 'Home';
@override
String get key => 'home';
}
/// A library or folder grid, optionally opened with a sort ("Show all"
/// next to Latest opens it newest first).
final class LibraryRoute extends BrowseRoute {
final JellyfinItem folder;
/// A `LibrarySort` name; null = by name.
final String? sort;
const LibraryRoute(this.folder, {this.sort});
@override
String get label => folder.name;
@override
String get key => sort == null ? 'lib:${folder.id}' : 'lib:${folder.id}:$sort';
}
final class SeriesRoute extends BrowseRoute {
final JellyfinItem series;
/// Season to open on (from a season card or an episode's "open series").
final String? seasonId;
const SeriesRoute(this.series, {this.seasonId});
@override
String get label => series.name;
@override
String get key => 'series:${series.id}';
}
/// A route stack as JSON for [JellyfinAccounts.saveBrowseLocation]: only
/// ids, names and the fields the pages start from (each page loads the
/// full item itself).
List<Map<String, Object?>> encodeBrowseStack(List<BrowseRoute> stack) => [
for (final r in stack)
switch (r) {
HomeRoute() => {'r': 'home'},
LibraryRoute(:final folder, :final sort) => {'r': 'lib', ..._itemJson(folder), 'sort': ?sort},
SeriesRoute(:final series, :final seasonId) => {
'r': 'series',
..._itemJson(series),
'seasonId': ?seasonId,
},
}
];
/// Inverse of [encodeBrowseStack]; always starts with Home. Null when
/// [raw] is not a stack. Entries it no longer knows are skipped.
List<BrowseRoute>? decodeBrowseStack(Object? raw) {
if (raw is! List) return null;
final out = <BrowseRoute>[const HomeRoute()];
for (final e in raw) {
if (e is! Map) return null;
final item = _itemFrom(e);
final route = switch (e['r']) {
'lib' when item != null =>
LibraryRoute(item, sort: e['sort'] is String ? e['sort'] as String : null),
'series' when item != null =>
SeriesRoute(item, seasonId: e['seasonId'] is String ? e['seasonId'] as String : null),
_ => null,
};
if (route != null && out.every((r) => r.key != route.key)) out.add(route);
}
return out;
}
Map<String, Object?> _itemJson(JellyfinItem i) => {
'id': i.id,
'name': i.name,
'type': i.type,
if (i.isFolder) 'isFolder': true,
'collectionType': ?i.collectionType,
'seriesId': ?i.seriesId,
'seriesName': ?i.seriesName,
'seasonId': ?i.seasonId,
'indexNumber': ?i.indexNumber,
'parentIndexNumber': ?i.parentIndexNumber,
};
JellyfinItem? _itemFrom(Map e) {
String? s(String k) => e[k] is String && (e[k] as String).isNotEmpty ? e[k] as String : null;
final id = s('id');
if (id == null) return null;
return JellyfinItem(
id: id,
name: s('name') ?? '',
type: s('type') ?? '',
isFolder: e['isFolder'] == true,
collectionType: s('collectionType'),
seriesId: s('seriesId'),
seriesName: s('seriesName'),
seasonId: e['r'] == 'series' ? null : s('seasonId'),
indexNumber: e['indexNumber'] is int ? e['indexNumber'] as int : null,
parentIndexNumber: e['parentIndexNumber'] is int ? e['parentIndexNumber'] as int : null,
);
}
/// Where a browser opens instead of the remembered location: a server and
/// a route stack (e.g. the series of the episode playing).
class BrowseStart {
final String serverId;
final List<BrowseRoute> stack;
const BrowseStart(this.serverId, this.stack);
/// The series page of [episode] with its season selected.
static BrowseStart? seriesOf(String serverId, JellyfinItem episode) {
final sid = episode.seriesId;
if (sid == null) return null;
return BrowseStart(serverId, [
const HomeRoute(),
SeriesRoute(JellyfinItem(id: sid, name: episode.seriesName ?? '', type: 'Series'),
seasonId: episode.seasonId),
]);
}
}
/// The card selected in the library (details panel): an item on a server.
/// [item] is what the card had, when known (the panel loads it in full).
class JellyfinSelection {
final String serverId;
final String itemId;
final JellyfinItem? item;
const JellyfinSelection(this.serverId, this.itemId, [this.item]);
@override
bool operator ==(Object other) =>
other is JellyfinSelection && other.serverId == serverId && other.itemId == itemId;
@override
int get hashCode => Object.hash(serverId, itemId);
}
/// Browser location shared by the browser, the library chips in the
/// Library header and the details panel: the server shown, its route
/// stack (saved per server on every move), a data version that bumps after
/// watched-state changes (rows reload), and the keys in the Continue row.
class BrowseController extends ChangeNotifier {
final JellyfinAccounts accounts;
/// Clock for the Continue row's age cut-off (tests pin it).
final DateTime Function() now;
BrowseController(this.accounts, {this.now = DateTime.now});
String? _serverId;
List<BrowseRoute> _stack = const [HomeRoute()];
bool _started = false;
int _version = 0;
Set<String> _continueKeys = const {};
List<BrowseRoute> get stack => _stack;
int get version => _version;
/// [continueKey]s of the cards in the Continue row now.
Set<String> get continueKeys => _continueKeys;
JellyfinAccount? get account =>
(_serverId == null ? null : accounts.account(_serverId!)) ?? accounts.defaultAccount;
String? get serverId => account?.serverId;
/// First use (accounts loaded): [start], else the server of [serverHint]
/// at its remembered location, else the default server's.
void start({BrowseStart? start, String? serverHint}) {
if (_started) return;
_started = true;
if (start != null && accounts.account(start.serverId) != null) {
_serverId = start.serverId;
_stack = start.stack.isEmpty ? const [HomeRoute()] : start.stack;
_remember();
} else {
_serverId = serverHint != null && accounts.account(serverHint) != null
? serverHint
: accounts.defaultServerId;
_stack = _restored(_serverId);
}
}
List<BrowseRoute> _restored(String? serverId) =>
(serverId == null ? null : decodeBrowseStack(accounts.browseLocation(serverId))) ??
const [HomeRoute()];
void _remember() {
final id = serverId;
if (id != null) unawaited(accounts.saveBrowseLocation(id, encodeBrowseStack(_stack)));
}
void switchServer(String serverId) {
if (serverId == this.serverId) return;
_serverId = serverId;
_stack = _restored(serverId);
_continueKeys = const {};
notifyListeners();
}
void open(BrowseRoute route) {
if (_stack.last.key == route.key) return;
_stack = [..._stack, route];
_remember();
notifyListeners();
}
/// Home, then [route] (library chips: a library is a top-level place).
void openTop(BrowseRoute route) {
_stack = [const HomeRoute(), route];
_remember();
notifyListeners();
}
bool back() {
if (_stack.length <= 1) return false;
_stack = _stack.sublist(0, _stack.length - 1);
_remember();
notifyListeners();
return true;
}
void popTo(int index) {
_stack = _stack.sublist(0, index + 1);
_remember();
notifyListeners();
}
/// Watched state changed on the server: drop its cached lists and let
/// the rows reload.
void dataChanged() {
final id = serverId;
if (id != null) accounts.invalidate(id);
_version++;
notifyListeners();
}
set continueKeys(Set<String> keys) {
if (setEquals(keys, _continueKeys)) return;
_continueKeys = keys;
// Set while building a row: tell listeners after the frame.
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!_disposed) notifyListeners();
});
}
bool _disposed = false;
@override
void dispose() {
_disposed = true;
super.dispose();
}
}
/// What every browser page needs, provided by [JellyfinBrowser].
class BrowseScope extends InheritedWidget {
final JellyfinAccounts accounts;
final JellyfinAccount account;
final JellyfinClient client;
final BrowseController controller;
/// Item id of the selected card on this server, highlighted everywhere.
final String? selectedItemId;
/// Click: select (details panel).
final ValueChanged<JellyfinItem> onSelect;
/// Double-click: put it up (a series: its next episode).
final ValueChanged<JellyfinItem> onPutUp;
/// Home shows the library chips itself (the Library tab has them in its
/// header instead).
final bool libraryStrip;
const BrowseScope({
super.key,
required this.accounts,
required this.account,
required this.client,
required this.controller,
required this.selectedItemId,
required this.onSelect,
required this.onPutUp,
this.libraryStrip = true,
required super.child,
});
static BrowseScope of(BuildContext context) =>
context.dependOnInheritedWidgetOfExactType<BrowseScope>()!;
void onOpen(BrowseRoute route) => controller.open(route);
/// Cached browse call on this server.
Future<T> load<T>(String key, Future<T> Function(JellyfinClient c) fn) =>
accounts.cached(account.serverId, key, () => fn(client));
/// Click / Enter on a card: series, movies and episodes are selected,
/// seasons open their series, libraries and folders open.
void activate(JellyfinItem item) {
if (isSelectable(item)) {
onSelect(item);
return;
}
switch (item.kind) {
case JellyfinItemKind.season:
final sid = item.seriesId;
if (sid != null) {
onOpen(SeriesRoute(
JellyfinItem(id: sid, name: item.seriesName ?? item.name, type: 'Series'),
seasonId: item.id));
}
default:
onOpen(LibraryRoute(item));
}
}
/// Opens the series of an episode.
void openSeriesOf(JellyfinItem episode) {
final sid = episode.seriesId;
if (sid == null) return;
onOpen(SeriesRoute(
JellyfinItem(id: sid, name: episode.seriesName ?? '', type: 'Series'),
seasonId: episode.seasonId));
}
@override
bool updateShouldNotify(BrowseScope old) =>
old.selectedItemId != selectedItemId ||
old.account != account ||
old.client != client ||
old.libraryStrip != libraryStrip;
}
/// Runs [load] once (again on [retry] and when [version] changes) and
/// shows [builder] with the result, a spinner meanwhile, and a compact
/// error with Retry on failure. A reload for a new [version] keeps the old
/// result on screen until the new one arrives.
class Loader<T> extends StatefulWidget {
final Future<T> Function() load;
final Widget Function(BuildContext context, T data) builder;
final Widget? loading;
/// When false the error collapses to nothing (optional sections).
final bool showError;
final Object? version;
const Loader({
super.key,
required this.load,
required this.builder,
this.loading,
this.showError = true,
this.version,
});
@override
State<Loader<T>> createState() => _LoaderState<T>();
}
class _LoaderState<T> extends State<Loader<T>> {
late Future<T> _future = widget.load();
(T,)? _last;
void _retry() => setState(() => _future = widget.load());
@override
void didUpdateWidget(Loader<T> old) {
super.didUpdateWidget(old);
if (old.version != widget.version) _future = widget.load();
}
@override
Widget build(BuildContext context) {
return FutureBuilder<T>(
future: _future,
builder: (context, snap) {
if (snap.hasData || (snap.connectionState == ConnectionState.done && !snap.hasError)) {
_last = (snap.data as T,);
return widget.builder(context, snap.data as T);
}
if (snap.hasError) {
if (!widget.showError) return const SizedBox.shrink();
return LoadError(error: snap.error!, onRetry: _retry);
}
final last = _last;
if (last != null) return widget.builder(context, last.$1);
return widget.loading ??
const Padding(
padding: EdgeInsets.all(32),
child: Center(child: CircularProgressIndicator(strokeWidth: 2)),
);
},
);
}
}
class LoadError extends StatelessWidget {
final Object error;
final VoidCallback onRetry;
const LoadError({super.key, required this.error, required this.onRetry});
@override
Widget build(BuildContext context) {
final e = error;
final msg = e is JellyfinException
? (e.isUnauthorized ? 'Sign-in expired' : e.message)
: '$e';
return Padding(
padding: const EdgeInsets.all(16),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.cloud_off, size: 18, color: Theme.of(context).colorScheme.error),
const SizedBox(width: 8),
Flexible(
child: Text(msg.length > 160 ? '${msg.substring(0, 160)}…' : msg,
style: const TextStyle(color: Colors.grey)),
),
const SizedBox(width: 8),
TextButton(onPressed: onRetry, child: const Text('Retry')),
],
),
);
}
}
+525
View File
@@ -0,0 +1,525 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'accounts.dart';
import 'browse_scope.dart';
import 'home_page.dart';
import 'library_page.dart';
import 'search_results.dart';
import 'series_page.dart';
import 'sign_in_panel.dart';
/// Library browser for the linked Jellyfin servers: server switcher,
/// breadcrumbs, search, and a back stack of pages (Home rows, library
/// grids, series). Pages below the top stay alive, so going back keeps
/// scroll position and loaded pages. Where it is lives in a
/// [BrowseController] (shared with the Library header's library chips and
/// the details panel; one of its own when none is given).
///
/// A click on a card selects it ([onSelect]; libraries and folders open),
/// a double-click puts it up ([onPutUp]).
///
/// Keyboard: arrows move between cards, Enter selects / opens, Esc /
/// Backspace / Alt+Left go back, / or Ctrl+F searches.
class JellyfinBrowser extends StatefulWidget {
final JellyfinAccounts accounts;
final BrowseController? controller;
/// Highlighted card.
final JellyfinSelection? selected;
final ValueChanged<JellyfinSelection> onSelect;
final ValueChanged<JellyfinSelection> onPutUp;
final VoidCallback? onOpenIntegrations;
final Duration searchDebounce;
/// Opens here instead of the remembered location (the series of the
/// episode playing, after "Change video").
final BrowseStart? start;
/// Library chips on Home (off when the Library header shows them).
final bool libraryStrip;
const JellyfinBrowser({
this.start,
super.key,
required this.accounts,
required this.onSelect,
required this.onPutUp,
this.controller,
this.selected,
this.onOpenIntegrations,
this.libraryStrip = true,
this.searchDebounce = const Duration(milliseconds: 300),
});
@override
State<JellyfinBrowser> createState() => _JellyfinBrowserState();
}
class _JellyfinBrowserState extends State<JellyfinBrowser> {
BrowseController? _own;
final _search = TextEditingController();
final _searchFocus = FocusNode(debugLabel: 'jf-search');
final _body = FocusScopeNode(debugLabel: 'jf-body');
String _term = '';
Timer? _debounce;
int _refresh = 0;
JellyfinAccounts get _accounts => widget.accounts;
BrowseController get _c => widget.controller ?? (_own ??= BrowseController(widget.accounts));
@override
void initState() {
super.initState();
_c.start(start: widget.start, serverHint: widget.selected?.serverId);
_accounts.addListener(_changed);
_c.addListener(_moved);
}
@override
void didUpdateWidget(JellyfinBrowser old) {
super.didUpdateWidget(old);
if (old.accounts != widget.accounts) {
old.accounts.removeListener(_changed);
widget.accounts.addListener(_changed);
}
final was = old.controller ?? _own;
if (was != _c) {
was?.removeListener(_moved);
_c.addListener(_moved);
}
}
@override
void dispose() {
_accounts.removeListener(_changed);
_c.removeListener(_moved);
_own?.dispose();
_debounce?.cancel();
_search.dispose();
_searchFocus.dispose();
_body.dispose();
super.dispose();
}
void _changed() {
if (mounted) setState(() {});
}
/// Any move (here, from the library chips or a server switch) ends a
/// search.
int _depth = 0;
String? _server;
void _moved() {
if (!mounted) return;
final moved = _c.stack.length != _depth || _c.serverId != _server;
_depth = _c.stack.length;
_server = _c.serverId;
setState(() {
if (moved) _clearSearch();
});
}
JellyfinAccount? get _account => _c.account;
// ------------------------------------------------------------- navigation
bool _back() {
final ok = _c.back();
if (ok) _body.requestFocus();
return ok;
}
void _popTo(int index) {
setState(_clearSearch);
_c.popTo(index);
_body.requestFocus();
}
void _onSearchChanged(String text) {
_debounce?.cancel();
_debounce = Timer(widget.searchDebounce, () {
if (mounted) setState(() => _term = text.trim());
});
}
void _clearSearch() {
_debounce?.cancel();
_search.clear();
_term = '';
}
KeyEventResult _onKey(FocusNode node, KeyEvent e) {
if (e is! KeyDownEvent && e is! KeyRepeatEvent) return KeyEventResult.ignored;
final keys = HardwareKeyboard.instance;
final inField = FocusManager.instance.primaryFocus?.context
?.findAncestorStateOfType<EditableTextState>() !=
null;
final k = e.logicalKey;
if (k == LogicalKeyboardKey.escape) {
if (_search.text.isNotEmpty || _term.isNotEmpty) {
setState(_clearSearch);
_body.requestFocus();
return KeyEventResult.handled;
}
if (inField) {
_body.requestFocus();
return KeyEventResult.handled;
}
return _back() ? KeyEventResult.handled : KeyEventResult.ignored;
}
if (inField) return KeyEventResult.ignored;
if (e.character == '/' || (keys.isControlPressed && k == LogicalKeyboardKey.keyF)) {
_searchFocus.requestFocus();
return KeyEventResult.handled;
}
if (k == LogicalKeyboardKey.backspace ||
(keys.isAltPressed && k == LogicalKeyboardKey.arrowLeft) ||
k == LogicalKeyboardKey.browserBack) {
return _back() ? KeyEventResult.handled : KeyEventResult.ignored;
}
return KeyEventResult.ignored;
}
// ------------------------------------------------------------------ build
Widget _page(BrowseRoute r) => switch (r) {
HomeRoute() => const JellyfinHomePage(),
LibraryRoute(:final folder, :final sort) => JellyfinLibraryPage(folder: folder, sort: sort),
SeriesRoute(:final series, :final seasonId) =>
JellyfinSeriesPage(series: series, seasonId: seasonId),
};
@override
Widget build(BuildContext context) {
final account = _account;
if (account == null) {
return const Center(child: Text('No Jellyfin server linked', style: TextStyle(color: Colors.grey)));
}
final client = _accounts.clientFor(account.serverId)!;
final status = _accounts.statusOf(account.serverId);
final sel = widget.selected;
final searching = _term.isNotEmpty;
final stack = _c.stack;
final pages = Stack(
children: [
for (var i = 0; i < stack.length; i++)
Visibility(
key: ValueKey('$_refresh:${account.serverId}:$i:${stack[i].key}'),
visible: !searching && i == stack.length - 1,
maintainState: true,
child: _page(stack[i]),
),
if (searching) JellyfinSearchResults(key: ValueKey('$_refresh:$_term'), term: _term),
],
);
return BrowseScope(
accounts: _accounts,
account: account,
client: client,
controller: _c,
selectedItemId: sel != null && sel.serverId == account.serverId ? sel.itemId : null,
onSelect: (item) => widget.onSelect(JellyfinSelection(account.serverId, item.id, item)),
onPutUp: (item) => widget.onPutUp(JellyfinSelection(account.serverId, item.id, item)),
libraryStrip: widget.libraryStrip,
child: Focus(
onKeyEvent: _onKey,
child: Column(
key: const ValueKey('jf-browser'),
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_header(context, account),
if (status == JellyfinAccountStatus.expired || status == JellyfinAccountStatus.offline)
_StatusBanner(accounts: _accounts, account: account, status: status),
const SizedBox(height: 12),
Expanded(child: FocusScope(node: _body, child: pages)),
],
),
),
);
}
Widget _header(BuildContext context, JellyfinAccount account) {
final theme = Theme.of(context);
final t = theme.textTheme;
final stack = _c.stack;
final crumbs = <Widget>[];
for (var i = 0; i < stack.length; i++) {
final last = i == stack.length - 1 && _term.isEmpty;
if (i > 0) {
crumbs.add(const Padding(
padding: EdgeInsets.symmetric(horizontal: 2),
child: Icon(Icons.chevron_right, size: 18, color: Colors.grey),
));
}
final label = Text(
stack[i].label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: t.titleSmall?.copyWith(
color: last ? theme.colorScheme.onSurface : theme.colorScheme.onSurfaceVariant,
fontWeight: last ? FontWeight.w600 : null),
);
crumbs.add(Flexible(
child: last
? label
: InkWell(
key: ValueKey('crumb-$i'),
borderRadius: BorderRadius.circular(6),
onTap: () => _popTo(i),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4), child: label),
),
));
}
if (_term.isNotEmpty) {
crumbs
..add(const Icon(Icons.chevron_right, size: 18, color: Colors.grey))
..add(Flexible(
child: Text('Search “$_term”',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: t.titleSmall?.copyWith(fontWeight: FontWeight.w600))));
}
return LayoutBuilder(builder: (context, box) {
final narrow = box.maxWidth < 640;
return Row(
children: [
_ServerSwitcher(
accounts: _accounts,
current: account,
compact: narrow,
onSelect: _c.switchServer,
onOpenIntegrations: widget.onOpenIntegrations,
),
IconButton(
tooltip: 'Back (Esc)',
visualDensity: VisualDensity.compact,
onPressed: stack.length > 1 ? _back : null,
icon: const Icon(Icons.arrow_back, size: 20),
),
const SizedBox(width: 4),
Expanded(child: Row(children: crumbs)),
const SizedBox(width: 8),
SizedBox(
width: narrow ? 170 : 260,
child: TextField(
key: const ValueKey('jf-search'),
controller: _search,
focusNode: _searchFocus,
onChanged: _onSearchChanged,
onSubmitted: (v) {
_debounce?.cancel();
setState(() => _term = v.trim());
},
decoration: InputDecoration(
isDense: true,
hintText: 'Search ( / )',
prefixIcon: const Icon(Icons.search, size: 18),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
suffixIcon: ListenableBuilder(
listenable: _search,
builder: (context, _) => _search.text.isEmpty
? const SizedBox.shrink()
: IconButton(
tooltip: 'Clear (Esc)',
iconSize: 16,
onPressed: () => setState(_clearSearch),
icon: const Icon(Icons.close),
),
),
),
),
),
IconButton(
tooltip: 'Refresh',
visualDensity: VisualDensity.compact,
onPressed: () {
_accounts.invalidate(account.serverId);
setState(() => _refresh++);
},
icon: const Icon(Icons.refresh, size: 20),
),
],
);
});
}
}
/// Current server (avatar + name); a menu when several are linked.
class _ServerSwitcher extends StatelessWidget {
final JellyfinAccounts accounts;
final JellyfinAccount current;
final ValueChanged<String> onSelect;
final VoidCallback? onOpenIntegrations;
/// Narrow: the name is shorter.
final bool compact;
const _ServerSwitcher({
required this.accounts,
required this.current,
required this.onSelect,
this.onOpenIntegrations,
this.compact = false,
});
@override
Widget build(BuildContext context) {
final t = Theme.of(context).textTheme;
final chip = Row(
mainAxisSize: MainAxisSize.min,
children: [
StatusDot(status: accounts.statusOf(current.serverId)),
const SizedBox(width: 8),
ConstrainedBox(
constraints: BoxConstraints(maxWidth: compact ? 110 : 200),
child: Text(current.displayName,
maxLines: 1, overflow: TextOverflow.ellipsis, style: t.titleSmall),
),
],
);
if (accounts.accounts.length < 2 && onOpenIntegrations == null) {
return Padding(padding: const EdgeInsets.symmetric(horizontal: 8), child: chip);
}
return MenuAnchor(
menuChildren: [
for (final a in accounts.accounts)
MenuItemButton(
key: ValueKey('server-${a.serverId}'),
leadingIcon: StatusDot(status: accounts.statusOf(a.serverId)),
trailingIcon: a.serverId == current.serverId ? const Icon(Icons.check, size: 18) : null,
onPressed: () => onSelect(a.serverId),
child: Text('${a.displayName} · ${a.userName}'),
),
if (onOpenIntegrations != null)
MenuItemButton(
leadingIcon: const Icon(Icons.add, size: 18),
onPressed: onOpenIntegrations,
child: const Text('Manage servers…'),
),
],
builder: (context, menu, _) => TextButton(
key: const ValueKey('jf-server-switch'),
onPressed: () => menu.isOpen ? menu.close() : menu.open(),
child: Row(mainAxisSize: MainAxisSize.min, children: [
chip,
const Icon(Icons.arrow_drop_down, size: 20),
]),
),
);
}
}
class StatusDot extends StatelessWidget {
final JellyfinAccountStatus status;
const StatusDot({super.key, required this.status});
@override
Widget build(BuildContext context) {
final color = switch (status) {
JellyfinAccountStatus.online => Colors.green,
JellyfinAccountStatus.offline => Colors.orange,
JellyfinAccountStatus.expired => Theme.of(context).colorScheme.error,
_ => Colors.grey,
};
return Container(
width: 8, height: 8, decoration: BoxDecoration(color: color, shape: BoxShape.circle));
}
}
class _StatusBanner extends StatelessWidget {
final JellyfinAccounts accounts;
final JellyfinAccount account;
final JellyfinAccountStatus status;
const _StatusBanner({required this.accounts, required this.account, required this.status});
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final expired = status == JellyfinAccountStatus.expired;
return Container(
key: const ValueKey('jf-status-banner'),
margin: const EdgeInsets.only(top: 8),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: (expired ? scheme.errorContainer : Colors.orange.shade900).withValues(alpha: 0.35),
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Icon(expired ? Icons.lock_clock : Icons.cloud_off, size: 18),
const SizedBox(width: 8),
Expanded(
child: Text(expired
? 'Sign-in to ${account.displayName} expired. Reconnect to keep browsing.'
: "Can't reach ${account.displayName}: ${accounts.errorOf(account.serverId) ?? 'offline'}"),
),
if (expired)
FilledButton.tonal(
onPressed: () => showReconnectDialog(context, accounts, account),
child: const Text('Reconnect'),
)
else
TextButton(
onPressed: () => accounts.validate(account.serverId),
child: const Text('Retry'),
),
],
),
);
}
}
/// Sign in again to an expired account (same server, new token).
Future<void> showReconnectDialog(
BuildContext context, JellyfinAccounts accounts, JellyfinAccount account) {
return showDialog<void>(
context: context,
builder: (context) => Dialog(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 520),
child: Padding(
padding: const EdgeInsets.all(20),
child: JellyfinSignInPanel(
accounts: accounts,
reconnect: account,
onDone: (_) => Navigator.of(context).pop(),
onCancel: () => Navigator.of(context).pop(),
),
),
),
),
);
}
/// Small helper for other screens: the user's avatar on a linked server.
class JellyfinAvatar extends StatelessWidget {
final JellyfinAccounts accounts;
final JellyfinAccount account;
final double radius;
const JellyfinAvatar({super.key, required this.accounts, required this.account, this.radius = 18});
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final initials = account.userName.isEmpty ? '?' : account.userName.characters.first.toUpperCase();
final fallback = CircleAvatar(
radius: radius,
backgroundColor: scheme.primaryContainer,
child: Text(initials, style: TextStyle(color: scheme.onPrimaryContainer)),
);
final client = accounts.clientFor(account.serverId);
final url = client?.userImageUrl(account.user, maxWidth: (radius * 4).round());
if (url == null) return fallback;
return ClipOval(
child: SizedBox(
width: radius * 2,
height: radius * 2,
child: Image.network(url.toString(),
fit: BoxFit.cover,
cacheWidth: (radius * 4).round(),
errorBuilder: (_, _, _) => fallback),
),
);
}
}
+422
View File
@@ -0,0 +1,422 @@
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:sync_relay_core/core.dart';
import 'browse_scope.dart';
import 'format.dart';
import 'images.dart';
import '../shell/motion.dart' show HScroll;
enum CardShape {
/// 2:3 poster (movies, series, latest).
poster(2 / 3, 150),
/// 16:9 thumbnail (continue watching, next up, episodes).
thumb(16 / 9, 260);
/// Image width / height.
final double aspect;
/// Smallest card width; rows and grids add columns past it.
final double minWidth;
const CardShape(this.aspect, this.minWidth);
}
/// Title + subtitle lines under a card image.
const cardTextHeight = 46.0;
const cardGap = 12.0;
/// Card image for [item] in [shape]: thumbs prefer the item's own
/// screenshot / thumb / backdrop, posters the primary (series poster for
/// episodes).
JellyfinImageRef? cardImage(JellyfinItem item, CardShape shape) {
if (shape == CardShape.poster) return item.image(JellyfinImageType.primary);
final own = item.ownImage(JellyfinImageType.primary);
final primaryIsWide = (item.primaryImageAspectRatio ?? 0) > 1.2 ||
item.kind == JellyfinItemKind.episode ||
item.kind == JellyfinItemKind.video;
if (own != null && primaryIsWide) return own;
return item.image(JellyfinImageType.thumb) ??
item.image(JellyfinImageType.backdrop) ??
item.image(JellyfinImageType.primary);
}
IconData kindIcon(JellyfinItem item) => switch (item.kind) {
JellyfinItemKind.series || JellyfinItemKind.season => Icons.live_tv_outlined,
JellyfinItemKind.episode => Icons.tv_outlined,
JellyfinItemKind.folder || JellyfinItemKind.collectionFolder => Icons.folder_outlined,
JellyfinItemKind.boxSet => Icons.collections_bookmark_outlined,
_ => Icons.movie_outlined,
};
/// Two clicks on the same card within this time put it up.
const doubleClickTime = Duration(milliseconds: 350);
/// Poster or thumb card: image with progress bar and state badges, title
/// and one meta line. Click / Enter selects it (details panel; libraries
/// and folders open), a second click soon after puts it up. [display]
/// gives the picture and title when they are not [item]'s own (a series
/// card for a new episode), [subtitle] replaces the meta line.
class MediaCard extends StatefulWidget {
final JellyfinItem item;
final JellyfinItem? display;
final String? subtitle;
final CardShape shape;
/// Rows mixing series (Continue): series name on top, "S2E3 · 12 min
/// left" below.
final bool seriesFirst;
final bool autofocus;
const MediaCard({
super.key,
required this.item,
required this.shape,
this.display,
this.subtitle,
this.seriesFirst = false,
this.autofocus = false,
});
@override
State<MediaCard> createState() => _MediaCardState();
}
class _MediaCardState extends State<MediaCard> {
bool _hover = false;
bool _focus = false;
DateTime? _lastTap;
JellyfinItem get item => widget.item;
void _tap(BrowseScope scope) {
final now = DateTime.now();
final last = _lastTap;
_lastTap = now;
if (last != null && now.difference(last) < doubleClickTime && isSelectable(item)) {
_lastTap = null;
scope.onPutUp(item);
return;
}
scope.activate(item);
}
@override
Widget build(BuildContext context) {
final scope = BrowseScope.of(context);
final theme = Theme.of(context);
final scheme = theme.colorScheme;
final shown = widget.display ?? item;
final selected = scope.selectedItemId == item.id;
final played = item.userData.played;
final progress = progressOf(item);
final unplayed = shown.userData.unplayedItemCount;
final ring = selected
? scheme.primary
: _focus
? scheme.onSurface
: Colors.transparent;
final image = AspectRatio(
aspectRatio: widget.shape.aspect,
child: AnimatedContainer(
duration: const Duration(milliseconds: 160),
curve: Curves.fastEaseInToSlowEaseOut,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(color: ring, width: 3),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(7),
child: Stack(
fit: StackFit.expand,
children: [
JellyfinImage(
client: scope.client,
image: cardImage(shown, widget.shape),
icon: kindIcon(shown),
),
if (_hover || _focus)
const ColoredBox(color: Color(0x22FFFFFF)),
if (progress > 0)
Positioned(
left: 0,
right: 0,
bottom: 0,
child: LinearProgressIndicator(
value: progress,
minHeight: 4,
backgroundColor: Colors.black54,
),
),
Positioned(
top: 6,
right: 6,
child: selected
? _Badge(color: scheme.primary, child: Icon(Icons.check, size: 14, color: scheme.onPrimary))
: played
? const _Badge(color: Color(0xCC1B5E20), child: Icon(Icons.check, size: 14, color: Colors.white))
: unplayed != null && unplayed > 0 && !isPlayable(shown) && widget.subtitle == null
? _Badge(
color: scheme.primaryContainer,
child: Text('$unplayed',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w700,
color: scheme.onPrimaryContainer)))
: const SizedBox.shrink(),
),
],
),
),
),
);
final title = widget.display != null ? shown.name : cardTitle(item, seriesFirst: widget.seriesFirst);
final sub = widget.subtitle ??
(widget.seriesFirst && item.kind == JellyfinItemKind.episode
? [cardSubtitle(item), item.name].where((s) => s.isNotEmpty).join(' · ')
: cardSubtitle(item));
return Semantics(
button: true,
selected: selected,
label: title,
child: InkWell(
key: ValueKey('card-${item.id}'),
autofocus: widget.autofocus,
borderRadius: BorderRadius.circular(10),
focusColor: Colors.transparent,
hoverColor: Colors.transparent,
onTap: () => _tap(scope),
onHover: (h) => setState(() => _hover = h),
onFocusChange: (f) => setState(() => _focus = f),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
image,
const SizedBox(height: 6),
SizedBox(
height: cardTextHeight - 6,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w600,
color: selected ? scheme.primary : null)),
Text(sub,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.bodySmall?.copyWith(color: Colors.grey)),
],
),
),
],
),
),
);
}
}
class _Badge extends StatelessWidget {
final Color color;
final Widget child;
const _Badge({required this.color, required this.child});
@override
Widget build(BuildContext context) => Container(
constraints: const BoxConstraints(minWidth: 22, minHeight: 22),
padding: const EdgeInsets.symmetric(horizontal: 5),
alignment: Alignment.center,
decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(11)),
child: child,
);
}
/// Columns and card width that fill [width] exactly with cards at least
/// [minWidth] wide: no half-cut card at the edge, any window width.
({int cols, double itemWidth}) fitColumns(double width, double minWidth, {double gap = cardGap}) {
final cols = math.max(1, ((width + gap) / (minWidth + gap)).floor());
return (cols: cols, itemWidth: (width - gap * (cols - 1)) / cols);
}
/// Grid layout for cards: as many columns as fit, height = image + text.
class CardGridDelegate extends SliverGridDelegate {
final CardShape shape;
final double minWidth;
CardGridDelegate(this.shape, {double? minWidth}) : minWidth = minWidth ?? shape.minWidth;
@override
SliverGridLayout getLayout(SliverConstraints constraints) {
final fit = fitColumns(constraints.crossAxisExtent, minWidth);
final h = fit.itemWidth / shape.aspect + cardTextHeight;
return SliverGridRegularTileLayout(
crossAxisCount: fit.cols,
mainAxisStride: h + 18,
crossAxisStride: fit.itemWidth + cardGap,
childMainAxisExtent: h,
childCrossAxisExtent: fit.itemWidth,
reverseCrossAxis: false,
);
}
@override
bool shouldRelayout(CardGridDelegate old) => old.shape != shape || old.minWidth != minWidth;
}
/// One card of a [MediaRow]: see [MediaCard].
typedef CardEntry = ({JellyfinItem item, JellyfinItem? display, String? subtitle});
List<CardEntry> cardEntries(Iterable<JellyfinItem> items) =>
[for (final i in items) (item: i, display: null, subtitle: null)];
/// A titled horizontal row of cards sized to fill the width; mouse drag
/// and the wheel move it ([HScroll]), arrows (on hover) page through it.
/// Lazy: only visible cards are built.
class MediaRow extends StatefulWidget {
final String title;
final List<CardEntry> entries;
final CardShape shape;
final bool seriesFirst;
final Widget? trailing;
const MediaRow({
super.key,
required this.title,
required this.entries,
required this.shape,
this.seriesFirst = false,
this.trailing,
});
@override
State<MediaRow> createState() => _MediaRowState();
}
class _MediaRowState extends State<MediaRow> {
final _scroll = ScrollController();
bool _hover = false;
@override
void dispose() {
_scroll.dispose();
super.dispose();
}
void _page(int dir, double width) {
if (!_scroll.hasClients) return;
final p = _scroll.position;
final to = (p.pixels + dir * (width + cardGap)).clamp(0.0, p.maxScrollExtent);
_scroll.animateTo(to, duration: const Duration(milliseconds: 280), curve: Curves.easeOutCubic);
}
@override
Widget build(BuildContext context) {
final entries = widget.entries;
return MouseRegion(
onEnter: (_) => setState(() => _hover = true),
onExit: (_) => setState(() => _hover = false),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SectionHeader(widget.title, count: '${entries.length}', trailing: widget.trailing),
LayoutBuilder(builder: (context, box) {
final fit = fitColumns(box.maxWidth, widget.shape.minWidth);
final h = fit.itemWidth / widget.shape.aspect + cardTextHeight;
final overflow = entries.length > fit.cols;
return SizedBox(
height: h,
child: Stack(
children: [
HScroll(
controller: _scroll,
builder: (c) => ListView.separated(
controller: c,
scrollDirection: Axis.horizontal,
itemCount: entries.length,
separatorBuilder: (_, _) => const SizedBox(width: cardGap),
itemBuilder: (context, i) => SizedBox(
width: fit.itemWidth,
child: MediaCard(
item: entries[i].item,
display: entries[i].display,
subtitle: entries[i].subtitle,
shape: widget.shape,
seriesFirst: widget.seriesFirst,
),
),
),
),
if (overflow && _hover) ...[
_PageArrow(left: true, height: h - cardTextHeight, onTap: () => _page(-1, box.maxWidth)),
_PageArrow(left: false, height: h - cardTextHeight, onTap: () => _page(1, box.maxWidth)),
],
],
),
);
}),
],
),
);
}
}
class _PageArrow extends StatelessWidget {
final bool left;
final double height;
final VoidCallback onTap;
const _PageArrow({required this.left, required this.height, required this.onTap});
@override
Widget build(BuildContext context) {
return Positioned(
left: left ? 0 : null,
right: left ? null : 0,
top: 0,
height: height,
child: Center(
child: ExcludeFocus(
child: IconButton.filledTonal(
tooltip: left ? 'Previous' : 'Next',
onPressed: onTap,
icon: Icon(left ? Icons.chevron_left : Icons.chevron_right),
),
),
),
);
}
}
/// Section heading used above grids and rows.
class SectionHeader extends StatelessWidget {
final String title;
final String? count;
final Widget? trailing;
const SectionHeader(this.title, {super.key, this.count, this.trailing});
@override
Widget build(BuildContext context) {
final t = Theme.of(context).textTheme;
return Padding(
padding: const EdgeInsets.only(bottom: 8, top: 4),
child: Row(
children: [
Expanded(
child: Row(
children: [
Flexible(
child: Text(title, maxLines: 1, overflow: TextOverflow.ellipsis, style: t.titleMedium),
),
if (count != null) ...[
const SizedBox(width: 8),
Text(count!, style: t.bodySmall?.copyWith(color: Colors.grey)),
],
],
),
),
?trailing,
],
),
);
}
}
@@ -0,0 +1,514 @@
import 'package:flutter/material.dart';
import 'package:sync_relay_core/core.dart';
import '../shell/motion.dart';
import 'accounts.dart';
import 'browse_scope.dart';
import 'cards.dart' show doubleClickTime, kindIcon;
import 'format.dart';
import 'home_rows.dart' show continueKey;
import 'images.dart';
/// Width of the details panel (docked or floating).
const detailsPanelWidth = 400.0;
/// What putting up [item] plays: itself when playable; for a series its
/// next episode (resume point or next up), else its first unwatched, else
/// its first episode. Null when there is nothing to play.
Future<JellyfinItem?> playableFor(JellyfinClient c, JellyfinItem item) async {
if (isPlayable(item)) return item;
if (item.kind != JellyfinItemKind.series) return null;
final next = await c.nextUp(seriesId: item.id, limit: 1, enableResumable: true);
if (next.isNotEmpty) return next.first;
final eps = await c.episodes(item.id);
return eps.where((e) => !e.userData.played).firstOrNull ?? eps.firstOrNull;
}
typedef _Data = ({
JellyfinItem item,
JellyfinItem? series,
List<JellyfinItem> seasons,
JellyfinItem? next,
});
/// The selected card's details, at the right of the library: banner,
/// title, meta, "Put it up · S1E3" ("Play · S1E3" when watching
/// [solo]), mark watched / unwatched, remove from Continue, overview and,
/// for a series or an episode, the episode list (click picks which
/// episode goes up, double-click puts it up). Reloads when the
/// [controller]'s data version moves.
class JellyfinDetailsPanel extends StatefulWidget {
final BrowseController controller;
final JellyfinSelection selection;
/// Nobody else watches (offline or solo): the button says Play.
final bool solo;
/// Puts [item] (playable) up; returns an error text, or null when sent.
final Future<String?> Function(JellyfinSelection item) onPutUp;
final VoidCallback onClose;
const JellyfinDetailsPanel({
super.key,
required this.controller,
required this.selection,
required this.onPutUp,
required this.onClose,
this.solo = false,
});
@override
State<JellyfinDetailsPanel> createState() => _JellyfinDetailsPanelState();
}
class _JellyfinDetailsPanelState extends State<JellyfinDetailsPanel> {
String? _seasonId;
String? _targetId;
bool _busy = false;
String? _error;
DateTime? _lastTap;
String? _lastTapId;
BrowseController get _c => widget.controller;
JellyfinAccounts get _accounts => _c.accounts;
String get _serverId => widget.selection.serverId;
JellyfinClient? get _client => _accounts.clientFor(_serverId);
Future<T> _cached<T>(String key, Future<T> Function(JellyfinClient c) fn) {
final c = _client;
if (c == null) return Future.error(const JellyfinException('that Jellyfin server is not linked any more'));
return _accounts.cached(_serverId, key, () => fn(c));
}
Future<_Data> _load() async {
await _accounts.ensureLoaded();
final id = widget.selection.itemId;
final item = await _cached('item:$id', (c) => c.item(id));
final sid = item.kind == JellyfinItemKind.series ? item.id : item.seriesId;
if (sid == null || (item.kind != JellyfinItemKind.series && item.kind != JellyfinItemKind.episode)) {
return (item: item, series: null, seasons: const <JellyfinItem>[], next: null);
}
final r = await Future.wait<Object?>([
item.kind == JellyfinItemKind.series ? Future.value(item) : _cached('item:$sid', (c) => c.item(sid)),
_cached('seasons:$sid', (c) => c.seasons(sid)),
_cached('next:$sid', (c) => c.nextUp(seriesId: sid, limit: 1, enableResumable: true))
.then<List<JellyfinItem>?>((v) => v, onError: (_) => null),
]);
final next = r[2] as List<JellyfinItem>?;
return (
item: item,
series: r[0] as JellyfinItem,
seasons: r[1] as List<JellyfinItem>,
next: next == null || next.isEmpty ? null : next.first,
);
}
Future<List<JellyfinItem>> _episodes(String seriesId, String seasonId) =>
_cached('episodes:$seriesId:$seasonId', (c) => c.episodes(seriesId, seasonId: seasonId));
Future<void> _putUp(JellyfinItem target) async {
if (_busy) return;
setState(() {
_busy = true;
_error = null;
});
String? error;
try {
error = await widget.onPutUp(JellyfinSelection(_serverId, target.id, target));
} catch (e) {
error = '$e';
}
if (!mounted) return;
setState(() {
_busy = false;
_error = error;
});
}
/// Runs a watched-state change, then lets every row reload.
Future<void> _change(Future<void> Function(JellyfinClient c) action) async {
final c = _client;
if (c == null || _busy) return;
setState(() {
_busy = true;
_error = null;
});
String? error;
try {
await action(c);
} on JellyfinException catch (e) {
if (e.isUnauthorized) _accounts.markExpired(_serverId);
error = e.isUnauthorized ? 'Jellyfin sign-in expired' : e.message;
} catch (e) {
error = '$e';
}
_c.dataChanged();
if (!mounted) return;
setState(() {
_busy = false;
_error = error;
});
}
void _toggleWatched(JellyfinItem t) =>
_change((c) => t.userData.played ? c.markUnplayed(t.id) : c.markPlayed(t.id));
void _removeFromContinue(JellyfinItem t) => _change((c) async {
if (t.userData.isResumable) await c.clearResumePosition(t.id);
await _accounts.dismissContinue(_serverId, continueKey(t));
});
void _tapEpisode(JellyfinItem e) {
final now = DateTime.now();
final again = _lastTapId == e.id && _lastTap != null && now.difference(_lastTap!) < doubleClickTime;
_lastTap = again ? null : now;
_lastTapId = e.id;
setState(() => _targetId = e.id);
if (again) _putUp(e);
}
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return Material(
key: const ValueKey('jf-details'),
color: scheme.surfaceContainerLow,
child: SizedBox(
width: detailsPanelWidth,
child: ListenableBuilder(
listenable: _c,
builder: (context, _) => Loader<_Data>(
key: ValueKey('details-${widget.selection.serverId}-${widget.selection.itemId}'),
version: _c.version,
load: _load,
loading: Stack(children: [
const Center(child: CircularProgressIndicator(strokeWidth: 2)),
Positioned(right: 4, top: 4, child: _closeButton()),
]),
builder: (context, d) => _body(context, d),
),
),
),
);
}
Widget _closeButton() => IconButton(
key: const ValueKey('details-close'),
tooltip: 'Close',
onPressed: widget.onClose,
icon: const Icon(Icons.close),
);
Widget _body(BuildContext context, _Data d) {
final series = d.series;
if (series == null) return _view(context, d, null, null);
final preferred = d.item.kind == JellyfinItemKind.episode ? d.item : null;
final baseTarget = preferred ?? d.next;
final seasons = d.seasons;
final seasonId = (_seasonId != null && seasons.any((s) => s.id == _seasonId))
? _seasonId
: seasons.any((s) => s.id == baseTarget?.seasonId)
? baseTarget!.seasonId
: (seasons.where((s) => !s.userData.played).firstOrNull ?? seasons.firstOrNull)?.id;
if (seasonId == null) return _view(context, d, baseTarget, const []);
return Loader<List<JellyfinItem>>(
key: ValueKey('details-episodes-$seasonId'),
version: _c.version,
load: () => _episodes(series.id, seasonId),
loading: _view(context, d, baseTarget, null),
builder: (context, eps) {
final chosen = _targetId == null
? null
: eps.where((e) => e.id == _targetId).firstOrNull ??
(baseTarget?.id == _targetId ? baseTarget : null);
final fresh = baseTarget == null ? null : eps.where((e) => e.id == baseTarget.id).firstOrNull;
final target = chosen ??
fresh ??
baseTarget ??
eps.where((e) => !e.userData.played).firstOrNull ??
eps.firstOrNull;
return _view(context, d, target, eps, seasonId: seasonId);
},
);
}
/// [episodes] null = loading, empty = none (or not a series).
Widget _view(BuildContext context, _Data d, JellyfinItem? target, List<JellyfinItem>? episodes,
{String? seasonId}) {
final theme = Theme.of(context);
final t = theme.textTheme;
final scheme = theme.colorScheme;
final client = _client;
final shown = d.series ?? d.item;
final isSeries = d.series != null;
final playable = isSeries ? target : (isPlayable(d.item) ? d.item : null);
final banner = shown.image(JellyfinImageType.backdrop) ??
shown.image(JellyfinImageType.thumb) ??
(d.item.kind == JellyfinItemKind.episode ? d.item.ownImage(JellyfinImageType.primary) : null) ??
shown.image(JellyfinImageType.primary);
final eps = shown.recursiveItemCount;
final meta = [
if (shown.productionYear != null) '${shown.productionYear}',
if (isSeries && d.seasons.length > 1) '${d.seasons.length} seasons',
if (isSeries && eps != null && eps > 0) '$eps episodes',
if (!isSeries && shown.runTime != null) formatRuntime(shown.runTime!),
?shown.officialRating,
if (shown.communityRating != null) '★ ${shown.communityRating!.toStringAsFixed(1)}',
if (!isSeries && shown.userData.played) 'Watched',
].join(' · ');
final verb = widget.solo ? 'Play' : 'Put it up';
final label = playable != null && isSeries ? '$verb · ${playable.episodeLabel ?? playable.name}' : verb;
final inContinue = playable != null &&
(playable.userData.isResumable || _c.continueKeys.contains(continueKey(playable)));
final left = playable == null ? null : timeLeft(playable);
return ListView(
key: const ValueKey('details-list'),
padding: EdgeInsets.zero,
children: [
SizedBox(
height: 180,
child: Stack(
fit: StackFit.expand,
children: [
if (client != null)
JellyfinImage(client: client, image: banner, alignment: Alignment.topCenter, icon: kindIcon(shown))
else
ColoredBox(color: scheme.surfaceContainerHighest),
DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.transparent, scheme.surfaceContainerLow],
stops: const [0.55, 1],
),
),
),
Positioned(right: 4, top: 4, child: _closeButton()),
],
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 4, 16, 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SelectableText(shown.name, style: t.titleLarge),
if (meta.isNotEmpty) Text(meta, style: t.bodyMedium?.copyWith(color: Colors.grey)),
const SizedBox(height: 12),
Wrap(
spacing: 4,
runSpacing: 4,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
FilledButton.icon(
key: const ValueKey('details-put-up'),
onPressed: playable == null || _busy ? null : () => _putUp(playable),
icon: _busy
? const SizedBox.square(dimension: 16, child: CircularProgressIndicator(strokeWidth: 2))
: const Icon(Icons.play_arrow),
label: Text(label, overflow: TextOverflow.ellipsis),
),
if (playable != null)
IconButton(
key: const ValueKey('details-watched'),
onPressed: _busy ? null : () => _toggleWatched(playable),
tooltip: playable.userData.played ? 'Mark unwatched' : 'Mark watched',
icon: Icon(playable.userData.played ? Icons.check_circle : Icons.check_circle_outline),
),
if (inContinue)
IconButton(
key: const ValueKey('details-remove-continue'),
onPressed: _busy ? null : () => _removeFromContinue(playable),
tooltip: 'Remove from Continue',
icon: const Icon(Icons.remove_circle_outline),
),
if (isSeries)
IconButton(
key: const ValueKey('details-open-series'),
tooltip: 'Open the series page',
onPressed: () => _c.open(SeriesRoute(d.series!, seasonId: seasonId)),
icon: const Icon(Icons.open_in_new),
),
],
),
if (_error != null) ...[
const SizedBox(height: 6),
Text(
_error!,
key: const ValueKey('details-hint'),
style: t.bodySmall?.copyWith(color: scheme.error),
),
],
if (isSeries && playable != null) ...[
const SizedBox(height: 10),
Text(
[playable.episodeLabel ?? '', playable.name].where((s) => s.isNotEmpty).join(' · '),
style: t.titleSmall,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
if (left != null) ...[
const SizedBox(height: 6),
Row(children: [
SizedBox(width: 160, child: LinearProgressIndicator(value: progressOf(playable!), minHeight: 3)),
const SizedBox(width: 8),
Text(left, style: t.bodySmall?.copyWith(color: Colors.grey)),
]),
],
if (shown.overview != null) ...[
const SizedBox(height: 12),
Text(shown.overview!, style: t.bodyMedium),
],
if (!isSeries) ...[
const SizedBox(height: 12),
_Tracks(item: d.item),
],
if (isSeries) ...[
const SizedBox(height: 16),
_episodesHeader(context, d, seasonId),
if (episodes == null)
const Padding(
padding: EdgeInsets.all(24),
child: Center(child: CircularProgressIndicator(strokeWidth: 2)),
)
else if (episodes.isEmpty)
const Padding(
padding: EdgeInsets.symmetric(vertical: 12),
child: Text('No episodes', style: TextStyle(color: Colors.grey)),
)
else
for (final e in episodes) _episodeRow(context, e, target?.id == e.id),
],
],
),
),
],
);
}
Widget _episodesHeader(BuildContext context, _Data d, String? seasonId) {
final seasons = d.seasons;
return Row(
children: [
const Expanded(child: Text('Episodes', style: TextStyle(fontWeight: FontWeight.w600))),
if (seasons.length > 1)
DropdownButton<String>(
key: const ValueKey('details-season'),
value: seasonId,
isDense: true,
underline: const SizedBox.shrink(),
borderRadius: BorderRadius.circular(10),
items: [
for (final s in seasons)
DropdownMenuItem(value: s.id, child: Text(s.name, overflow: TextOverflow.ellipsis)),
],
onChanged: (id) => setState(() => _seasonId = id),
),
],
);
}
Widget _episodeRow(BuildContext context, JellyfinItem e, bool isTarget) {
final scheme = Theme.of(context).colorScheme;
final t = Theme.of(context).textTheme;
final client = _client;
final sub = e.userData.played ? 'watched' : timeLeft(e) ?? (e.runTime == null ? '' : formatRuntime(e.runTime!));
return Material(
color: isTarget ? scheme.primary.withValues(alpha: 0.10) : Colors.transparent,
borderRadius: BorderRadius.circular(8),
child: InkWell(
key: ValueKey('details-episode-${e.id}'),
borderRadius: BorderRadius.circular(8),
onTap: () => _tapEpisode(e),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 4),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: SizedBox(
width: 64,
height: 36,
child: client == null
? ColoredBox(color: scheme.surfaceContainerHighest)
: JellyfinImage(
client: client,
image: e.ownImage(JellyfinImageType.primary) ?? e.image(JellyfinImageType.thumb),
icon: Icons.tv_outlined,
),
),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
e.indexNumber == null ? e.name : 'E${e.indexNumber} · ${e.name}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: t.bodyMedium?.copyWith(
color: isTarget ? scheme.primary : e.userData.played ? Colors.grey : null,
fontWeight: isTarget ? FontWeight.w600 : null,
),
),
if (sub.isNotEmpty) Text(sub, style: t.bodySmall?.copyWith(color: Colors.grey)),
],
),
),
AnimatedOpacity(
opacity: isTarget ? 1 : 0,
duration: easeTime,
child: Icon(Icons.play_circle, color: scheme.primary),
),
],
),
),
),
);
}
}
/// What the file holds, one line per track kind.
class _Tracks extends StatelessWidget {
final JellyfinItem item;
const _Tracks({required this.item});
@override
Widget build(BuildContext context) {
final t = Theme.of(context).textTheme;
final source = item.mediaSources.firstOrNull;
final streams = source?.mediaStreams.isNotEmpty == true ? source!.mediaStreams : item.mediaStreams;
if (streams.isEmpty) return const SizedBox.shrink();
String names(JellyfinStreamType type) => streams
.where((s) => s.type == type)
.map((s) => s.displayTitle ?? [s.language, s.codec].whereType<String>().join(' '))
.join(' · ');
final rows = [
('Video', names(JellyfinStreamType.video)),
('Audio', names(JellyfinStreamType.audio)),
('Subtitles', names(JellyfinStreamType.subtitle)),
if (source?.container != null || source?.size != null)
('File', [
?source?.container,
if (source?.size != null) '${(source!.size! / (1 << 30)).toStringAsFixed(2)} GB',
].join(' · ')),
].where((r) => r.$2.isNotEmpty);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (final (label, value) in rows)
Padding(
padding: const EdgeInsets.only(bottom: 6),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(width: 76, child: Text(label, style: t.bodySmall?.copyWith(color: Colors.grey))),
Expanded(child: Text(value, style: t.bodySmall)),
],
),
),
],
);
}
}
+93
View File
@@ -0,0 +1,93 @@
import 'package:sync_relay_core/core.dart';
import '../screens/home/picked_media.dart';
import 'accounts.dart';
/// "1 h 42 min", "24 min", "45 s".
String formatRuntime(Duration d) {
final h = d.inHours, m = d.inMinutes % 60;
if (h > 0) return m == 0 ? '$h h' : '$h h $m min';
if (d.inMinutes > 0) return '${d.inMinutes} min';
return '${d.inSeconds} s';
}
/// Watched share of a resumable item, 0 when there is no resume point.
double progressOf(JellyfinItem item) {
final run = item.runTimeTicks;
final pos = item.userData.playbackPositionTicks;
if (run == null || run <= 0 || pos <= 0) return 0;
return (pos / run).clamp(0.0, 1.0);
}
/// "12 min left" for a resumable item, else null.
String? timeLeft(JellyfinItem item) {
final run = item.runTimeTicks;
if (run == null || !item.userData.isResumable) return null;
final left = ticksToDuration(run - item.userData.playbackPositionTicks);
if (left.inMinutes < 1) return 'almost done';
return '${formatRuntime(left)} left';
}
bool isPlayable(JellyfinItem item) => switch (item.kind) {
JellyfinItemKind.movie || JellyfinItemKind.episode || JellyfinItemKind.video => true,
_ => !item.isFolder && item.mediaType == 'Video',
};
/// Cards that select (details panel) on click: playable items and series.
bool isSelectable(JellyfinItem item) => isPlayable(item) || item.kind == JellyfinItemKind.series;
/// Line under a card title: "S2E3 · 12 min left", "2019 · 24 eps", ...
String cardSubtitle(JellyfinItem item, {bool withSeries = false}) {
final parts = <String>[];
switch (item.kind) {
case JellyfinItemKind.episode:
if (withSeries && item.seriesName != null) parts.add(item.seriesName!);
parts.add(item.episodeLabel ?? item.seasonName ?? 'Episode');
case JellyfinItemKind.series:
if (item.productionYear != null) parts.add('${item.productionYear}');
final eps = item.recursiveItemCount, seasons = item.childCount;
if (eps != null && eps > 0) {
parts.add('$eps eps');
} else if (seasons != null && seasons > 0) {
parts.add(seasons == 1 ? '1 season' : '$seasons seasons');
}
case JellyfinItemKind.season:
final n = item.childCount ?? item.recursiveItemCount;
if (n != null) parts.add('$n eps');
default:
if (item.productionYear != null) parts.add('${item.productionYear}');
}
final left = timeLeft(item);
if (left != null) {
parts.add(left);
} else if (isPlayable(item) && item.runTime != null) {
parts.add(formatRuntime(item.runTime!));
}
return parts.join(' · ');
}
/// Title on a card: episodes in rows that mix series show the series name
/// on top and the episode below.
String cardTitle(JellyfinItem item, {bool seriesFirst = false}) =>
seriesFirst && item.kind == JellyfinItemKind.episode && item.seriesName != null
? item.seriesName!
: item.name;
/// The selection for a playable [item] of [account].
JellyfinMedia pickFor(JellyfinAccount account, JellyfinItem item) {
final String subtitle;
if (item.kind == JellyfinItemKind.episode) {
subtitle = [?item.seriesName, ?item.episodeLabel].join(' · ');
} else {
subtitle = item.productionYear?.toString() ?? '';
}
return JellyfinMedia(
serverId: account.serverId,
baseUrl: account.baseUrl,
itemId: item.id,
mediaSourceId: item.mediaSources.length > 1 ? item.mediaSources.first.id : null,
title: item.name,
subtitle: subtitle,
runtime: item.runTime,
);
}
@@ -0,0 +1,259 @@
import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
import 'package:sync_relay_core/core.dart';
import '../state/settings.dart';
import 'browse_scope.dart';
import 'cards.dart';
import 'home_rows.dart';
/// Libraries that hold videos; music, books, photos and playlists are left
/// out of the browser.
bool isVideoLibrary(JellyfinItem view) => switch (view.collectionType) {
'movies' || 'tvshows' || 'homevideos' || 'boxsets' || 'mixed' || 'musicvideos' || null => true,
_ => false,
};
IconData libraryIcon(JellyfinItem view) => switch (view.collectionType) {
'movies' => Icons.movie_outlined,
'tvshows' => Icons.live_tv_outlined,
'boxsets' => Icons.collections_bookmark_outlined,
'homevideos' => Icons.videocam_outlined,
_ => Icons.folder_outlined,
};
/// The video libraries of the scope's server (cached).
Future<List<JellyfinItem>> videoLibraries(BrowseScope scope) =>
scope.load('views', (c) => c.userViews()).then((all) => all.where(isVideoLibrary).toList());
/// Start page: (library shortcuts,) one Continue row (resume points and
/// next up merged per series, see [mergeContinue]), then the newest
/// additions of each library as series cards ([latestCards]). Each row
/// loads on its own, so a slow library does not hold up the rest; rows
/// reload when watched state changes ([BrowseController.dataChanged]).
class JellyfinHomePage extends StatelessWidget {
const JellyfinHomePage({super.key});
@override
Widget build(BuildContext context) {
final scope = BrowseScope.of(context);
return Loader<List<JellyfinItem>>(
key: ValueKey('views-${scope.account.serverId}'),
load: () => videoLibraries(scope),
builder: (context, views) => ListenableBuilder(
listenable: scope.controller,
builder: (context, _) {
final v = scope.controller.version;
return ListView(
key: const PageStorageKey('jf-home'),
padding: const EdgeInsets.fromLTRB(0, 4, 0, 24),
children: [
if (scope.libraryStrip) ...[
LibraryStrip(views: views),
const SizedBox(height: 20),
] else if (views.isEmpty)
const _NoLibraries(),
_ContinueRow(version: v),
for (final view in views) _LatestRow(key: ValueKey('row-latest-${view.id}'), view: view, version: v),
],
);
},
),
);
}
}
class _NoLibraries extends StatelessWidget {
const _NoLibraries();
@override
Widget build(BuildContext context) => const Padding(
padding: EdgeInsets.only(bottom: 20),
child: Text('No video libraries on this server for your user.', style: TextStyle(color: Colors.grey)),
);
}
/// The libraries as chips; a click opens one.
class LibraryStrip extends StatelessWidget {
final List<JellyfinItem> views;
const LibraryStrip({super.key, required this.views});
@override
Widget build(BuildContext context) {
final scope = BrowseScope.of(context);
if (views.isEmpty) return const _NoLibraries();
return Wrap(
spacing: 8,
runSpacing: 8,
children: [
for (final v in views)
ActionChip(
key: ValueKey('library-${v.id}'),
avatar: Icon(libraryIcon(v), size: 18),
label: Text(v.name),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
onPressed: () => scope.controller.openTop(LibraryRoute(v)),
),
],
);
}
}
class _ContinueRow extends StatelessWidget {
final int version;
const _ContinueRow({required this.version});
static const title = 'Continue';
Future<List<JellyfinItem>> _load(BrowseScope scope) async {
final lists = await Future.wait([
scope.load('resume', (c) => c.resume(limit: 40)),
scope.load('nextup', (c) => c.nextUp(limit: 40)),
// Only for ordering: a failure leaves the row as it is.
scope
.load('played', (c) => c.recentlyPlayed(limit: 150))
.catchError((Object _) => const <JellyfinItem>[]),
]);
return mergeContinue(
resume: lists[0],
nextUp: lists[1],
recentlyPlayed: lists[2],
dismissed: scope.accounts.continueDismissed(scope.account.serverId),
now: scope.controller.now(),
);
}
@override
Widget build(BuildContext context) {
final scope = BrowseScope.of(context);
return Loader<List<JellyfinItem>>(
version: version,
load: () => _load(scope),
showError: false,
loading: const _RowSkeleton(title: title, shape: CardShape.thumb),
builder: (context, items) {
scope.controller.continueKeys = {for (final i in items) continueKey(i)};
if (items.isEmpty) return const SizedBox.shrink();
return Padding(
key: const ValueKey('row-continue'),
padding: const EdgeInsets.only(bottom: 24),
child: MediaRow(
title: title,
entries: cardEntries(items),
shape: CardShape.thumb,
seriesFirst: true,
),
);
},
);
}
}
class _LatestRow extends StatelessWidget {
final JellyfinItem view;
final int version;
const _LatestRow({super.key, required this.view, required this.version});
@override
Widget build(BuildContext context) {
final scope = BrowseScope.of(context);
final title = 'Latest in ${view.name}';
final shape = view.collectionType == 'homevideos' ? CardShape.thumb : CardShape.poster;
return Loader<List<JellyfinItem>>(
version: version,
load: () => scope.load('latest:${view.id}', (c) => c.latest(view.id, limit: 100, groupItems: false)),
showError: false,
loading: _RowSkeleton(title: title, shape: shape),
builder: (context, items) => SignalBuilder(builder: (context) {
final hide = AppSettings.instance.hideReReleases.value;
final cards = latestCards(items, hideReReleases: hide);
if (cards.isEmpty) return const SizedBox.shrink();
return Padding(
padding: const EdgeInsets.only(bottom: 24),
child: MediaRow(
key: ValueKey('latest-${view.id}'),
title: title,
entries: [for (final c in cards) (item: c.item, display: c.display, subtitle: c.subtitle)],
shape: shape,
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
key: ValueKey('show-all-${view.id}'),
// Newest first, like the row.
onPressed: () => scope.onOpen(LibraryRoute(view, sort: 'added')),
child: const Text('Show all'),
),
PopupMenuButton<void>(
key: ValueKey('latest-menu-${view.id}'),
tooltip: 'Row options',
icon: const Icon(Icons.more_vert, size: 18),
itemBuilder: (context) => [
CheckedPopupMenuItem(
key: const ValueKey('hide-rereleases'),
checked: hide,
onTap: () => AppSettings.instance.setHideReReleases(!hide),
child: const Text('Hide re-releases in Latest'),
),
],
),
],
),
),
);
}),
);
}
}
class _RowSkeleton extends StatelessWidget {
final String title;
final CardShape shape;
const _RowSkeleton({required this.title, required this.shape});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SectionHeader(title),
LayoutBuilder(builder: (context, box) {
final fit = fitColumns(box.maxWidth, shape.minWidth);
return Row(
children: [
for (var i = 0; i < fit.cols; i++) ...[
if (i > 0) const SizedBox(width: cardGap),
Expanded(child: SkeletonCard(shape: shape)),
],
],
);
}),
],
),
);
}
}
class SkeletonCard extends StatelessWidget {
final CardShape shape;
const SkeletonCard({super.key, required this.shape});
@override
Widget build(BuildContext context) {
final c = Theme.of(context).colorScheme.surfaceContainerHigh.withValues(alpha: 0.6);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
AspectRatio(
aspectRatio: shape.aspect,
child: DecoratedBox(
decoration: BoxDecoration(color: c, borderRadius: BorderRadius.circular(8))),
),
const SizedBox(height: cardTextHeight),
],
);
}
}
@@ -0,0 +1,163 @@
import 'package:sync_relay_core/core.dart';
import 'format.dart';
// What the Jellyfin home rows show, as pure functions over the API lists
// (tested without widgets).
/// Series not watched for longer than this leave the Continue row, unless
/// nothing newer is left.
const continueMaxAge = Duration(days: 60);
/// Added this long after it first aired: a re-release ([isReRelease]).
const reReleaseAge = Duration(days: 30);
/// One Continue card per series: an episode's series, else the item itself.
String continueKey(JellyfinItem item) => item.seriesId ?? item.id;
DateTime? _later(DateTime? a, DateTime? b) =>
a == null ? b : b == null || !b.isAfter(a) ? a : b;
/// The Continue row: Continue watching ([resume]) and Next up ([nextUp])
/// merged into one card per series, the series watched most recently first.
/// A series shows its resume item when that was the last thing played in
/// it, else its next-up episode. When a series was last played comes from
/// the resume points and from [recentlyPlayed] (finished episodes, which
/// next-up items do not carry). Series untouched for [maxAge] are left out
/// unless nothing else is left; [dismissed] (key -> when) hides a series
/// until it is played again after that.
List<JellyfinItem> mergeContinue({
required List<JellyfinItem> resume,
required List<JellyfinItem> nextUp,
List<JellyfinItem> recentlyPlayed = const [],
Map<String, DateTime> dismissed = const {},
required DateTime now,
Duration maxAge = continueMaxAge,
}) {
final finished = <String, DateTime>{};
for (final p in recentlyPlayed) {
final at = p.userData.lastPlayedDate;
if (at != null) finished[continueKey(p)] = _later(finished[continueKey(p)], at)!;
}
final resumeBy = <String, JellyfinItem>{};
final nextBy = <String, JellyfinItem>{};
final order = <String>[];
for (final r in resume) {
final k = continueKey(r);
final had = resumeBy[k];
if (had == null) order.add(k);
final at = r.userData.lastPlayedDate;
if (had == null || (at != null && at.isAfter(had.userData.lastPlayedDate ?? DateTime(0)))) {
resumeBy[k] = r;
}
}
for (final n in nextUp) {
final k = continueKey(n);
if (nextBy.containsKey(k)) continue;
nextBy[k] = n;
if (!resumeBy.containsKey(k)) order.add(k);
}
final cards = <({JellyfinItem item, DateTime? at, int index})>[];
for (final (i, k) in order.indexed) {
final r = resumeBy[k], n = nextBy[k], done = finished[k];
final resumedAt = r?.userData.lastPlayedDate;
final JellyfinItem item;
if (r != null && (n == null || (resumedAt != null && (done == null || !resumedAt.isBefore(done))))) {
item = r;
} else {
item = n!;
}
final at = _later(resumedAt, done);
final hiddenAt = dismissed[k];
if (hiddenAt != null && (at == null || !at.isAfter(hiddenAt))) continue;
cards.add((item: item, at: at, index: i));
}
cards.sort((a, b) {
if (a.at == null || b.at == null) {
return a.at == b.at ? a.index.compareTo(b.index) : (a.at == null ? 1 : -1);
}
final c = b.at!.compareTo(a.at!);
return c != 0 ? c : a.index.compareTo(b.index);
});
final fresh = [for (final c in cards) if (c.at != null && now.difference(c.at!) <= maxAge) c.item];
return fresh.isNotEmpty ? fresh : [for (final c in cards) c.item];
}
/// Added to the library long ([age]) after it first aired: a re-upload or
/// an old season added late, not news. Unknown dates are never re-releases.
bool isReRelease(JellyfinItem item, {Duration age = reReleaseAge}) {
final aired = item.premiereDate, added = item.dateCreated;
return aired != null && added != null && added.difference(aired) > age;
}
/// One card of a "Latest in X" row: [item] is what selecting or putting it
/// up acts on, [display] gives the picture and title, [subtitle] the line
/// under it.
class LatestCard {
final JellyfinItem item;
final JellyfinItem display;
final String subtitle;
const LatestCard(this.item, this.display, this.subtitle);
}
/// A series to show for [episode]: its id, name and poster.
JellyfinItem seriesOf(JellyfinItem episode) => JellyfinItem(
id: episode.seriesId!,
name: episode.seriesName ?? episode.name,
type: JellyfinItemType.series,
isFolder: true,
imageTags: {
if (episode.seriesPrimaryImageTag != null)
JellyfinImageType.primary.apiName: episode.seriesPrimaryImageTag!,
},
parentBackdropItemId: episode.parentBackdropItemId,
parentBackdropImageTags: episode.parentBackdropImageTags,
);
/// "Latest in X" from the library's newest items (ungrouped, newest
/// first): episodes become one card per series (its poster and name,
/// "S1E1" for a single new episode, else "N new"), in order of the newest
/// episode; movies and other items stay as they are. With [hideReReleases]
/// items that [isReRelease] are left out first.
List<LatestCard> latestCards(List<JellyfinItem> items, {bool hideReReleases = true}) {
final kept = hideReReleases ? items.where((i) => !isReRelease(i)) : items;
final groups = <String, List<JellyfinItem>>{};
final out = <Object>[]; // a JellyfinItem, or a series id (group)
for (final i in kept) {
final sid = i.seriesId;
if (i.kind == JellyfinItemKind.episode && sid != null) {
final g = groups[sid];
if (g == null) {
groups[sid] = [i];
out.add(sid);
} else {
g.add(i);
}
} else {
out.add(i);
}
}
return [
for (final o in out)
if (o is JellyfinItem)
LatestCard(
o,
o,
o.kind == JellyfinItemKind.series && (o.childCount ?? 0) > 1
? '${o.childCount} new'
: cardSubtitle(o),
)
else
_seriesCard(groups[o]!),
];
}
LatestCard _seriesCard(List<JellyfinItem> eps) {
final series = seriesOf(eps.first);
if (eps.length == 1) {
final e = eps.single;
return LatestCard(e, series, e.episodeLabel ?? e.name);
}
return LatestCard(series, series, '${eps.length} new');
}
+87
View File
@@ -0,0 +1,87 @@
import 'package:flutter/material.dart';
import 'package:sync_relay_core/core.dart';
/// Server-side scale steps: nearby sizes share one URL (and one cache
/// entry) instead of fetching per-pixel variants on every resize.
const _widthSteps = [160, 240, 320, 480, 640, 960, 1280, 1920, 2560];
int imageWidthStep(double physicalWidth) {
for (final s in _widthSteps) {
if (s >= physicalWidth) return s;
}
return _widthSteps.last;
}
/// A Jellyfin image sized to its box. The URL carries the image tag and the
/// width step, so Flutter's image cache (keyed by URL) reuses it until the
/// image changes on the server; decode is capped at the same width. Shows
/// [placeholder] (or a tinted box with [icon]) while loading and on error,
/// and fades in when ready.
class JellyfinImage extends StatelessWidget {
final JellyfinClient client;
final JellyfinImageRef? image;
final BoxFit fit;
final IconData icon;
final Alignment alignment;
final Widget? placeholder;
const JellyfinImage({
super.key,
required this.client,
required this.image,
this.fit = BoxFit.cover,
this.icon = Icons.movie_outlined,
this.alignment = Alignment.center,
this.placeholder,
});
@override
Widget build(BuildContext context) {
final ref = image;
final fallback = placeholder ?? _Placeholder(icon: icon);
if (ref == null) return fallback;
return LayoutBuilder(builder: (context, box) {
final dpr = MediaQuery.maybeDevicePixelRatioOf(context) ?? 1;
final w = box.hasBoundedWidth ? box.maxWidth : 480.0;
final step = imageWidthStep(w * dpr);
final url = client.imageUrl(ref, maxWidth: step, quality: 85).toString();
return Image.network(
url,
key: ValueKey(url),
fit: fit,
alignment: alignment,
width: double.infinity,
height: double.infinity,
cacheWidth: step,
filterQuality: FilterQuality.medium,
frameBuilder: (context, child, frame, sync) {
if (sync) return child;
return Stack(fit: StackFit.passthrough, children: [
if (frame == null) fallback,
AnimatedOpacity(
opacity: frame == null ? 0 : 1,
duration: const Duration(milliseconds: 180),
child: child,
),
]);
},
errorBuilder: (context, _, _) => fallback,
);
});
}
}
class _Placeholder extends StatelessWidget {
final IconData icon;
const _Placeholder({required this.icon});
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return ColoredBox(
color: scheme.surfaceContainerHigh,
child: Center(
child: Icon(icon, size: 28, color: scheme.onSurfaceVariant.withValues(alpha: 0.35)),
),
);
}
}
@@ -0,0 +1,279 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:sync_relay_core/core.dart';
import 'browse_scope.dart';
import 'cards.dart';
import 'home_page.dart' show SkeletonCard;
enum LibrarySort {
name('Name', JellyfinSortBy.sortName, false),
added('Date added', JellyfinSortBy.dateCreated, true),
premiere('Release date', JellyfinSortBy.premiereDate, true),
rating('Rating', JellyfinSortBy.communityRating, true),
random('Random', JellyfinSortBy.random, false);
final String label;
final String apiName;
/// Natural direction: newest / best first.
final bool descending;
const LibrarySort(this.label, this.apiName, this.descending);
}
/// Item types and recursion for a library: movie and show libraries list
/// their titles flat; anything else is browsed folder by folder.
({List<String> types, bool recursive}) libraryShape(JellyfinItem folder) =>
switch (folder.collectionType) {
'movies' => (types: const [JellyfinItemType.movie], recursive: true),
'tvshows' => (types: const [JellyfinItemType.series], recursive: true),
'boxsets' => (types: const [JellyfinItemType.boxSet], recursive: true),
_ => (types: const <String>[], recursive: false),
};
const libraryPageSize = 100;
/// One library (or folder) as a grid that fills the width, with sort,
/// unplayed / favourite filters and infinite scroll. The scrollbar spans
/// the full count from the start: not-yet-loaded cells are skeletons and
/// pages load as they come near the viewport.
class JellyfinLibraryPage extends StatefulWidget {
final JellyfinItem folder;
/// A [LibrarySort] name to start with (in its natural direction).
final String? sort;
const JellyfinLibraryPage({super.key, required this.folder, this.sort});
@override
State<JellyfinLibraryPage> createState() => _JellyfinLibraryPageState();
}
class _JellyfinLibraryPageState extends State<JellyfinLibraryPage> {
late LibrarySort _sort =
LibrarySort.values.where((s) => s.name == widget.sort).firstOrNull ?? LibrarySort.name;
late bool _descending = _sort.descending;
bool _unplayed = false;
bool _favorites = false;
final _items = <JellyfinItem>[];
int? _total;
bool _loading = false;
Object? _error;
int _gen = 0;
final _scroll = ScrollController();
BrowseScope? _scope;
@override
void didChangeDependencies() {
super.didChangeDependencies();
final scope = BrowseScope.of(context);
final first = _scope == null;
final changedServer = _scope != null && _scope!.client != scope.client;
_scope = scope;
if (first || changedServer) _reload();
}
@override
void dispose() {
_scroll.dispose();
super.dispose();
}
JellyfinItemQuery get _query {
final shape = libraryShape(widget.folder);
return JellyfinItemQuery(
parentId: widget.folder.id,
includeItemTypes: shape.types,
recursive: shape.recursive,
sortBy: [
// Folders first when browsing a folder tree.
if (!shape.recursive && _sort == LibrarySort.name) 'IsFolder',
_sort.apiName,
if (_sort != LibrarySort.name) JellyfinSortBy.sortName,
],
descending: _descending,
filters: [
if (_unplayed) JellyfinFilter.isUnplayed,
if (_favorites) JellyfinFilter.isFavorite,
],
);
}
void _reload() {
_gen++;
_items.clear();
_total = null;
_error = null;
_loading = false;
if (_scroll.hasClients) _scroll.jumpTo(0);
_loadMore();
}
Future<void> _loadMore() async {
final scope = _scope;
if (scope == null || _loading) return;
if (_total != null && _items.length >= _total!) return;
final gen = _gen;
setState(() {
_loading = true;
_error = null;
});
try {
final page = await scope.client.items(_query.page(_items.length, libraryPageSize));
if (gen != _gen || !mounted) return;
setState(() {
_items.addAll(page.items);
// An empty page before the advertised end: the library shrank.
_total = page.items.isEmpty ? _items.length : page.totalRecordCount;
_loading = false;
});
} catch (e) {
if (gen != _gen || !mounted) return;
if (e is JellyfinException && e.isUnauthorized) {
scope.accounts.markExpired(scope.account.serverId);
}
setState(() {
_loading = false;
_error = e;
});
}
}
void _setSort(LibrarySort s) {
setState(() {
_sort = s;
_descending = s.descending;
});
_reload();
}
CardShape get _shape =>
widget.folder.collectionType == 'homevideos' ? CardShape.thumb : CardShape.poster;
@override
Widget build(BuildContext context) {
final t = Theme.of(context).textTheme;
final total = _total;
final toolbar = Wrap(
spacing: 8,
runSpacing: 8,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Text(widget.folder.name, style: t.titleLarge),
const SizedBox(width: 4),
Text(total == null ? '' : '$total ${total == 1 ? 'item' : 'items'}',
key: const ValueKey('library-count'),
style: t.bodySmall?.copyWith(color: Colors.grey)),
const SizedBox(width: 12),
_SortMenu(sort: _sort, onSelected: _setSort),
if (_sort != LibrarySort.random)
IconButton(
tooltip: _descending ? 'Descending' : 'Ascending',
visualDensity: VisualDensity.compact,
onPressed: () {
setState(() => _descending = !_descending);
_reload();
},
icon: Icon(_descending ? Icons.arrow_downward : Icons.arrow_upward, size: 18),
)
else
IconButton(
tooltip: 'Shuffle again',
visualDensity: VisualDensity.compact,
onPressed: _reload,
icon: const Icon(Icons.shuffle, size: 18),
),
FilterChip(
key: const ValueKey('filter-unplayed'),
label: const Text('Unwatched'),
selected: _unplayed,
visualDensity: VisualDensity.compact,
onSelected: (v) {
setState(() => _unplayed = v);
_reload();
},
),
FilterChip(
key: const ValueKey('filter-favorites'),
label: const Text('Favorites'),
selected: _favorites,
visualDensity: VisualDensity.compact,
onSelected: (v) {
setState(() => _favorites = v);
_reload();
},
),
],
);
final count = total ?? (_loading ? libraryPageSize ~/ 4 : 0);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(padding: const EdgeInsets.only(bottom: 12), child: toolbar),
if (_error != null && _items.isEmpty)
LoadError(error: _error!, onRetry: _loadMore)
else if (total == 0)
const Padding(
padding: EdgeInsets.all(32),
child: Center(child: Text('Nothing here', style: TextStyle(color: Colors.grey))),
)
else
Expanded(
child: Scrollbar(
controller: _scroll,
child: CustomScrollView(
controller: _scroll,
slivers: [
SliverGrid(
gridDelegate: CardGridDelegate(_shape),
delegate: SliverChildBuilderDelegate(
(context, i) {
if (i >= _items.length - 40 && _error == null) {
// Near the loaded edge: fetch the next page.
scheduleMicrotask(_loadMore);
}
if (i >= _items.length) return SkeletonCard(shape: _shape);
return MediaCard(item: _items[i], shape: _shape);
},
childCount: count,
),
),
if (_error != null && _items.isNotEmpty)
SliverToBoxAdapter(child: LoadError(error: _error!, onRetry: _loadMore)),
const SliverToBoxAdapter(child: SizedBox(height: 24)),
],
),
),
),
],
);
}
}
class _SortMenu extends StatelessWidget {
final LibrarySort sort;
final ValueChanged<LibrarySort> onSelected;
const _SortMenu({required this.sort, required this.onSelected});
@override
Widget build(BuildContext context) {
return MenuAnchor(
menuChildren: [
for (final s in LibrarySort.values)
MenuItemButton(
key: ValueKey('sort-${s.name}'),
leadingIcon: Icon(s == sort ? Icons.check : null, size: 18),
onPressed: () => onSelected(s),
child: Text(s.label),
),
],
builder: (context, menu, _) => OutlinedButton.icon(
key: const ValueKey('library-sort'),
onPressed: () => menu.isOpen ? menu.close() : menu.open(),
icon: const Icon(Icons.sort, size: 18),
label: Text(sort.label),
),
);
}
}
@@ -0,0 +1,111 @@
import 'package:flutter/material.dart';
import '../screens/home/ui.dart';
import 'accounts.dart';
import 'browse_scope.dart';
import 'browser.dart';
import 'sign_in_panel.dart';
/// The Library's Jellyfin source: the library browser once a server is
/// linked, else [empty] (default: an inline add-server panel).
class JellyfinPickerSource extends StatefulWidget {
final JellyfinAccounts accounts;
final BrowseController? controller;
final JellyfinSelection? selected;
final ValueChanged<JellyfinSelection> onSelect;
final ValueChanged<JellyfinSelection> onPutUp;
final VoidCallback? onOpenIntegrations;
final BrowseStart? start;
final bool libraryStrip;
/// Shown while no server is linked.
final WidgetBuilder? empty;
const JellyfinPickerSource({
super.key,
this.start,
required this.accounts,
required this.onSelect,
required this.onPutUp,
this.controller,
this.selected,
this.onOpenIntegrations,
this.libraryStrip = true,
this.empty,
});
@override
State<JellyfinPickerSource> createState() => _JellyfinPickerSourceState();
}
class _JellyfinPickerSourceState extends State<JellyfinPickerSource> {
@override
void initState() {
super.initState();
widget.accounts.ensureLoaded();
}
@override
Widget build(BuildContext context) {
return ListenableBuilder(
listenable: widget.accounts,
builder: (context, _) {
final a = widget.accounts;
if (!a.loaded) {
return const Center(child: CircularProgressIndicator(strokeWidth: 2));
}
if (a.isEmpty && widget.empty != null) return widget.empty!(context);
if (a.isEmpty) return _LinkFirst(accounts: a, onOpenIntegrations: widget.onOpenIntegrations);
return JellyfinBrowser(
key: const ValueKey('media-jellyfin'),
accounts: a,
controller: widget.controller,
selected: widget.selected,
onSelect: widget.onSelect,
onPutUp: widget.onPutUp,
onOpenIntegrations: widget.onOpenIntegrations,
start: widget.start,
libraryStrip: widget.libraryStrip,
);
},
);
}
}
class _LinkFirst extends StatelessWidget {
final JellyfinAccounts accounts;
final VoidCallback? onOpenIntegrations;
const _LinkFirst({required this.accounts, this.onOpenIntegrations});
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
key: const ValueKey('media-jellyfin'),
child: Align(
alignment: Alignment.topLeft,
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 560),
child: Panel(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
const Text('Link a Jellyfin server to browse it here. '
'You can manage linked servers in Integrations.',
style: TextStyle(color: Colors.grey)),
const SizedBox(height: kGap),
JellyfinSignInPanel(accounts: accounts),
if (onOpenIntegrations != null) ...[
const SizedBox(height: 8),
Align(
alignment: Alignment.centerLeft,
child: TextButton(
onPressed: onOpenIntegrations, child: const Text('Open Integrations')),
),
],
],
),
),
),
),
);
}
}
+134
View File
@@ -0,0 +1,134 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:sync_relay_core/core.dart';
/// What the player shows, sampled about once a second. [aid] / [sid] are
/// mpv track ids (null = none / subtitles off).
class PlayerSnapshot {
final double position;
final bool paused;
final int? aid;
final int? sid;
const PlayerSnapshot({required this.position, required this.paused, this.aid, this.sid});
@override
String toString() => 'PlayerSnapshot($position, paused: $paused, aid: $aid, sid: $sid)';
}
/// Playing / Progress / Stopped for one item played with this viewer's own
/// account, so Jellyfin's watched state and Continue Watching follow what
/// this viewer saw. Fed a [PlayerSnapshot] every tick ([update]); sends
/// Progress every [interval] and at once on pause, resume, seek (a jump of
/// more than 3 s from where playback should be) or a track change. Reports
/// go out one after another; a failure is logged and handed to [onError]
/// (the session shows one quiet toast) and never touches playback. A 401
/// calls [onUnauthorized] and ends reporting.
class JellyfinProgressReporter {
final JellyfinClient client;
final String itemId;
final JellyfinMediaSource source;
final String playSessionId;
final Duration interval;
final DateTime Function() _clock;
final void Function(Object error)? onError;
final VoidCallback? onUnauthorized;
JellyfinProgressReporter({
required this.client,
required this.itemId,
required this.source,
required this.playSessionId,
this.interval = const Duration(seconds: 10),
this.onError,
this.onUnauthorized,
DateTime Function()? clock,
}) : _clock = clock ?? DateTime.now;
Future<void> _chain = Future.value();
bool _playing = false;
bool _dead = false;
PlayerSnapshot? _last;
DateTime? _lastAt;
DateTime? _lastSentAt;
/// Between [start] and [stop].
bool get active => _playing && !_dead;
/// The report body for [s]: ticks, pause, and mpv's aid/sid mapped to
/// Jellyfin stream indices (-1 = subtitles off).
JellyfinPlaybackReport reportFor(PlayerSnapshot s) => JellyfinPlaybackReport(
itemId: itemId,
mediaSourceId: source.id,
playSessionId: playSessionId,
positionTicks: (s.position * jellyfinTicksPerSecond).round(),
isPaused: s.paused,
audioStreamIndex: s.aid == null
? null
: source.jellyfinIndexForMpvTrack(JellyfinStreamType.audio, s.aid!),
subtitleStreamIndex: s.sid == null
? -1
: source.jellyfinIndexForMpvTrack(JellyfinStreamType.subtitle, s.sid!),
);
/// Playing: once the media is open. Again after [stop] (played to the
/// end, then someone seeked back) starts reporting anew.
void start(PlayerSnapshot s) {
if (_playing || _dead) return;
_playing = true;
_mark(s);
_send('Playing', () => client.reportPlaybackStart(reportFor(s)));
}
/// Progress when due (see the class doc).
void update(PlayerSnapshot s, {bool force = false}) {
if (!_playing || _dead) return;
final last = _last, lastAt = _lastAt, sentAt = _lastSentAt;
final now = _clock();
var due = force || last == null || lastAt == null || sentAt == null;
if (!due) {
final elapsed = now.difference(lastAt).inMilliseconds / 1000;
final expected = last.position + (last.paused ? 0 : elapsed);
due = s.paused != last.paused ||
s.aid != last.aid ||
s.sid != last.sid ||
(s.position - expected).abs() > 3 ||
now.difference(sentAt) >= interval;
}
_last = s;
_lastAt = now;
if (!due) return;
_lastSentAt = now;
_send('Progress', () => client.reportPlaybackProgress(reportFor(s)));
}
/// Stopped (swap, leave, kick, window close, played to the end).
/// Completes when it and everything before it went out.
Future<void> stop(PlayerSnapshot s) {
if (!_playing || _dead) return _chain;
_playing = false;
_send('Stopped', () => client.reportPlaybackStopped(reportFor(s)));
return _chain;
}
void _mark(PlayerSnapshot s) {
_last = s;
_lastAt = _lastSentAt = _clock();
}
void _send(String what, Future<void> Function() call) {
_chain = _chain.then((_) async {
if (_dead) return;
try {
await call().timeout(const Duration(seconds: 10));
} catch (e) {
debugPrint('jellyfin $what report for $itemId failed: $e');
if (e is JellyfinException && e.isUnauthorized) {
_dead = true;
onUnauthorized?.call();
}
onError?.call(e);
}
});
}
}
@@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
import 'package:sync_relay_core/core.dart';
import 'browse_scope.dart';
import 'cards.dart';
/// Search results grouped by kind: shows and movies as posters, episodes
/// as thumbnails, each a grid that fills the width.
class JellyfinSearchResults extends StatelessWidget {
final String term;
const JellyfinSearchResults({super.key, required this.term});
@override
Widget build(BuildContext context) {
final scope = BrowseScope.of(context);
return Loader<List<JellyfinItem>>(
key: ValueKey('search-$term-${scope.account.serverId}'),
load: () => scope.load('search:$term', (c) => c.search(term, limit: 80)),
builder: (context, items) {
if (items.isEmpty) {
return Padding(
padding: const EdgeInsets.all(32),
child: Center(
child: Text('Nothing found for "$term"',
key: const ValueKey('search-empty'),
style: const TextStyle(color: Colors.grey)),
),
);
}
final groups = [
('Shows', CardShape.poster, items.where((i) => i.kind == JellyfinItemKind.series).toList()),
('Movies', CardShape.poster, items.where((i) => i.kind == JellyfinItemKind.movie).toList()),
('Episodes', CardShape.thumb, items.where((i) => i.kind == JellyfinItemKind.episode).toList()),
(
'Other',
CardShape.poster,
items
.where((i) => !const [
JellyfinItemKind.series,
JellyfinItemKind.movie,
JellyfinItemKind.episode
].contains(i.kind))
.toList()
),
].where((g) => g.$3.isNotEmpty);
return CustomScrollView(
key: const ValueKey('search-results'),
slivers: [
for (final (title, shape, list) in groups) ...[
SliverToBoxAdapter(child: SectionHeader(title, count: '${list.length}')),
SliverGrid(
gridDelegate: CardGridDelegate(shape),
delegate: SliverChildBuilderDelegate(
(context, i) => MediaCard(
item: list[i],
shape: shape,
seriesFirst: shape == CardShape.thumb,
),
childCount: list.length,
),
),
const SliverToBoxAdapter(child: SizedBox(height: 16)),
],
],
);
},
);
}
}
@@ -0,0 +1,452 @@
import 'package:flutter/material.dart';
import 'package:sync_relay_core/core.dart';
import 'browse_scope.dart';
import 'cards.dart' show doubleClickTime;
import 'format.dart';
import 'images.dart';
/// Series page: backdrop + logo header with overview and the smart "next
/// unwatched" button, a season selector and the season's episodes as dense
/// rows (several columns on wide windows).
class JellyfinSeriesPage extends StatefulWidget {
final JellyfinItem series;
final String? seasonId;
const JellyfinSeriesPage({super.key, required this.series, this.seasonId});
@override
State<JellyfinSeriesPage> createState() => _JellyfinSeriesPageState();
}
typedef _SeriesData = ({JellyfinItem series, List<JellyfinItem> seasons, JellyfinItem? next});
class _JellyfinSeriesPageState extends State<JellyfinSeriesPage> {
String? _seasonId;
@override
void initState() {
super.initState();
_seasonId = widget.seasonId;
}
Future<_SeriesData> _load(BrowseScope scope) async {
final id = widget.series.id;
final results = await Future.wait<Object?>([
scope.load('item:$id', (c) => c.item(id)),
scope.load('seasons:$id', (c) => c.seasons(id)),
scope
.load('next:$id', (c) => c.nextUp(seriesId: id, limit: 1, enableResumable: true))
.then<List<JellyfinItem>?>((v) => v, onError: (_) => null),
]);
final next = results[2] as List<JellyfinItem>?;
return (
series: results[0] as JellyfinItem,
seasons: results[1] as List<JellyfinItem>,
next: next == null || next.isEmpty ? null : next.first,
);
}
/// Season to show first: the one holding the next episode, else the first
/// with something unwatched, else the first.
String? _defaultSeason(_SeriesData d) {
if (d.seasons.isEmpty) return null;
final nextSeason = d.next?.seasonId;
if (nextSeason != null && d.seasons.any((s) => s.id == nextSeason)) return nextSeason;
return (d.seasons.where((s) => !s.userData.played).firstOrNull ?? d.seasons.first).id;
}
@override
Widget build(BuildContext context) {
final scope = BrowseScope.of(context);
return Loader<_SeriesData>(
key: ValueKey('series-${widget.series.id}-${scope.account.serverId}'),
version: scope.controller.version,
load: () => _load(scope),
builder: (context, d) {
final seasonId = (_seasonId != null && d.seasons.any((s) => s.id == _seasonId))
? _seasonId
: _defaultSeason(d);
return CustomScrollView(
key: PageStorageKey('series-${widget.series.id}'),
slivers: [
SliverToBoxAdapter(child: _SeriesHeader(data: d)),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Wrap(
spacing: 8,
runSpacing: 8,
children: [
for (final s in d.seasons)
ChoiceChip(
key: ValueKey('season-${s.id}'),
label: Text(s.name),
avatar: s.userData.played
? const Icon(Icons.check, size: 16)
: null,
showCheckmark: false,
selected: s.id == seasonId,
onSelected: (_) => setState(() => _seasonId = s.id),
tooltip: s.userData.unplayedItemCount != null
? '${s.userData.unplayedItemCount} unwatched'
: null,
),
],
),
),
),
if (seasonId != null)
_EpisodeList(
key: ValueKey('episodes-$seasonId'),
seriesId: widget.series.id,
seasonId: seasonId,
version: scope.controller.version,
),
const SliverToBoxAdapter(child: SizedBox(height: 24)),
],
);
},
);
}
}
class _SeriesHeader extends StatefulWidget {
final _SeriesData data;
const _SeriesHeader({required this.data});
@override
State<_SeriesHeader> createState() => _SeriesHeaderState();
}
class _SeriesHeaderState extends State<_SeriesHeader> {
bool _more = false;
@override
Widget build(BuildContext context) {
final scope = BrowseScope.of(context);
final theme = Theme.of(context);
final t = theme.textTheme;
final s = widget.data.series;
final next = widget.data.next;
final logo = s.image(JellyfinImageType.logo);
final meta = [
if (s.productionYear != null) '${s.productionYear}',
if (s.officialRating != null) s.officialRating!,
if (s.communityRating != null) '★ ${s.communityRating!.toStringAsFixed(1)}',
if (widget.data.seasons.isNotEmpty)
'${widget.data.seasons.length} ${widget.data.seasons.length == 1 ? 'season' : 'seasons'}',
if ((s.userData.unplayedItemCount ?? 0) > 0) '${s.userData.unplayedItemCount} unwatched',
].join(' · ');
final String nextLabel;
if (next == null) {
nextLabel = 'All watched';
} else if (next.userData.isResumable) {
nextLabel = 'Resume ${next.episodeLabel ?? next.name}';
} else {
nextLabel = 'Next up: ${next.episodeLabel ?? ''} ${next.name}'.trim();
}
final selectedNext = next != null && scope.selectedItemId == next.id;
return LayoutBuilder(builder: (context, box) {
final h = (box.maxWidth * 0.28).clamp(220.0, 380.0);
return ClipRRect(
borderRadius: BorderRadius.circular(12),
child: SizedBox(
height: h,
child: Stack(
fit: StackFit.expand,
children: [
JellyfinImage(
client: scope.client,
image: s.image(JellyfinImageType.backdrop),
alignment: Alignment.topCenter,
icon: Icons.live_tv_outlined,
),
DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
theme.colorScheme.surface.withValues(alpha: 0.96),
theme.colorScheme.surface.withValues(alpha: 0.75),
theme.colorScheme.surface.withValues(alpha: 0.1),
],
stops: const [0, 0.45, 1],
),
),
),
Padding(
padding: const EdgeInsets.all(24),
child: Align(
alignment: Alignment.centerLeft,
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 720),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (logo != null)
SizedBox(
height: 72,
width: 320,
child: JellyfinImage(
client: scope.client,
image: logo,
fit: BoxFit.contain,
alignment: Alignment.centerLeft,
placeholder: Align(
alignment: Alignment.centerLeft,
child: Text(s.name, style: t.headlineMedium)),
),
)
else
Text(s.name, style: t.headlineMedium),
const SizedBox(height: 8),
Text(meta, style: t.bodyMedium?.copyWith(color: Colors.grey)),
if (s.overview != null) ...[
const SizedBox(height: 8),
GestureDetector(
onTap: () => setState(() => _more = !_more),
child: Text(s.overview!,
maxLines: _more ? null : 3,
overflow: _more ? null : TextOverflow.ellipsis,
style: t.bodyMedium),
),
],
const SizedBox(height: 14),
Wrap(spacing: 8, runSpacing: 8, children: [
FilledButton.icon(
key: const ValueKey('series-next'),
onPressed: next == null ? null : () => scope.onSelect(next),
icon: Icon(selectedNext ? Icons.check : Icons.play_arrow),
label: Text(nextLabel, overflow: TextOverflow.ellipsis),
),
]),
],
),
),
),
),
),
],
),
),
);
});
}
}
/// Episodes of one season as a lazy grid of rows: one column, more past
/// ~760 px each.
class _EpisodeList extends StatefulWidget {
final String seriesId;
final String seasonId;
final int version;
const _EpisodeList(
{super.key, required this.seriesId, required this.seasonId, required this.version});
@override
State<_EpisodeList> createState() => _EpisodeListState();
}
class _EpisodeListState extends State<_EpisodeList> {
Future<List<JellyfinItem>>? _future;
List<JellyfinItem>? _last;
@override
void didUpdateWidget(_EpisodeList old) {
super.didUpdateWidget(old);
if (old.version != widget.version) _future = null;
}
void _load(BrowseScope scope) => _future = scope.load('episodes:${widget.seriesId}:${widget.seasonId}',
(c) => c.episodes(widget.seriesId, seasonId: widget.seasonId));
@override
Widget build(BuildContext context) {
final scope = BrowseScope.of(context);
if (_future == null) _load(scope);
return FutureBuilder<List<JellyfinItem>>(
future: _future,
builder: (context, snap) {
if (snap.hasError) {
return SliverToBoxAdapter(
child: LoadError(error: snap.error!, onRetry: () => setState(() => _load(scope))));
}
final eps = snap.data ?? _last;
if (snap.data != null) _last = snap.data;
if (eps == null) {
return const SliverToBoxAdapter(
child: Padding(
padding: EdgeInsets.all(32),
child: Center(child: CircularProgressIndicator(strokeWidth: 2)),
),
);
}
if (eps.isEmpty) {
return const SliverToBoxAdapter(
child: Padding(
padding: EdgeInsets.all(24),
child: Text('No episodes', style: TextStyle(color: Colors.grey)),
),
);
}
return SliverGrid(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 760,
mainAxisExtent: episodeTileHeight,
crossAxisSpacing: 12,
mainAxisSpacing: 8,
),
delegate: SliverChildBuilderDelegate(
(context, i) => EpisodeTile(episode: eps[i]),
childCount: eps.length,
),
);
},
);
}
}
const episodeTileHeight = 6 + 176 * 9 / 16 + 6 + 2;
/// One episode: thumbnail with progress, number + title, runtime / air
/// date, two lines of overview. Click selects it (details panel), a second
/// click soon after puts it up.
class EpisodeTile extends StatefulWidget {
final JellyfinItem episode;
const EpisodeTile({super.key, required this.episode});
@override
State<EpisodeTile> createState() => _EpisodeTileState();
}
class _EpisodeTileState extends State<EpisodeTile> {
bool _focus = false;
bool _hover = false;
DateTime? _lastTap;
void _tap(BrowseScope scope) {
final now = DateTime.now();
final last = _lastTap;
_lastTap = now;
if (last != null && now.difference(last) < doubleClickTime) {
_lastTap = null;
scope.onPutUp(widget.episode);
} else {
scope.onSelect(widget.episode);
}
}
@override
Widget build(BuildContext context) {
final scope = BrowseScope.of(context);
final theme = Theme.of(context);
final scheme = theme.colorScheme;
final t = theme.textTheme;
final e = widget.episode;
final selected = scope.selectedItemId == e.id;
final played = e.userData.played;
final progress = progressOf(e);
final meta = [
?e.episodeLabel,
?timeLeft(e),
if (timeLeft(e) == null && e.runTime != null) formatRuntime(e.runTime!),
if (e.premiereDate != null)
'${e.premiereDate!.year}-${e.premiereDate!.month.toString().padLeft(2, '0')}-${e.premiereDate!.day.toString().padLeft(2, '0')}',
].join(' · ');
return Material(
color: selected
? scheme.primaryContainer.withValues(alpha: 0.35)
: _hover || _focus
? scheme.surfaceContainerHigh
: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(
color: selected ? scheme.primary : _focus ? scheme.onSurface : Colors.transparent,
width: 1.5),
),
clipBehavior: Clip.antiAlias,
child: InkWell(
key: ValueKey('episode-${e.id}'),
focusColor: Colors.transparent,
onTap: () => _tap(scope),
onHover: (h) => setState(() => _hover = h),
onFocusChange: (f) => setState(() => _focus = f),
child: Padding(
padding: const EdgeInsets.all(6),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 176,
child: AspectRatio(
aspectRatio: 16 / 9,
child: ClipRRect(
borderRadius: BorderRadius.circular(6),
child: Stack(fit: StackFit.expand, children: [
JellyfinImage(
client: scope.client,
image: e.ownImage(JellyfinImageType.primary) ??
e.image(JellyfinImageType.thumb) ??
e.image(JellyfinImageType.backdrop),
icon: Icons.tv_outlined,
),
if (progress > 0)
Positioned(
left: 0,
right: 0,
bottom: 0,
child: LinearProgressIndicator(
value: progress, minHeight: 3, backgroundColor: Colors.black54),
),
if (played || selected)
Positioned(
top: 4,
right: 4,
child: CircleAvatar(
radius: 10,
backgroundColor: selected ? scheme.primary : const Color(0xCC1B5E20),
child: Icon(Icons.check,
size: 13, color: selected ? scheme.onPrimary : Colors.white),
),
),
]),
),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
e.indexNumber == null ? e.name : '${e.indexNumber}. ${e.name}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: t.titleSmall?.copyWith(
color: selected ? scheme.primary : played ? Colors.grey : null,
),
),
Text(meta, style: t.bodySmall?.copyWith(color: Colors.grey)),
if (e.overview != null) ...[
const SizedBox(height: 4),
Text(e.overview!,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: t.bodySmall?.copyWith(color: scheme.onSurfaceVariant)),
],
],
),
),
],
),
),
),
);
}
}
@@ -0,0 +1,354 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:sync_relay_core/core.dart';
import '../screens/home/picked_media.dart';
import 'accounts.dart';
import 'format.dart';
// Jellyfin media in a session: turning picks and pasted links into refs
// (the wire carries only `{serverId, itemId, ...}`), the hosted server's
// relay credential, and how this viewer plays a ref (own account, relay,
// or neither).
/// The wire ref of a browser pick.
JellyfinRef refForPick(JellyfinMedia m) => JellyfinRef(
serverId: m.serverId,
itemId: m.itemId,
mediaSourceId: m.mediaSourceId,
title: m.title,
subtitle: m.subtitle.isEmpty ? null : m.subtitle,
runtimeSecs: m.runtime == null ? null : m.runtime!.inMilliseconds / 1000,
);
/// The `swap` media for a browser pick: a ref, never a URL.
HelloMedia mediaForPick(JellyfinMedia m) => HelloMedia(
name: m.title.isEmpty ? 'Jellyfin item' : m.title,
size: 0,
duration: 0,
jellyfin: refForPick(m));
/// The pick a ref stands for on a linked server (for highlighting it in
/// the browser); null when this install has no account there.
JellyfinMedia? pickForRef(JellyfinAccounts accounts, JellyfinRef ref) {
final a = accounts.account(ref.serverId);
if (a == null) return null;
return JellyfinMedia(
serverId: ref.serverId,
baseUrl: a.baseUrl,
itemId: ref.itemId,
mediaSourceId: ref.mediaSourceId,
title: ref.title,
subtitle: ref.subtitle ?? '',
runtime: ref.runtimeSecs == null
? null
: Duration(milliseconds: (ref.runtimeSecs! * 1000).round()),
);
}
/// The ref in a `state.media` map, or null for other media.
JellyfinRef? refOfStateMedia(Map<String, Object?>? media) {
final jf = media?['jellyfin'];
if (jf is! Map) return null;
try {
final ref = JellyfinRef.fromJson(Map<String, Object?>.from(jf));
return ref.serverId.isEmpty || ref.itemId.isEmpty ? null : ref;
} catch (_) {
return null;
}
}
/// Same item (server, item, source); titles may differ.
bool sameJellyfinItem(JellyfinRef? a, JellyfinRef? b) =>
a != null &&
b != null &&
a.serverId == b.serverId &&
a.itemId == b.itemId &&
a.mediaSourceId == b.mediaSourceId;
final _sourceId = RegExp(r'^[A-Za-z0-9-]{1,64}$');
/// What a pasted link becomes on the wire. A recognised Jellyfin item link
/// (`parseJellyfinLink`) becomes a ref when its ServerId is known (in the
/// link or from `/System/Info/Public`); its `api_key` is never shared then.
/// Anything else, or a server that does not answer, stays a plain link.
/// The title comes from this install's account on that server when there
/// is one.
Future<HelloMedia> mediaForLink(JellyfinAccounts accounts, String url,
{Duration timeout = const Duration(seconds: 6)}) async {
final plain = HelloMedia(name: linkName(url), size: 0, duration: 0, url: url);
final link = parseJellyfinLink(url);
if (link == null) return plain;
await accounts.ensureLoaded();
var serverId = link.serverId;
if (serverId == null || serverId.isEmpty) {
final probe = accounts.anonymousClient(link.baseUrl);
try {
serverId = (await probe.publicInfo().timeout(timeout)).id;
} catch (e) {
debugPrint('jellyfin link: no server id from ${link.baseUrl}: $e');
return plain;
} finally {
probe.close();
}
if (serverId.isEmpty) return plain;
}
final src = Uri.tryParse(url)?.queryParameters['mediaSourceId'];
var ref = JellyfinRef(
serverId: serverId,
itemId: link.itemId,
mediaSourceId: src != null && _sourceId.hasMatch(src) && src != link.itemId ? src : null,
);
final account = accounts.account(serverId);
final client = accounts.clientFor(serverId);
if (account != null && client != null) {
try {
final pick = pickFor(account, await client.item(link.itemId).timeout(timeout));
ref = refForPick(JellyfinMedia(
serverId: serverId,
baseUrl: account.baseUrl,
itemId: link.itemId,
mediaSourceId: ref.mediaSourceId,
title: pick.title,
subtitle: pick.subtitle,
runtime: pick.runtime,
));
} catch (e) {
if (e is JellyfinException && e.isUnauthorized) accounts.markExpired(serverId);
}
}
return HelloMedia(
name: ref.title.isEmpty ? 'Jellyfin item' : ref.title, size: 0, duration: 0, jellyfin: ref);
}
/// Display name for a link: last path segment, decoded, never the query
/// (Jellyfin-style links carry api_key there and names are broadcast).
String linkName(String url) {
final uri = Uri.tryParse(url.trim());
if (uri == null) return 'link';
final segs = uri.pathSegments.where((s) => s.isNotEmpty).toList();
if (segs.isNotEmpty) {
try {
return Uri.decodeComponent(segs.last);
} catch (_) {
return segs.last;
}
}
return uri.host.isNotEmpty ? uri.host : 'link';
}
/// The hosted server's relay login: the account for [serverId] (the
/// hosted item's server), else the default account, when its token is not
/// known to be expired. The device fields are exactly the ones this app
/// sends with that token ([JellyfinAccounts.clientInfo]): Jellyfin rewrites
/// a token's device from the request header, so the relay must not differ.
JellyfinCredential? relayCredentialFor(JellyfinAccounts accounts, {String? serverId}) {
bool usable(JellyfinAccount? a) =>
a != null && accounts.statusOf(a.serverId) != JellyfinAccountStatus.expired;
var a = serverId == null ? null : accounts.account(serverId);
if (!usable(a)) a = accounts.defaultAccount;
if (!usable(a)) return null;
final info = accounts.clientInfo;
return JellyfinCredential(
baseUrl: a!.baseUrl,
serverId: a.serverId,
token: a.token,
client: info.client,
device: info.device,
deviceId: info.deviceId,
version: info.version,
);
}
/// `/j/...` URL of the current Jellyfin media on the host this client
/// dialled, built like the `/m/...` one.
String relayUrlFor(String host, int port, String token, String itemId) =>
'http://$host:$port/j/default/$token/${Uri.encodeComponent(itemId)}';
// ---------------------------------------------------------------- playing
/// How this viewer plays a ref.
sealed class JellyfinPlan {
const JellyfinPlan();
/// `report.jf` for this plan; null when it plays nothing.
bool? get jfAccess;
}
/// Direct play with this install's account: the static stream, the token
/// in [headers] (not in the URL), progress reported per [info].
final class OwnPlay extends JellyfinPlan {
final JellyfinClient client;
final JellyfinAccount account;
final JellyfinPlaybackInfo info;
final JellyfinMediaSource source;
/// Full item (series, episode numbers); null if that call failed.
final JellyfinItem? item;
final String url;
final Map<String, String> headers;
const OwnPlay({
required this.client,
required this.account,
required this.info,
required this.source,
required this.item,
required this.url,
required this.headers,
});
@override
bool get jfAccess => true;
}
/// Through the server: its own Jellyfin login (`/j/...`) or the member
/// who put it up (`/r/...`).
final class RelayPlay extends JellyfinPlan {
final String url;
const RelayPlay(this.url);
@override
bool get jfAccess => false;
}
/// Neither: no (usable) account here and nothing relays it.
final class BlockedPlay extends JellyfinPlan {
final String serverId;
/// Known only when this install has (had) an account there.
final String? serverName;
/// Why the own account did not work, if there is one.
final String? problem;
const BlockedPlay({required this.serverId, this.serverName, this.problem});
@override
bool? get jfAccess => null;
String get message {
final where = serverName == null || serverName!.isEmpty ? 'that Jellyfin server' : serverName!;
final base = 'No Jellyfin account here for $where, and nobody is passing it on';
return problem == null ? base : '$base ($problem)';
}
}
/// Picks how to play [ref]: this install's account first (PlaybackInfo
/// with its own user; a 401 marks the account expired), else the server's
/// own Jellyfin login ([relay] = `state.relay`, [relayUrl] this member's
/// `/j/...` URL), else the member who put it up ([memberRelayUrl], the
/// `/r/...` URL when `media.relay` is set), else [BlockedPlay].
Future<JellyfinPlan> resolveJellyfinRef(
JellyfinAccounts accounts,
JellyfinRef ref, {
required bool relay,
required String? relayUrl,
String? memberRelayUrl,
Duration timeout = const Duration(seconds: 15),
}) async {
await accounts.ensureLoaded();
final account = accounts.account(ref.serverId);
String? problem;
if (account != null && accounts.statusOf(ref.serverId) == JellyfinAccountStatus.expired) {
problem = 'your sign-in expired, reconnect it in Integrations';
} else if (account != null) {
final client = accounts.clientFor(ref.serverId)!;
try {
final infoF = client.playbackInfo(ref.itemId, mediaSourceId: ref.mediaSourceId).timeout(timeout);
final itemF = client
.item(ref.itemId)
.timeout(timeout)
.then<JellyfinItem?>((i) => i, onError: (Object _) => null);
final info = await infoF;
final item = await itemF;
final source = (ref.mediaSourceId == null
? null
: info.mediaSources.where((s) => s.id == ref.mediaSourceId).firstOrNull) ??
info.primarySource;
if (source == null) throw const JellyfinException('no playable source');
return OwnPlay(
client: client,
account: account,
info: info,
source: source,
item: item,
url: client.shareableStreamUrl(ref.itemId, mediaSourceId: source.id).toString(),
headers: client.authHeaders,
);
} catch (e) {
debugPrint('jellyfin resolve ${ref.itemId} with own account failed: $e');
if (e is JellyfinException && e.isUnauthorized) {
accounts.markExpired(ref.serverId);
problem = 'your sign-in expired, reconnect it in Integrations';
} else if (e is JellyfinException && e.statusCode == 404) {
problem = 'your account cannot see this item';
} else {
problem = 'could not reach it';
}
}
}
if (relay && relayUrl != null && relayUrl.isNotEmpty) return RelayPlay(relayUrl);
if (memberRelayUrl != null && memberRelayUrl.isNotEmpty) return RelayPlay(memberRelayUrl);
return BlockedPlay(serverId: ref.serverId, serverName: account?.displayName, problem: problem);
}
// ------------------------------------------------------------ next episode
/// A neighbouring episode as this viewer can put it up.
class EpisodeLink {
final JellyfinItem item;
final JellyfinMedia pick;
const EpisodeLink(this.item, this.pick);
/// "S1E7 Battle with the Heroes".
String get label => [?item.episodeLabel, item.name].join(' ');
}
/// The episodes before and after the one [plan] plays, with its account.
/// Both null for movies, at the ends of a series, or on failure.
Future<({EpisodeLink? previous, EpisodeLink? next})> findAdjacentEpisodes(OwnPlay plan,
{Duration timeout = const Duration(seconds: 15)}) async {
const none = (previous: null, next: null);
final item = plan.item;
if (item == null || item.kind != JellyfinItemKind.episode || item.seriesId == null) return none;
try {
final around = await plan.client.adjacentEpisodes(item).timeout(timeout);
EpisodeLink? link(JellyfinItem? i) => i == null ? null : EpisodeLink(i, pickFor(plan.account, i));
return (previous: link(around.previous), next: link(around.next));
} catch (e) {
debugPrint('episode lookup failed: $e');
return none;
}
}
/// When the floating "Next episode" pill appears: at [endingStart] (the
/// ED: Jellyfin Outro segment or ED-titled chapter, see `endingRange`)
/// when one is known inside [runtime], else the last two minutes of
/// [runtime] (all of it for shorter media).
Duration nextPromptStart(JellyfinItem? item, Duration runtime, {Duration? endingStart}) {
if (endingStart != null && endingStart > Duration.zero && endingStart < runtime) {
return endingStart;
}
const lead = Duration(minutes: 2);
return runtime > lead ? runtime - lead : Duration.zero;
}
/// Who performs the auto-advance swap, so several counting clients do not
/// swap twice: the lowest member id among members that play the item with
/// their own account (`jf[serverId] == true`, not stale, not out). Only what every
/// client sees in `state` decides (who put the item up is not in it: a
/// late joiner never saw that swap), so all clients agree.
String? autoAdvanceSwapper(List<MemberView> members, String serverId) {
final eligible = [
for (final m in members)
if (!m.stale && !m.out && m.jf[serverId] == true) m
];
if (eligible.isEmpty) return null;
eligible.sort((a, b) => compareMemberIds(a.id, b.id));
return eligible.first.id;
}
/// "m2" before "m10": numeric suffix order, then plain text.
int compareMemberIds(String a, String b) {
final na = int.tryParse(a.replaceFirst(RegExp(r'^\D+'), ''));
final nb = int.tryParse(b.replaceFirst(RegExp(r'^\D+'), ''));
if (na != null && nb != null && na != nb) return na.compareTo(nb);
return a.compareTo(b);
}
+182
View File
@@ -0,0 +1,182 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:sync_relay_core/core.dart';
import 'accounts.dart';
enum JellyfinSignInStep {
/// Typing the server address.
address,
probing,
/// Server found: choose Quick Connect or password.
method,
/// Code shown, polling until the user approves it in Jellyfin.
quickConnect,
password,
signingIn,
/// Linked; [JellyfinSignIn.linked] is the account.
done,
}
/// Add-server / reconnect flow: address -> /System/Info/Public -> Quick
/// Connect (code + poll, cancellable) or username/password -> linked in
/// [JellyfinAccounts]. UI-free so tests drive it with a fake transport.
class JellyfinSignIn extends ChangeNotifier {
JellyfinSignIn(this.accounts, {this.pollInterval = const Duration(seconds: 3), this.reconnect});
final JellyfinAccounts accounts;
final Duration pollInterval;
/// Set when re-signing an expired account: the address is prefilled.
final JellyfinAccount? reconnect;
JellyfinSignInStep _step = JellyfinSignInStep.address;
String? _error;
JellyfinServerInfo? _server;
bool _quickConnectAvailable = false;
String? _code;
JellyfinAccount? _linked;
JellyfinClient? _client;
int _gen = 0;
bool _disposed = false;
JellyfinSignInStep get step => _step;
String? get error => _error;
JellyfinServerInfo? get server => _server;
String? get baseUrl => _client?.baseUrl;
bool get quickConnectAvailable => _quickConnectAvailable;
/// The Quick Connect code to show.
String? get code => _code;
JellyfinAccount? get linked => _linked;
bool get busy =>
_step == JellyfinSignInStep.probing || _step == JellyfinSignInStep.signingIn;
void _set(JellyfinSignInStep s, {String? error}) {
_step = s;
_error = error;
if (!_disposed) notifyListeners();
}
/// Checks [input] is a Jellyfin server and whether Quick Connect is on.
Future<void> probe(String input) async {
final url = normalizeServerUrl(input);
if (url == null) {
_set(JellyfinSignInStep.address, error: 'Enter the server address, e.g. jellyfin.example.com');
return;
}
final gen = ++_gen;
_client?.close();
final client = _client = accounts.anonymousClient(url);
_set(JellyfinSignInStep.probing);
try {
final info = await client.publicInfo();
if (gen != _gen) return;
if (info.id.isEmpty) throw const JellyfinException('not a Jellyfin server');
if (reconnect != null && info.id != reconnect!.serverId) {
_set(JellyfinSignInStep.address,
error: 'That address is a different Jellyfin server (${info.name}).');
return;
}
var qc = false;
try {
qc = await client.quickConnectEnabled();
} catch (_) {}
if (gen != _gen) return;
_server = info;
_quickConnectAvailable = qc;
_set(JellyfinSignInStep.method);
} catch (e) {
if (gen != _gen) return;
_set(JellyfinSignInStep.address, error: 'No Jellyfin server at $url (${_short(e)})');
}
}
/// Starts Quick Connect: shows the code, polls until approved, links.
Future<void> startQuickConnect() async {
final client = _client;
if (client == null || _server == null) return;
final gen = ++_gen;
_code = null;
_set(JellyfinSignInStep.quickConnect);
try {
final session = await client.quickConnectInitiate();
if (gen != _gen) return;
_code = session.code;
_set(JellyfinSignInStep.quickConnect);
final auth = await client.waitForQuickConnect(session,
interval: pollInterval, isCancelled: () => gen != _gen || _disposed);
if (auth == null || gen != _gen) return;
await _finish(auth);
} on TimeoutException {
if (gen == _gen) _set(JellyfinSignInStep.method, error: 'The code expired. Try again.');
} catch (e) {
if (gen != _gen) return;
final msg = e is JellyfinException && e.isUnauthorized
? 'Quick Connect is turned off on this server. Use your password.'
: e is JellyfinException && e.statusCode == 404
? 'The code expired. Try again.'
: 'Quick Connect failed: ${_short(e)}';
_set(JellyfinSignInStep.method, error: msg);
}
}
/// Stops waiting for the Quick Connect code.
void cancel() {
_gen++;
_code = null;
_set(_server == null ? JellyfinSignInStep.address : JellyfinSignInStep.method);
}
void usePassword() => _set(JellyfinSignInStep.password);
/// Back to the address field.
void changeAddress() {
_gen++;
_server = null;
_code = null;
_set(JellyfinSignInStep.address);
}
Future<void> signIn(String username, String password) async {
final client = _client;
if (client == null) return;
if (username.trim().isEmpty) {
_set(JellyfinSignInStep.password, error: 'Enter your Jellyfin username');
return;
}
final gen = ++_gen;
_set(JellyfinSignInStep.signingIn);
try {
final auth = await client.authenticateByName(username.trim(), password);
if (gen != _gen) return;
await _finish(auth);
} catch (e) {
if (gen != _gen) return;
_set(JellyfinSignInStep.password,
error: e is JellyfinException && e.isUnauthorized
? 'Wrong username or password'
: 'Sign-in failed: ${_short(e)}');
}
}
Future<void> _finish(JellyfinAuthResult auth) async {
final server = _server!;
if (auth.accessToken.isEmpty) throw const JellyfinException('no token in the answer');
_linked = await accounts.link(baseUrl: _client!.baseUrl, server: server, auth: auth);
_code = null;
_set(JellyfinSignInStep.done);
}
static String _short(Object e) {
final s = '$e'.replaceFirst(RegExp(r'^JellyfinException( \d+)?: '), '');
return s.length > 140 ? '${s.substring(0, 140)}…' : s;
}
@override
void dispose() {
_disposed = true;
_gen++;
_client?.close();
super.dispose();
}
}
@@ -0,0 +1,306 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../screens/home/ui.dart';
import 'accounts.dart';
import 'sign_in.dart';
/// Add a Jellyfin server (or reconnect an expired one): address, then Quick
/// Connect (big code, approve it in Jellyfin) or username and password.
class JellyfinSignInPanel extends StatefulWidget {
final JellyfinAccounts accounts;
final JellyfinAccount? reconnect;
final ValueChanged<JellyfinAccount>? onDone;
final VoidCallback? onCancel;
final Duration pollInterval;
const JellyfinSignInPanel({
super.key,
required this.accounts,
this.reconnect,
this.onDone,
this.onCancel,
this.pollInterval = const Duration(seconds: 3),
});
@override
State<JellyfinSignInPanel> createState() => _JellyfinSignInPanelState();
}
class _JellyfinSignInPanelState extends State<JellyfinSignInPanel> {
late final JellyfinSignIn _flow =
JellyfinSignIn(widget.accounts, pollInterval: widget.pollInterval, reconnect: widget.reconnect);
late final _address = TextEditingController(text: widget.reconnect?.baseUrl ?? '');
late final _user = TextEditingController(text: widget.reconnect?.userName ?? '');
final _password = TextEditingController();
bool _reported = false;
@override
void initState() {
super.initState();
_flow.addListener(_changed);
if (widget.reconnect != null) _flow.probe(widget.reconnect!.baseUrl);
}
@override
void dispose() {
_flow.removeListener(_changed);
_flow.dispose();
_address.dispose();
_user.dispose();
_password.dispose();
super.dispose();
}
void _changed() {
if (!mounted) return;
setState(() {});
final linked = _flow.linked;
if (_flow.step == JellyfinSignInStep.done && linked != null && !_reported) {
_reported = true;
WidgetsBinding.instance.addPostFrameCallback((_) => widget.onDone?.call(linked));
}
}
@override
Widget build(BuildContext context) {
final t = Theme.of(context).textTheme;
final f = _flow;
final body = switch (f.step) {
JellyfinSignInStep.address || JellyfinSignInStep.probing => _addressStep(),
JellyfinSignInStep.method => _methodStep(),
JellyfinSignInStep.quickConnect => _quickConnectStep(),
JellyfinSignInStep.password || JellyfinSignInStep.signingIn => _passwordStep(),
JellyfinSignInStep.done => Row(children: [
const Icon(Icons.check_circle, color: Colors.green),
const SizedBox(width: 8),
Expanded(
child: Text('Linked as ${f.linked?.userName} on ${f.linked?.displayName}',
key: const ValueKey('jf-linked'))),
]),
};
return Column(
key: const ValueKey('jf-sign-in'),
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
Text(widget.reconnect == null ? 'Add a Jellyfin server' : 'Reconnect ${widget.reconnect!.displayName}',
style: t.titleMedium),
const SizedBox(height: 12),
if (f.server != null && f.step != JellyfinSignInStep.address && f.step != JellyfinSignInStep.done)
Padding(
padding: const EdgeInsets.only(bottom: 12),
child: _ServerLine(
name: f.server!.name,
version: f.server!.version,
url: f.baseUrl ?? '',
onChange: widget.reconnect == null ? f.changeAddress : null,
),
),
body,
if (f.error != null)
Padding(
padding: const EdgeInsets.only(top: 10),
child: Text(f.error!,
key: const ValueKey('jf-sign-in-error'),
style: TextStyle(color: Theme.of(context).colorScheme.error)),
),
],
);
}
Widget _cancelButton() => widget.onCancel == null
? const SizedBox.shrink()
: TextButton(onPressed: widget.onCancel, child: const Text('Cancel'));
Widget _addressStep() {
final probing = _flow.step == JellyfinSignInStep.probing;
return Row(
children: [
Expanded(
child: TextField(
key: const ValueKey('jf-address'),
controller: _address,
autofocus: true,
enabled: !probing,
decoration: fieldDecoration('Server address', hint: 'jellyfin.example.com'),
keyboardType: TextInputType.url,
onSubmitted: (v) => _flow.probe(v),
),
),
const SizedBox(width: 12),
FilledButton(
key: const ValueKey('jf-connect'),
onPressed: probing ? null : () => _flow.probe(_address.text),
child: probing
? const SizedBox(width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2))
: const Text('Connect'),
),
_cancelButton(),
],
);
}
Widget _methodStep() {
return Wrap(
spacing: 8,
runSpacing: 8,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
if (_flow.quickConnectAvailable)
FilledButton.icon(
key: const ValueKey('jf-quick-connect'),
onPressed: _flow.startQuickConnect,
icon: const Icon(Icons.qr_code_2, size: 18),
label: const Text('Quick Connect'),
),
if (_flow.quickConnectAvailable)
OutlinedButton.icon(
key: const ValueKey('jf-use-password'),
onPressed: _flow.usePassword,
icon: const Icon(Icons.password, size: 18),
label: const Text('Username & password'),
)
else
FilledButton.icon(
key: const ValueKey('jf-use-password'),
onPressed: _flow.usePassword,
icon: const Icon(Icons.password, size: 18),
label: const Text('Username & password'),
),
_cancelButton(),
],
);
}
Widget _quickConnectStep() {
final scheme = Theme.of(context).colorScheme;
final t = Theme.of(context).textTheme;
final code = _flow.code;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (code == null)
const Padding(
padding: EdgeInsets.all(16),
child: CircularProgressIndicator(strokeWidth: 2),
)
else
Row(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
color: scheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(10),
),
child: SelectableText(
code,
key: const ValueKey('jf-qc-code'),
style: t.displaySmall?.copyWith(
fontFeatures: const [FontFeature.tabularFigures()],
letterSpacing: 8,
fontWeight: FontWeight.w600,
),
),
),
const SizedBox(width: 8),
IconButton(
tooltip: 'Copy code',
onPressed: () => Clipboard.setData(ClipboardData(text: code)),
icon: const Icon(Icons.copy, size: 18),
),
],
),
const SizedBox(height: 12),
const Text('Approve it in Jellyfin: Profile → Quick Connect, then enter this code.'),
const SizedBox(height: 12),
Row(
children: [
const SizedBox(width: 14, height: 14, child: CircularProgressIndicator(strokeWidth: 2)),
const SizedBox(width: 10),
const Text('Waiting for approval…', style: TextStyle(color: Colors.grey)),
const SizedBox(width: 16),
TextButton(
key: const ValueKey('jf-qc-cancel'),
onPressed: _flow.cancel,
child: const Text('Cancel'),
),
],
),
],
);
}
Widget _passwordStep() {
final busy = _flow.step == JellyfinSignInStep.signingIn;
void submit() => _flow.signIn(_user.text, _password.text);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(
key: const ValueKey('jf-username'),
controller: _user,
autofocus: true,
enabled: !busy,
decoration: fieldDecoration('Username'),
onSubmitted: (_) => submit(),
),
const SizedBox(height: 10),
TextField(
key: const ValueKey('jf-password'),
controller: _password,
enabled: !busy,
obscureText: true,
decoration: fieldDecoration('Password'),
onSubmitted: (_) => submit(),
),
const SizedBox(height: 12),
Row(
children: [
FilledButton(
key: const ValueKey('jf-sign-in-submit'),
onPressed: busy ? null : submit,
child: busy
? const SizedBox(width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2))
: const Text('Sign in'),
),
const SizedBox(width: 8),
if (_flow.quickConnectAvailable)
TextButton(onPressed: _flow.cancel, child: const Text('Back')),
_cancelButton(),
],
),
],
);
}
}
class _ServerLine extends StatelessWidget {
final String name;
final String version;
final String url;
final VoidCallback? onChange;
const _ServerLine({required this.name, required this.version, required this.url, this.onChange});
@override
Widget build(BuildContext context) {
final t = Theme.of(context).textTheme;
return Row(
children: [
Icon(Icons.dns_outlined, size: 20, color: Theme.of(context).colorScheme.primary),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(name.isEmpty ? 'Jellyfin server' : name,
key: const ValueKey('jf-server-name'), style: t.titleSmall),
Text('Jellyfin $version · $url', style: t.bodySmall?.copyWith(color: Colors.grey)),
],
),
),
if (onChange != null) TextButton(onPressed: onChange, child: const Text('Change')),
],
);
}
}
@@ -0,0 +1,274 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:sync_relay_core/core.dart';
import 'accounts.dart';
// Per-viewer audio/subtitle carry-over: the last choice is kept per series
// (and as a global fallback) and matched against the next Jellyfin item's
// tracks with core's `matchTrack`. Local only; nothing goes on the wire.
const trackPrefsBoxName = 'track_prefs';
const _keyGlobal = 'global';
/// One audio or subtitle track of the open file as mpv's `track-list`
/// has it ([id] is mpv's `aid` / `sid`; [selected] is mpv's choice).
class MpvTrack {
final int id;
final JellyfinStreamType type;
final String? language;
final String? title;
final String? codec;
final int? channels;
final bool isDefault;
final bool isForced;
final bool isHearingImpaired;
final bool isExternal;
final bool selected;
const MpvTrack({
required this.id,
required this.type,
this.language,
this.title,
this.codec,
this.channels,
this.isDefault = false,
this.isForced = false,
this.isHearingImpaired = false,
this.isExternal = false,
this.selected = false,
});
/// As a stream for [matchTrack]; its index is the mpv id.
JellyfinMediaStream get asStream => JellyfinMediaStream(
index: id,
type: type,
language: language,
title: title,
codec: codec,
channels: channels,
isDefault: isDefault,
isForced: isForced,
isHearingImpaired: isHearingImpaired);
/// Menu text: "Japanese · E-AC3 2.0 · Commentary", "Track 2 · ASS".
String get label => [
languageName(language) ?? 'Track $id',
[?codecName(codec), ?channelLayoutName(channels)].join(' '),
if (title != null && title != languageName(language)) title!,
if (isForced) 'Forced',
if (isHearingImpaired) 'SDH',
].where((p) => p.isNotEmpty).join(' · ');
@override
bool operator ==(Object other) =>
other is MpvTrack &&
other.id == id &&
other.type == type &&
other.language == language &&
other.title == title &&
other.codec == codec &&
other.channels == channels &&
other.isDefault == isDefault &&
other.isForced == isForced &&
other.isHearingImpaired == isHearingImpaired &&
other.isExternal == isExternal &&
other.selected == selected;
@override
int get hashCode => Object.hash(id, type, language, title, codec, channels, isDefault, isForced,
isHearingImpaired, isExternal, selected);
@override
String toString() => 'MpvTrack(${type.name} $id ${selected ? '*' : ''}$label)';
}
const _languages = {
'jpn': 'Japanese', 'ja': 'Japanese', 'eng': 'English', 'en': 'English',
'ger': 'German', 'deu': 'German', 'de': 'German', 'fre': 'French', 'fra': 'French',
'fr': 'French', 'spa': 'Spanish', 'es': 'Spanish', 'ita': 'Italian', 'it': 'Italian',
'por': 'Portuguese', 'pt': 'Portuguese', 'rus': 'Russian', 'ru': 'Russian',
'chi': 'Chinese', 'zho': 'Chinese', 'zh': 'Chinese', 'kor': 'Korean', 'ko': 'Korean',
'ara': 'Arabic', 'ar': 'Arabic', 'pol': 'Polish', 'pl': 'Polish', 'dut': 'Dutch',
'nld': 'Dutch', 'nl': 'Dutch', 'swe': 'Swedish', 'sv': 'Swedish', 'fin': 'Finnish',
'fi': 'Finnish', 'nor': 'Norwegian', 'no': 'Norwegian', 'dan': 'Danish', 'da': 'Danish',
'tur': 'Turkish', 'tr': 'Turkish', 'hin': 'Hindi', 'hi': 'Hindi', 'tha': 'Thai',
'th': 'Thai', 'vie': 'Vietnamese', 'vi': 'Vietnamese', 'ind': 'Indonesian', 'id': 'Indonesian',
'may': 'Malay', 'msa': 'Malay', 'ms': 'Malay', 'heb': 'Hebrew', 'he': 'Hebrew',
'gre': 'Greek', 'ell': 'Greek', 'el': 'Greek', 'cze': 'Czech', 'ces': 'Czech', 'cs': 'Czech',
'hun': 'Hungarian', 'hu': 'Hungarian', 'ukr': 'Ukrainian', 'uk': 'Ukrainian',
'rum': 'Romanian', 'ron': 'Romanian', 'ro': 'Romanian', 'und': 'Undetermined',
};
/// "jpn" -> "Japanese"; other codes upper-cased, null for none.
String? languageName(String? code) {
if (code == null || code.isEmpty) return null;
return _languages[code.toLowerCase()] ?? code.toUpperCase();
}
const _codecs = {
'eac3': 'E-AC3', 'ac3': 'AC3', 'aac': 'AAC', 'flac': 'FLAC', 'opus': 'Opus',
'vorbis': 'Vorbis', 'mp3': 'MP3', 'truehd': 'TrueHD', 'dts': 'DTS', 'mlp': 'MLP',
'alac': 'ALAC', 'ass': 'ASS', 'ssa': 'SSA', 'subrip': 'SRT', 'srt': 'SRT',
'webvtt': 'VTT', 'mov_text': 'TX3G', 'hdmv_pgs_subtitle': 'PGS', 'pgssub': 'PGS',
'dvd_subtitle': 'VobSub', 'dvdsub': 'VobSub', 'dvb_subtitle': 'DVB',
};
/// mpv / Jellyfin codec name for menus: "eac3" -> "E-AC3", "subrip" -> "SRT".
String? codecName(String? codec) {
if (codec == null || codec.isEmpty) return null;
final c = codec.toLowerCase();
if (c.startsWith('pcm')) return 'PCM';
return _codecs[c] ?? codec.toUpperCase();
}
/// Channel count as a layout: 2 -> "2.0", 6 -> "5.1", 8 -> "7.1".
String? channelLayoutName(int? channels) => switch (channels) {
null || <= 0 => null,
1 => '1.0',
2 => '2.0',
3 => '2.1',
6 => '5.1',
7 => '6.1',
8 => '7.1',
final n => '${n}ch',
};
/// A viewer's audio/subtitle choice as traits (indices shift between
/// files). [subtitlesOff] wins over [subtitle].
class TrackChoice {
final TrackPreference? audio;
final TrackPreference? subtitle;
final bool subtitlesOff;
const TrackChoice({this.audio, this.subtitle, this.subtitlesOff = false});
Map<String, Object?> toJson() => {
'audio': ?_prefToJson(audio),
'subtitle': ?_prefToJson(subtitle),
'subtitlesOff': subtitlesOff,
};
static TrackChoice? fromJson(Object? j) {
if (j is! Map) return null;
return TrackChoice(
audio: _prefFromJson(j['audio'], JellyfinStreamType.audio),
subtitle: _prefFromJson(j['subtitle'], JellyfinStreamType.subtitle),
subtitlesOff: j['subtitlesOff'] == true,
);
}
/// The choice behind mpv's [aid] / [sid] (null = none / off): traits from
/// the Jellyfin [source] when this viewer has it (title, channels,
/// forced/HI flags), else from mpv's [tracks].
factory TrackChoice.of({
required int? aid,
required int? sid,
JellyfinMediaSource? source,
List<MpvTrack> tracks = const [],
}) {
TrackPreference? pref(JellyfinStreamType type, int? id) {
if (id == null) return null;
if (source != null) {
final index = source.jellyfinIndexForMpvTrack(type, id);
final s = index == null ? null : source.streamAt(index);
if (s != null) return TrackPreference.fromStream(s);
}
final t = tracks.where((t) => t.type == type && t.id == id).firstOrNull;
return t == null ? null : TrackPreference.fromStream(t.asStream);
}
return TrackChoice(
audio: pref(JellyfinStreamType.audio, aid),
subtitle: pref(JellyfinStreamType.subtitle, sid),
subtitlesOff: sid == null,
);
}
}
Map<String, Object?>? _prefToJson(TrackPreference? p) => p == null
? null
: {
'language': ?p.language,
'title': ?p.title,
'codec': ?p.codec,
'channels': ?p.channels,
if (p.isForced) 'forced': true,
if (p.isHearingImpaired) 'hi': true,
};
TrackPreference? _prefFromJson(Object? j, JellyfinStreamType type) {
if (j is! Map) return null;
String? s(Object? v) => v is String && v.isNotEmpty ? v : null;
return TrackPreference(
type: type,
language: s(j['language']),
title: s(j['title']),
codec: s(j['codec']),
channels: j['channels'] is int ? j['channels'] as int : null,
isForced: j['forced'] == true,
isHearingImpaired: j['hi'] == true,
);
}
/// mpv tracks to select for [choice] on a new item. Matches against the
/// Jellyfin [source]'s streams when this viewer has them (mapped to mpv ids
/// with [JellyfinMediaSource.mpvTrackId]), else against mpv's [tracks].
/// Null fields keep the player's default; [subtitlesOff] turns them off.
({int? aid, int? sid, bool subtitlesOff}) chooseTracks(TrackChoice choice,
{JellyfinMediaSource? source, List<MpvTrack> tracks = const []}) {
int? pick(TrackPreference? pref) {
if (pref == null) return null;
if (source != null) {
final s = matchTrack(pref, source.mediaStreams);
return s == null ? null : source.mpvTrackId(s.index);
}
return matchTrack(pref, [for (final t in tracks) t.asStream])?.index;
}
return (
aid: pick(choice.audio),
sid: choice.subtitlesOff ? null : pick(choice.subtitle),
subtitlesOff: choice.subtitlesOff,
);
}
/// The last choice per series and overall, in their own Hive box.
class TrackPrefs {
TrackPrefs({JellyfinStore? store}) : _store = store ?? HiveJellyfinStore(boxName: trackPrefsBoxName);
final JellyfinStore _store;
Future<void>? _open;
Future<void> _ready() => _open ??= _store.open().catchError((Object e) {
debugPrint('open track prefs failed: $e');
});
/// The series' choice, else the global one.
Future<TrackChoice?> load({String? seriesId}) async {
await _ready();
try {
Object? read(String key) {
final v = _store.get(key);
return v is String ? jsonDecode(v) : null;
}
return (seriesId == null ? null : TrackChoice.fromJson(read('series:$seriesId'))) ??
TrackChoice.fromJson(read(_keyGlobal));
} catch (e) {
debugPrint('read track prefs failed: $e');
return null;
}
}
/// Saves [choice] as the global one and, with [seriesId], the series'.
Future<void> save(TrackChoice choice, {String? seriesId}) async {
await _ready();
try {
final v = jsonEncode(choice.toJson());
await _store.put(_keyGlobal, v);
if (seriesId != null) await _store.put('series:$seriesId', v);
} catch (e) {
debugPrint('save track prefs failed: $e');
}
}
}
@@ -0,0 +1,19 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
/// The runner's window channel (linux/runner/my_application.cc,
/// windows/runner/flutter_window.cpp).
const _window = MethodChannel('starsync/window');
/// Asks the desktop to point at the app's window (taskbar flash, urgency
/// hint) when it is not focused; nothing when it is. The runner clears it
/// once the window gets focus. Quietly nothing where the runner lacks it.
Future<void> requestWindowAttention() async {
try {
await _window.invokeMethod<void>('attention');
} on MissingPluginException {
// Tests, other platforms.
} catch (e) {
debugPrint('window attention: $e');
}
}
@@ -0,0 +1,53 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:sync_relay_core/core.dart' show findInstallRoot;
/// The app's id: GTK's application id, so the Wayland `app_id` the
/// compositor matches to [desktopFileName] for the window's icon.
const appId = 'moe.senkochan.watch_together';
const desktopFileName = '$appId.desktop';
/// Linux release installs (an unpacked tarball) carry no launcher entry, so
/// Wayland shows a generic icon. On start: write the `.desktop` file into
/// `~/.local/share/applications` (Exec = this binary) and the icon into
/// the hicolor theme, only when something changed (a moved install, a new
/// icon). Never in debug runs; failures only log.
Future<void> installDesktopEntry({String? executable, String? dataHome, String? home}) async {
if (!Platform.isLinux || kDebugMode) return;
try {
final exe = executable ?? Platform.resolvedExecutable;
final root = findInstallRoot(exe, windows: false);
if (root == null) return;
final share = dataHome ?? Platform.environment['XDG_DATA_HOME'] ??
'${home ?? Platform.environment['HOME']}/.local/share';
final icons = '${root.path}/data/flutter_assets/assets/icon';
for (final size in const [16, 32, 48, 64, 128, 256, 512]) {
final src = File('$icons/icon_$size.png');
if (!src.existsSync()) continue;
final dst = File('$share/icons/hicolor/${size}x$size/apps/$appId.png');
if (dst.existsSync() && dst.lengthSync() == src.lengthSync()) continue;
await dst.parent.create(recursive: true);
await src.copy(dst.path);
}
final entry = desktopEntryText(exe);
final file = File('$share/applications/$desktopFileName');
if (file.existsSync() && file.readAsStringSync() == entry) return;
await file.parent.create(recursive: true);
await file.writeAsString(entry);
} catch (e) {
debugPrint('desktop entry: $e');
}
}
@visibleForTesting
String desktopEntryText(String exe) => '''[Desktop Entry]
Type=Application
Name=Starsync
Comment=Watch videos in sync with friends
Exec="$exe"
Icon=$appId
StartupWMClass=$appId
Categories=AudioVideo;Video;Player;Network;
Terminal=false
''';
@@ -0,0 +1,120 @@
import 'dart:typed_data';
/// FNV-1a over a 15x15 grid of RGB(A) pixels in the central half of an
/// image. Used on a small capture of what the compositor shows: the
/// centre is video, the edges carry the (changing) control overlays.
int hashCenterPixels(ByteData rgba, int width, int height) {
var x = 0x811c9dc5;
if (width <= 0 || height <= 0) return x;
for (var gy = 1; gy < 16; gy++) {
for (var gx = 1; gx < 16; gx++) {
final px = (width * (0.25 + 0.5 * gx / 16)).floor();
final py = (height * (0.25 + 0.5 * gy / 16)).floor();
final o = (py * width + px) * 4;
if (o + 3 > rgba.lengthInBytes) continue;
for (var c = 0; c < 3; c++) {
x = ((x ^ rgba.getUint8(o + c)) * 0x01000193) & 0xffffffff;
}
}
}
return x;
}
/// FNV-1a over ~4k bytes spread evenly across a raw frame (mpv
/// `screenshot-raw`); dimension-agnostic, enough to tell frames apart.
int hashSpread(Uint8List bytes) {
var x = 0x811c9dc5;
if (bytes.isEmpty) return x;
final step = bytes.length <= 4096 ? 1 : bytes.length ~/ 4096;
for (var i = 0; i < bytes.length; i += step) {
x = ((x ^ bytes[i]) * 0x01000193) & 0xffffffff;
}
return x;
}
/// One sample of player state, taken about once a second.
class FrameSample {
final DateTime at;
final double position;
/// Fingerprint of the picture currently shown on screen; null when it
/// cannot be captured (no view, hidden window, headless).
final int? frames;
/// Playing, not buffering, rate > 0, video track present, window
/// visible. Anything else disarms the watchdog.
final bool eligible;
const FrameSample({
required this.at,
required this.position,
required this.frames,
required this.eligible,
});
}
/// Detects a frozen picture: position advances normally but the shown
/// picture stands still for [stallAfter]. Seeks (position jumps), pauses,
/// buffering and missing captures all reset the stall clock. A hit is a
/// suspicion only — a still scene looks the same — so the caller confirms
/// it against the decoder and calls [dismiss] when it was a still scene.
/// Fires at most once until the picture is seen moving again for
/// [rearmAfter], so a player that stays (or soon gets) frozen after a
/// reload does not reload in a loop. [reset] keeps that latch.
class FrameWatchdog {
static const stallAfter = Duration(seconds: 3);
static const rearmAfter = Duration(seconds: 10);
FrameSample? _last;
DateTime? _stallSince;
DateTime? _healthySince;
bool _fired = false;
void reset() {
_last = null;
_stallSince = null;
_healthySince = null;
}
/// The last hit was a still scene: re-arm, but wait a full
/// [stallAfter] before suspecting again.
void dismiss() {
_fired = false;
_stallSince = null;
}
/// Returns true exactly when a freeze is suspected.
bool feed(FrameSample s) {
final last = _last;
_last = s;
if (!s.eligible || s.frames == null || last == null ||
!last.eligible || last.frames == null) {
_stallSince = null;
_healthySince = null;
return false;
}
if (s.frames != last.frames) {
_stallSince = null;
_healthySince ??= last.at;
if (s.at.difference(_healthySince!) >= rearmAfter) _fired = false;
return false;
}
_healthySince = null;
final wall = s.at.difference(last.at).inMilliseconds / 1000.0;
final moved = s.position - last.position;
// Normal playback only: a jump (seek) or a standstill (stall, the
// position itself stuck) is not a frozen picture.
final normal = wall > 0 && moved > wall * 0.5 && moved < wall * 2 + 0.5;
if (!normal) {
_stallSince = null;
return false;
}
_stallSince ??= last.at;
if (_fired) return false;
if (s.at.difference(_stallSince!) >= stallAfter) {
_fired = true;
_stallSince = null;
return true;
}
return false;
}
}
@@ -0,0 +1,274 @@
import 'dart:convert';
import 'package:sync_relay_core/core.dart' show JellyfinMediaSegment, JellyfinSegmentType, JellyfinStreamType;
import '../jellyfin/track_prefs.dart' show MpvTrack;
// Chapters, segments and tracks of the open media as plain values, plus
// the skip rules built on them (OP / ED / the 1:30 fallback, chapter
// steps). No Flutter, no player: `PlaybackController` feeds its signals
// through these and any controls UI reads them.
/// One mpv chapter. [end] is the next chapter's start, or the duration for
/// the last one ([start] while the duration is unknown).
class MediaChapter {
final int index;
final String title;
final Duration start;
final Duration end;
const MediaChapter({required this.index, required this.title, required this.start, required this.end});
@override
bool operator ==(Object other) =>
other is MediaChapter &&
other.index == index &&
other.title == title &&
other.start == start &&
other.end == end;
@override
int get hashCode => Object.hash(index, title, start, end);
@override
String toString() => 'MediaChapter($index "$title" $start-$end)';
}
/// A marked range of the item from Jellyfin (MediaSegments): intro,
/// outro, recap, preview or commercial.
class MediaSegment {
final JellyfinSegmentType kind;
final Duration start;
final Duration end;
const MediaSegment({required this.kind, required this.start, required this.end});
factory MediaSegment.fromJellyfin(JellyfinMediaSegment s) =>
MediaSegment(kind: s.type, start: s.start, end: s.end);
@override
String toString() => 'MediaSegment(${kind.name} $start-$end)';
}
/// Where a skip range came from.
enum SkipSource { segment, chapter }
/// An opening or ending: skipping it seeks to [end].
class SkipRange {
final Duration start;
final Duration end;
final SkipSource source;
/// The chapter title when [source] is a chapter.
final String? title;
const SkipRange(this.start, this.end, this.source, {this.title});
@override
bool operator ==(Object other) =>
other is SkipRange &&
other.start == start &&
other.end == end &&
other.source == source &&
other.title == title;
@override
int get hashCode => Object.hash(start, end, source, title);
@override
String toString() => 'SkipRange(${source.name} $start-$end${title == null ? '' : ' "$title"'})';
}
Duration _secs(num s) => Duration(microseconds: (s * 1e6).round());
/// mpv `chapter-list` as JSON (`[{"title": "OP", "time": 30.0}, ...]`).
/// Ends come from the next start, the last from [duration].
List<MediaChapter> parseChapterList(String json, Duration duration) {
final Object? raw;
try {
raw = jsonDecode(json);
} on FormatException {
return const [];
}
if (raw is! List) return const [];
final starts = <(String, Duration)>[
for (final c in raw)
if (c is Map && c['time'] is num && (c['time'] as num).isFinite)
((c['title'] is String ? c['title'] as String : '').trim(), _secs(c['time'] as num)),
]..sort((a, b) => a.$2.compareTo(b.$2));
return chaptersFromStarts(starts, duration);
}
/// Chapters from (title, start) pairs in start order.
List<MediaChapter> chaptersFromStarts(List<(String, Duration)> starts, Duration duration) => [
for (var i = 0; i < starts.length; i++)
MediaChapter(
index: i,
title: starts[i].$1,
start: starts[i].$2,
end: i + 1 < starts.length
? starts[i + 1].$2
: (duration > starts[i].$2 ? duration : starts[i].$2),
),
];
/// mpv `track-list` as JSON: the audio and subtitle tracks (video and
/// unknown types left out), with mpv's selection.
List<MpvTrack> parseTrackList(String json) {
final Object? raw;
try {
raw = jsonDecode(json);
} on FormatException {
return const [];
}
if (raw is! List) return const [];
String? s(Object? v) => v is String && v.isNotEmpty ? v : null;
return [
for (final t in raw)
if (t is Map && t['id'] is int && (t['type'] == 'audio' || t['type'] == 'sub'))
MpvTrack(
id: t['id'] as int,
type: t['type'] == 'audio' ? JellyfinStreamType.audio : JellyfinStreamType.subtitle,
language: s(t['lang']),
title: s(t['title']),
codec: s(t['codec']),
channels: (t['demux-channel-count'] ?? t['audio-channels']) is int
? (t['demux-channel-count'] ?? t['audio-channels']) as int
: null,
isDefault: t['default'] == true,
isForced: t['forced'] == true,
isHearingImpaired: t['hearing-impaired'] == true,
isExternal: t['external'] == true,
selected: t['selected'] == true,
),
];
}
final _openingTitle = RegExp(r'\b(op|opening|intro)\d*\b', caseSensitive: false);
final _endingTitle = RegExp(r'\b(ed|ending|credits|outro)\d*\b', caseSensitive: false);
/// "OP", "OP1", "Opening", "Intro" (whole words, any case).
bool isOpeningTitle(String title) => _openingTitle.hasMatch(title);
/// "ED", "ED2", "Ending", "Credits", "Outro" (whole words, any case).
bool isEndingTitle(String title) => _endingTitle.hasMatch(title);
SkipRange? _range(List<MediaSegment> segments, List<MediaChapter> chapters,
JellyfinSegmentType kind, bool Function(String) titleMatches) {
for (final s in segments) {
if (s.kind == kind && s.end > s.start) return SkipRange(s.start, s.end, SkipSource.segment);
}
for (final c in chapters) {
if (c.end > c.start && titleMatches(c.title)) {
return SkipRange(c.start, c.end, SkipSource.chapter, title: c.title);
}
}
return null;
}
/// The opening: the first Jellyfin Intro segment, else the first chapter
/// titled like an opening ([isOpeningTitle]).
SkipRange? openingRange(List<MediaSegment> segments, List<MediaChapter> chapters) =>
_range(segments, chapters, JellyfinSegmentType.intro, isOpeningTitle);
/// The ending: the first Jellyfin Outro segment, else the first chapter
/// titled like an ending ([isEndingTitle]).
SkipRange? endingRange(List<MediaSegment> segments, List<MediaChapter> chapters) =>
_range(segments, chapters, JellyfinSegmentType.outro, isEndingTitle);
/// Index of the chapter playing at [position], or null (before the first
/// chapter, or none).
int? chapterIndexAt(List<MediaChapter> chapters, Duration position) {
int? at;
for (final c in chapters) {
if (c.start <= position) at = c.index;
}
return at;
}
/// mpv's `chapter-seek-threshold`: going back within this much of a
/// chapter's start goes to the chapter before it.
const chapterBackThreshold = Duration(seconds: 5);
/// Target of a chapter step ([delta] +1 = PgDn, -1 = PgUp), as mpv: back
/// goes to the current chapter's start, or the previous one within
/// [chapterBackThreshold] of it; forward to the next chapter's start.
/// Null when there is nowhere to go.
Duration? chapterStepTarget(List<MediaChapter> chapters, Duration position, int delta) {
if (chapters.isEmpty || delta == 0) return null;
final at = chapterIndexAt(chapters, position);
if (delta > 0) {
final next = (at ?? -1) + delta;
return next < chapters.length ? chapters[next].start : null;
}
if (at == null) return null;
var target = at + delta + 1;
if (position - chapters[at].start < chapterBackThreshold) target--;
return target >= 0 ? chapters[target].start : Duration.zero;
}
/// What the skip pill offers.
enum SkipKind {
/// "Skip OP": to the opening's end.
opening,
/// "Skip ED": to the ending's end, nothing further.
ending,
/// "Skip 1:30": no opening known, +[fallbackSkip] from where it plays.
fixed,
}
class SkipOffer {
final SkipKind kind;
/// Seek target (for [SkipKind.fixed] as of the position it was computed
/// at; the press itself skips relative to the moment it lands).
final Duration target;
const SkipOffer(this.kind, this.target);
@override
bool operator ==(Object other) => other is SkipOffer && other.kind == kind && other.target == target;
@override
int get hashCode => Object.hash(kind, target);
@override
String toString() => 'SkipOffer(${kind.name} -> $target)';
}
/// The fixed skip (pill without an opening, Shift+Right, the controls'
/// "1:30" button).
const fallbackSkip = Duration(seconds: 90);
/// The fallback pill needs media longer than this (an episode, not a clip).
const fallbackMinDuration = Duration(minutes: 20);
/// ... and shows during this first part only.
const fallbackWindow = Duration(minutes: 6);
/// No pill in the last second of a range: the skip would do nothing.
const _skipTail = Duration(seconds: 1);
/// The skip pill at [position]: inside the opening "Skip OP" (to its end),
/// inside the ending "Skip ED" (to its end only: what follows, preview or
/// more episode, is the viewer's call). Without any opening info on media
/// longer than [fallbackMinDuration] and without chapters ([chaptered]: a
/// chaptered file without an OP chapter has none on purpose), "Skip 1:30"
/// during the first [fallbackWindow]. Null otherwise.
SkipOffer? skipOfferAt({
required Duration position,
required Duration duration,
required SkipRange? opening,
required SkipRange? ending,
bool chaptered = false,
}) {
bool inside(SkipRange r) => position >= r.start && position < r.end - _skipTail;
if (opening != null && inside(opening)) return SkipOffer(SkipKind.opening, opening.end);
if (ending != null && inside(ending)) return SkipOffer(SkipKind.ending, ending.end);
if (opening == null && !chaptered && duration > fallbackMinDuration && position < fallbackWindow) {
final to = position + fallbackSkip;
return SkipOffer(SkipKind.fixed, to < duration ? to : duration);
}
return null;
}
/// "1:05", "1:02:03".
String formatClock(Duration d) {
final neg = d.isNegative;
final t = d.abs();
String two(int n) => n.toString().padLeft(2, '0');
final h = t.inHours, m = t.inMinutes % 60, s = t.inSeconds % 60;
final body = h > 0 ? '$h:${two(m)}:${two(s)}' : '$m:${two(s)}';
return neg ? '-$body' : body;
}
@@ -1,75 +1,611 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart' show listEquals;
import 'package:flutter/widgets.dart';
import 'package:media_kit/media_kit.dart';
import 'package:media_kit_video/media_kit_video.dart';
import 'package:signals/signals_flutter.dart';
import 'package:sync_relay_core/core.dart' show JellyfinStreamType;
import '../sync/room_diff.dart';
import '../jellyfin/track_prefs.dart' show MpvTrack;
import '../shaders/shader_assets.dart';
import '../shaders/shader_presets.dart';
import '../state/settings.dart';
import '../sync/state_diff.dart';
import '../sync/user_seek.dart';
import 'frame_watchdog.dart';
import 'media_marks.dart';
import 'yt_dlp_app.dart' show AppYtDlp, ytdlFormat;
/// Owns one mpv [Player] (and its [VideoController]) per session: created
/// by [init] on join, released by [dispose] on leave, swapped by
/// [recreate] for "Reload video". Never reuse a player across sessions —
/// a GPU-side freeze sticks to the player, not to the widget.
class PlaybackController {
late final Player player;
bool _ready = false;
/// Read-ahead target: about two minutes rides out short outages and
/// makes skipping an intro instant.
static const readAheadSecs = 150;
/// Forward demuxer cache. Holds [readAheadSecs] up to ~14 Mbit/s (a
/// 1.4 GB / 24 min file is ~8); higher bitrates get less time. In RAM:
/// media_kit sets cache-on-disk but no cache-dir, so mpv falls back.
static const maxBytes = 256 * 1024 * 1024;
/// Back buffer (media_kit sets it to bufferSize too): enough for a short
/// seek back without holding another 256 MiB.
static const maxBackBytes = 64 * 1024 * 1024;
/// mpv's `volume-max` (its default is 130): the volume controls go
/// this far, past 100 % amplified.
static const maxVolume = 150;
SyncPlayer? _player;
VideoController? _video;
Timer? _watchTimer;
bool _probing = false;
final FrameWatchdog _watchdog = FrameWatchdog();
StreamSubscription<PlayerLog>? _logSub;
final List<StreamSubscription<Object?>> _timeSubs = [];
String _chapterJson = '';
// `glsl-shaders` as last set on the current player ('' = none).
String _shaders = '';
Future<void> _shaderOp = Future.value();
int _noticeId = 0;
/// Bumped for every new player; the video view keys itself on it so a
/// recreated player gets a fresh `Video` widget.
final generation = signal(0);
/// True while a live player exists. The video view shows plain black
/// otherwise, so no texture outlives its player.
final alive = signal(false);
/// Called once when the watchdog sees a frozen picture.
void Function()? onFrozen;
/// mpv's yt-dlp hook could not open the current link (its error text).
void Function(String error)? onYtdlError;
/// Points mpv's yt-dlp hook at [path] (the app's managed copy) for web
/// page links, at most 1080p, once per player; its cookies
/// (`ytdl-raw-options`) whenever they changed.
Future<void> useYtDlp(String path) async {
final p = _player;
if (p == null) return;
try {
final dynamic platform = p.platform;
if (!identical(p, _ytdlSetOn)) {
await platform?.setProperty('script-opts', 'ytdl_hook-ytdl_path=$path');
await platform?.setProperty('ytdl-format', ytdlFormat);
_ytdlSetOn = p;
_ytdlRaw = '';
}
final raw = await AppYtDlp.instance.mpvRawOptions();
if (raw != _ytdlRaw && identical(p, _player)) {
await platform?.setProperty('ytdl-raw-options', raw);
_ytdlRaw = raw;
}
} catch (e) {
debugPrint('yt-dlp path not set: $e');
}
}
Player? _ytdlSetOn;
String _ytdlRaw = '';
/// Fingerprint of the picture on screen, installed by the video view
/// (`ShownFrameProbe.sample`). Without it the watchdog stays idle.
Future<int?> Function()? shownFrame;
Player get player => _player!;
/// Seeks from the video controls (seek bar) go here instead of to mpv.
void Function(Duration position)? onUserSeek;
/// Latest shader message for the user (the session player toasts it).
final shaderNotice = signal<ShaderNotice?>(null);
/// How media_kit renders the current player's video (shaders need
/// [RenderMode.gpu]); [lastRenderMode] keeps it after leaving.
final renderMode = signal(RenderMode.unknown);
static final lastRenderMode = signal(RenderMode.unknown);
// ------------------------------------------------- media marks (signals)
//
// What the open file has, for any controls UI (the session's seek bar,
// chapter / track menus and skip pills read these; nothing here knows
// about widgets). Reset when the player goes away; [segments] is owned
// by the coordinator (it survives Reload video, the item is the same).
/// mpv `chapter-list`: title and start; end = next start, or the
/// duration for the last one. Empty without chapters.
final chapters = signal<List<MediaChapter>>(const []);
/// Jellyfin MediaSegments of the current item (Intro / Outro / Recap /
/// Preview / Commercial). Only viewers with their own account fetch
/// them (relay viewers cannot call Jellyfin): empty otherwise.
final segments = signal<List<MediaSegment>>(const []);
/// Audio and subtitle tracks from mpv's `track-list` (id, language,
/// title, codec, channels, default / forced flags, [MpvTrack.selected]).
final tracks = signal<List<MpvTrack>>(const []);
/// Playback position, in 250 ms steps (enough for pills and menus; a
/// seek bar should listen to `player.stream.position`).
final position = signal(Duration.zero);
/// Duration of the open file (zero until known).
final duration = signal(Duration.zero);
/// End of the cached range ahead of the position (mpv
/// `demuxer-cache-time`, media_kit's `buffer`), in 250 ms steps.
final buffered = signal(Duration.zero);
/// The opening ([openingRange]: Intro segment, else an OP-titled
/// chapter) and the ending ([endingRange]: Outro, else ED-titled).
late final Computed<SkipRange?> opening =
computed(() => openingRange(segments.value, chapters.value));
late final Computed<SkipRange?> ending =
computed(() => endingRange(segments.value, chapters.value));
/// Index into [chapters] of the chapter playing now.
late final Computed<int?> currentChapter =
computed(() => chapterIndexAt(chapters.value, position.value));
/// Pinned mpv properties whose read-back differs from what was set
/// (mpv ignores invalid values silently), as `name: wanted -> got`.
final List<String> pinMismatches = [];
bool get _ready => _player != null;
/// The [VideoController] of the current player. Created lazily by the
/// video view (it needs a running Flutter binding; headless tests never
/// touch it) and released with the player.
VideoController get video {
final existing = _video;
if (existing != null) return existing;
final hwdec = _hwdecOverride;
// media_kit's video output sets hwdec itself on attach ('auto' unless
// configured), which used to override SYNC_RELAY_HWDEC.
final video = _video = VideoController(
player,
configuration: VideoControllerConfiguration(hwdec: hwdec),
);
unawaited(_detectRenderMode(player, video));
return video;
}
// SYNC_RELAY_HWDEC wins over the Settings choice; read per player, so a
// changed setting applies on the next join or Reload video.
static String? get _hwdecOverride {
final hwdec = Platform.environment['SYNC_RELAY_HWDEC'];
if (hwdec != null && hwdec.isNotEmpty) return hwdec;
return AppSettings.instance.hwdec.peek();
}
Future<void> init() async {
player = Player(
if (_player != null) return;
_player = SyncPlayer(
configuration: PlayerConfiguration(
bufferSize: 64 * 1024 * 1024,
bufferSize: maxBytes,
libass: true,
vo: Platform.environment['SYNC_RELAY_VO'],
));
_ready = true;
// Verbose: the render-mode probe needs mpv's GL init lines.
logLevel: MPVLogLevel.v,
))
..onUserSeek = (d) => onUserSeek?.call(d);
_shaders = '';
renderMode.value = RenderMode.unknown;
shaderNotice.value = null;
_logSub = _player!.stream.log.listen(_onLog);
_watchMarks(_player!);
await _pinBufferProps();
await _pin('volume-max', '$maxVolume');
await applyShaders();
_watchdog.reset();
_watchTimer = Timer.periodic(const Duration(seconds: 1), (_) => _watch());
generation.value++;
alive.value = true;
}
/// Dispose the current player and start a fresh one (no media loaded).
/// Keeps volume. The caller reopens media and resyncs.
Future<void> recreate() async {
final volume = _player?.state.volume;
await dispose();
await init();
if (volume != null) {
try {
await player.setVolume(volume);
} catch (_) {}
}
}
Future<void> _pin(String property, String value) async {
try {
final dynamic platform = player.platform;
await platform?.setProperty(property, value);
final got = '${await platform?.getProperty(property)}';
if (!pinMatches(value, got)) {
pinMismatches.add('$property: $value -> $got');
debugPrint('pin $property=$value not applied, mpv has "$got"');
}
} catch (e, st) {
pinMismatches.add('$property: $value -> error $e');
debugPrint('pin $property=$value failed: $e\n$st');
}
}
Future<void> _pinBufferProps() async {
await _pin('demuxer-readahead-secs', '10');
await _pin('cache-secs', '10');
final hwdec = Platform.environment['SYNC_RELAY_HWDEC'];
if (hwdec != null && hwdec.isNotEmpty) {
pinMismatches.clear();
await _pin('demuxer-readahead-secs', '$readAheadSecs');
await _pin('cache-secs', '$readAheadSecs');
await _pin('demuxer-max-bytes', '$maxBytes');
await _pin('demuxer-max-back-bytes', '$maxBackBytes');
final hwdec = _hwdecOverride;
if (hwdec != null) {
await _pin('hwdec', hwdec);
}
await _pin('stream-lavf-o',
'reconnect_streamed=1,reconnect_on_http_error=1,reconnect_delay_max=4');
}
Future<void> openUrl(String url) async {
/// Puts the saved shader choice (Settings, Ctrl+0..6, shader button) on
/// the current player, live. A missing file or a value mpv won't take
/// turns shaders off and says why in [shaderNotice]; [announce] also
/// reports success ("Shaders: Mode A (HQ)").
Future<void> applyShaders({bool announce = false}) =>
_shaderOp = _shaderOp.then((_) => _applyShaders(announce));
Future<void> _applyShaders(bool announce) async {
final p = _player;
if (p == null) return;
final s = AppSettings.instance;
final preset = s.shaderPreset.peek();
final label = shaderLabel(preset, s.shaderQuality.peek());
String? problem;
var paths = const <String>[];
try {
paths = await shaderPathsFor(
preset, s.shaderQuality.peek(), s.customShaders.peek());
if (preset == ShaderPreset.custom && paths.isEmpty) {
problem = 'no custom shader files picked';
} else {
problem = shaderPathsProblem(paths);
}
} catch (e) {
problem = 'could not unpack the shaders ($e)';
}
if (!identical(p, _player)) return;
if (problem != null) paths = const [];
final value = joinShaderPaths(paths);
if (value != _shaders || problem != null) {
final got = await setShaderValue(value);
if (!identical(p, _player)) return;
if (problem == null && got != value) {
problem = 'mpv did not take them';
await setShaderValue('');
}
}
if (problem != null) {
_turnShadersOff('$label failed: $problem — turned off');
} else if (announce) {
_notice('Shaders: $label${_softwareHint(paths)}');
}
}
/// Sets mpv `glsl-shaders` on the current player and returns the
/// read-back (null on error).
@visibleForTesting
Future<String?> setShaderValue(String value) async {
try {
final dynamic platform = player.platform;
await platform?.setProperty('glsl-shaders', value);
final got = '${await platform?.getProperty('glsl-shaders')}';
_shaders = got;
if (got != value) debugPrint('glsl-shaders=$value not applied: "$got"');
return got;
} catch (e, st) {
debugPrint('glsl-shaders=$value failed: $e\n$st');
return null;
}
}
void _turnShadersOff(String message) {
AppSettings.instance.setShaderPreset(ShaderPreset.off);
_notice(message);
}
void _notice(String text) {
shaderNotice.value = ShaderNotice(++_noticeId, text);
}
String _softwareHint(List<String> paths) => paths.isEmpty
? ''
: switch (renderMode.peek()) {
RenderMode.software => ' (no effect: video uses software rendering)',
RenderMode.softwareGl => ' (likely slow: OpenGL runs on the CPU)',
_ => '',
};
// mpv logs from the render context: GL init lines mean GPU rendering;
// an error there while shaders are on is a shader that failed to load
// or compile (mpv then renders without it).
void _onLog(PlayerLog log) {
if (log.prefix == 'ytdl_hook' && (log.level == 'error' || log.level == 'fatal')) {
final text = log.text.trim().replaceFirst(RegExp(r'^ERROR:\s*'), '');
if (text.isNotEmpty) onYtdlError?.call(text);
return;
}
if (log.prefix != 'libmpv_render') return;
if (log.text.contains('suspected software renderer')) {
_setRenderMode(RenderMode.softwareGl);
} else if (log.text.startsWith('GL_VERSION') &&
renderMode.peek() == RenderMode.unknown) {
_setRenderMode(RenderMode.gpu);
} else if ((log.level == 'error' || log.level == 'fatal') &&
_shaders.isNotEmpty) {
debugPrint('shader error: ${log.text}');
final p = _player;
_shaders = '';
_shaderOp = _shaderOp.then((_) async {
if (!identical(p, _player)) return;
await setShaderValue('');
_turnShadersOff('Shaders failed in mpv (${log.text}) — turned off');
});
}
}
void _setRenderMode(RenderMode mode) {
renderMode.value = mode;
lastRenderMode.value = mode;
}
// media_kit's Linux/Windows video output creates mpv's render context
// while the controller is created: OpenGL when it can, else the
// software renderer, which never logs GL init (and ignores shaders).
Future<void> _detectRenderMode(Player p, VideoController video) async {
try {
await video.platform.future;
} catch (_) {
return;
}
await Future.delayed(const Duration(milliseconds: 1500));
if (!identical(p, _player) || renderMode.peek() != RenderMode.unknown) {
return;
}
_setRenderMode(RenderMode.software);
debugPrint('video output: software rendering (no GL init logged)');
if (_shaders.isNotEmpty) {
_notice('Shaders have no effect: video uses software rendering');
}
}
/// Fingerprint of the decoder's current frame (mpv `screenshot-raw`,
/// off-thread); null without video.
Future<int?> _sourceFrame(Player p) async {
try {
final raw = await p.screenshot(format: null);
return raw == null || raw.isEmpty ? null : hashSpread(raw);
} catch (_) {
return null;
}
}
/// A suspected freeze is real only if mpv's own frames still move: a
/// still scene looks frozen on screen too.
Future<bool> _sourceMoving(Player p) async {
final a = await _sourceFrame(p);
await Future.delayed(const Duration(milliseconds: 500));
if (!identical(p, _player) || !p.state.playing) return false;
final b = await _sourceFrame(p);
return a != null && b != null && a != b;
}
static bool get _windowVisible {
try {
final s = WidgetsBinding.instance.lifecycleState;
return s == null ||
s == AppLifecycleState.resumed ||
s == AppLifecycleState.inactive;
} catch (_) {
return false;
}
}
Future<void> _watch() async {
final p = _player;
final probe = shownFrame;
// Only meaningful once the video view is up.
if (p == null || _video == null || probe == null || _probing) return;
_probing = true;
try {
final frames = await probe();
if (!identical(p, _player)) return;
final s = p.state;
final eligible = s.playing &&
!s.buffering &&
!s.completed &&
s.rate > 0 &&
(s.width ?? 0) > 0 &&
_windowVisible;
final fired = _watchdog.feed(FrameSample(
at: DateTime.now(),
position: positionSecs,
frames: frames,
eligible: eligible,
));
if (fired) {
if (await _sourceMoving(p) && identical(p, _player)) {
debugPrint('video watchdog: picture stuck while mpv frames move');
onFrozen?.call();
} else {
_watchdog.dismiss();
}
}
} finally {
_probing = false;
}
}
/// Opens [url] paused. [headers] go with every request for it (mpv
/// `http-header-fields`): a Jellyfin token travels there, not in the URL.
Future<void> openUrl(String url, {Map<String, String>? headers}) async {
if (!_ready) return;
try {
await player.open(Media(url), play: false);
_chapterJson = '';
chapters.value = const [];
tracks.value = const [];
buffered.value = Duration.zero;
// An empty map, not null: media_kit caches headers per URI.
await player.open(Media(url, httpHeaders: headers ?? const {}), play: false);
} catch (e, st) {
debugPrint('open $url failed: $e\n$st');
debugPrint('open ${Uri.tryParse(url)?.replace(query: '') ?? 'media'} failed: $e\n$st');
rethrow;
}
}
double get positionSecs =>
player.state.position.inMilliseconds / 1000.0;
// Position / duration from media_kit's streams; chapter-list and
// track-list observed in mpv (read back as JSON).
void _watchMarks(SyncPlayer p) {
_timeSubs.add(p.stream.position.listen((d) {
// At the end the bar shows full: mpv's last position is the last
// frame's start (and the 250 ms steps round down), short of duration.
if (p.state.completed && duration.value > Duration.zero) {
position.value = duration.value;
return;
}
final ms = d.inMilliseconds;
position.value = Duration(milliseconds: ms - ms % 250);
}));
_timeSubs.add(p.stream.completed.listen((done) {
if (done && duration.value > Duration.zero) position.value = duration.value;
}));
_timeSubs.add(p.stream.buffer.listen((d) {
final ms = d.inMilliseconds;
buffered.value = Duration(milliseconds: ms - ms % 250);
}));
_timeSubs.add(p.stream.duration.listen((d) {
duration.value = d;
// The last chapter ends at the duration.
if (_chapterJson.isNotEmpty) chapters.value = parseChapterList(_chapterJson, d);
}));
() async {
try {
final dynamic platform = p.platform;
await platform?.observeProperty('chapter-list', (String v) async {
if (!identical(p, _player)) return;
_chapterJson = v;
chapters.value = parseChapterList(v, duration.peek());
});
await platform?.observeProperty('track-list', (String v) async {
if (!identical(p, _player)) return;
_setTracks(parseTrackList(v));
});
} catch (e) {
debugPrint('observe chapters / tracks failed: $e');
}
}();
}
void _setTracks(List<MpvTrack> list) {
if (!listEquals(tracks.peek(), list)) tracks.value = list;
}
/// Reads `track-list` now (after a selection: [tracks] then shows it at
/// once instead of on mpv's next change notice).
Future<void> _refreshTracks() async {
final p = _player;
if (p == null) return;
try {
final dynamic platform = p.platform;
final v = '${await platform?.getProperty('track-list')}';
if (identical(p, _player)) _setTracks(parseTrackList(v));
} catch (_) {}
}
void _clearMarks() {
_chapterJson = '';
chapters.value = const [];
tracks.value = const [];
position.value = Duration.zero;
duration.value = Duration.zero;
buffered.value = Duration.zero;
}
/// Waits (bounded) until the open file's tracks are listed and mpv has
/// selected its audio track, so a report or a carried-over choice sees
/// the real selection.
Future<List<MpvTrack>> waitForTracks({Duration timeout = const Duration(seconds: 5)}) async {
final end = DateTime.now().add(timeout);
while (_player != null && DateTime.now().isBefore(end)) {
final t = tracks.peek();
final hasAudio = t.any((x) => x.type == JellyfinStreamType.audio);
if (t.isNotEmpty && (!hasAudio || (await currentTrackIds()).aid != null)) return t;
await Future.delayed(const Duration(milliseconds: 100));
}
return tracks.peek();
}
/// mpv's selected audio and subtitle track ids (null = none / off).
Future<({int? aid, int? sid})> currentTrackIds() async {
final p = _player;
if (p == null) return (aid: null, sid: null);
try {
final dynamic platform = p.platform;
final aid = int.tryParse('${await platform?.getProperty('current-tracks/audio/id')}');
final sid = int.tryParse('${await platform?.getProperty('current-tracks/sub/id')}');
return (aid: aid, sid: sid);
} catch (_) {
return (aid: null, sid: null);
}
}
/// Selects tracks by mpv id; a null [aid] keeps audio, [subtitlesOff]
/// turns subtitles off, else a null [sid] keeps them.
Future<void> selectTracks({int? aid, int? sid, bool subtitlesOff = false}) async {
final p = _player;
if (p == null) return;
try {
final dynamic platform = p.platform;
if (aid != null) await platform?.setProperty('aid', '$aid');
if (subtitlesOff) {
await platform?.setProperty('sid', 'no');
} else if (sid != null) {
await platform?.setProperty('sid', '$sid');
}
} catch (e) {
debugPrint('select tracks failed: $e');
}
await _refreshTracks();
}
double get positionSecs {
final p = _player;
if (p == null) return 0;
return p.state.position.inMilliseconds / 1000.0;
}
double get bufferedSecsAhead {
final ahead =
(player.state.buffer - player.state.position).inMilliseconds / 1000.0;
final p = _player;
if (p == null) return 0;
final ahead = (p.state.buffer - p.state.position).inMilliseconds / 1000.0;
if (ahead.isNaN || ahead < 0) return 0;
return ahead;
}
double get durationSecs =>
player.state.duration.inMilliseconds / 1000.0;
double get durationSecs {
final p = _player;
if (p == null) return 0;
return p.state.duration.inMilliseconds / 1000.0;
}
Future<void> apply(List<PlayerAction> actions) async {
if (!_ready) return;
for (final action in actions) {
try {
switch (action) {
case SeekAction(:final position):
if ((positionSecs - position).abs() < 0.5) break;
await player.seek(Duration(milliseconds: (position * 1000).round()));
await _player!
.syncSeek(Duration(milliseconds: (position * 1000).round()));
case TransportAction(:final playing):
if (playing) {
await player.play();
@@ -85,10 +621,40 @@ class PlaybackController {
}
}
/// Release the player. The view drops its texture first ([alive] goes
/// false, one frame passes), then mpv is torn down.
Future<void> dispose() async {
_ready = false;
final p = _player;
if (p == null) return;
_watchTimer?.cancel();
_watchTimer = null;
await _logSub?.cancel();
_logSub = null;
for (final sub in _timeSubs) {
await sub.cancel();
}
_timeSubs.clear();
_player = null;
_video = null;
alive.value = false;
_clearMarks();
await _settleUi();
try {
await player.dispose();
await p.dispose();
} catch (_) {}
}
// Two frames: one may already be mid-build when [alive] flips. Then
// the length of a route pop (the fullscreen route holds its own `Video`
// and leaves animated). Bounded, since a hidden window renders no
// frames; headless tests have no binding and skip it all.
static Future<void> _settleUi() async {
try {
for (var i = 0; i < 2; i++) {
await WidgetsBinding.instance.endOfFrame
.timeout(const Duration(milliseconds: 300));
}
await Future.delayed(const Duration(milliseconds: 350));
} catch (_) {}
}
@@ -101,3 +667,26 @@ class PlaybackController {
}
}
}
enum RenderMode {
unknown,
/// OpenGL on a real GPU: shaders work.
gpu,
/// OpenGL on a software rasterizer (llvmpipe): shaders crawl.
softwareGl,
/// media_kit's software fallback: shaders are ignored.
software,
}
class ShaderNotice {
final int id;
final String text;
const ShaderNotice(this.id, this.text);
}
/// mpv echoes numbers in its own format (`150` reads back `150.000000`).
bool pinMatches(String wanted, String got) {
if (wanted == got) return true;
final a = double.tryParse(wanted), b = double.tryParse(got);
return a != null && b != null && a == b;
}
@@ -0,0 +1,57 @@
import 'dart:async';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:sync_relay_core/core.dart';
/// mpv's `ytdl-format` for links every player opens itself.
const ytdlFormat = 'bv*[height<=1080]+ba/b';
/// The app's yt-dlp (see [YtDlp]): in the app-support folder, shared with
/// the hosted server. [probe] remembers what pasted links are for a while,
/// so the Link preview and the swap ask once.
class AppYtDlp {
static final instance = AppYtDlp();
/// [ytDlp]: a given one (tests); default in the app-support folder.
AppYtDlp([YtDlp? ytDlp]) : _ytDlp = ytDlp == null ? null : Future.value(ytDlp);
Future<YtDlp>? _ytDlp;
Future<YtDlp> get ytDlp => _ytDlp ??= () async {
final base = await getApplicationSupportDirectory();
return YtDlp(dir(base.path));
}();
static String dir(String appSupport) => '$appSupport${Platform.pathSeparator}yt-dlp';
/// Gets yt-dlp ready in the background (download on first run, a daily
/// update check afterwards).
void warmUp() => unawaited(ytDlp.then((y) => y.ensure()).catchError((_) => null));
/// mpv's `ytdl-raw-options` for the cookies in the yt-dlp folder ('' for
/// none; see [YtDlpCookies]).
Future<String> mpvRawOptions() async {
try {
return (await ytDlp).cookies.mpvRawOptions();
} catch (_) {
return '';
}
}
final _probes = <String, (DateTime, Future<LinkProbe>)>{};
static const _probeTtl = Duration(minutes: 10);
Future<LinkProbe> probe(String url) {
final now = DateTime.now();
_probes.removeWhere((_, v) => now.difference(v.$1) > _probeTtl);
final hit = _probes[url];
if (hit != null) return hit.$2;
final f = ytDlp.then((y) => probeLink(Uri.parse(url), ytDlp: y));
_probes[url] = (now, f);
f.catchError((Object _) {
_probes.remove(url);
return LinkProbe.file(Uri(), -1);
});
return f;
}
}
+391
View File
@@ -0,0 +1,391 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:ui' show Color;
import 'package:path_provider/path_provider.dart';
import 'package:sync_relay_core/core.dart';
// AniList airing schedule (public GraphQL, no login): every episode airing
// in a time range, with covers, banners and popularity; and a title search
// (cover + banner of a show, for the media art of files and links).
//
// AniList allows ~90 requests/min (sometimes degraded to 30), so the raw
// pages are cached: in memory for 10 min (tab switches) and on disk for an
// hour (restarts), keyed by range start. A 429 waits out Retry-After once;
// if AniList still refuses (or is unreachable), stale cached data is
// returned with its fetch time.
final _log = createLogger('anilist');
const aniListDefaultUrl = 'https://graphql.anilist.co';
/// One episode airing, as AniList announces it.
class Airing {
final int anilistId;
/// English title, else romaji.
final String title;
final String titleRomaji;
final int episode;
/// UTC.
final DateTime airingAt;
final String? cover;
final String? banner;
/// The cover's dominant color, if AniList has one.
final Color? color;
final int popularity;
const Airing({
required this.anilistId,
required this.title,
required this.titleRomaji,
required this.episode,
required this.airingAt,
this.cover,
this.banner,
this.color,
this.popularity = 0,
});
/// Null for adult entries, music videos and malformed rows.
static Airing? fromJson(Object? j) {
if (j is! Map) return null;
final m = j['media'], at = j['airingAt'], ep = j['episode'];
if (m is! Map || at is! int || ep is! int) return null;
final id = m['id'];
if (id is! int || m['isAdult'] == true || m['format'] == 'MUSIC') return null;
final t = m['title'] is Map ? m['title'] as Map : const {};
final romaji = t['romaji'] is String ? t['romaji'] as String : '';
final english = t['english'] is String ? t['english'] as String : '';
final cov = m['coverImage'] is Map ? m['coverImage'] as Map : const {};
return Airing(
anilistId: id,
title: english.isNotEmpty ? english : (romaji.isNotEmpty ? romaji : '?'),
titleRomaji: romaji,
episode: ep,
airingAt: DateTime.fromMillisecondsSinceEpoch(at * 1000, isUtc: true),
cover: cov['large'] is String ? cov['large'] as String : null,
banner: m['bannerImage'] is String ? m['bannerImage'] as String : null,
color: parseHexColor(cov['color']),
popularity: m['popularity'] is int ? m['popularity'] as int : 0,
);
}
@override
String toString() => 'Airing($anilistId "$title" ep $episode @ $airingAt)';
}
/// An anime found by title ([AniListClient.searchMedia]).
class AniListMedia {
final int id;
/// English title, else romaji.
final String title;
/// Cover (portrait) URL: extraLarge, else large.
final String? cover;
/// The smaller cover, for when [cover] is too big to send on.
final String? coverSmall;
final String? banner;
const AniListMedia({required this.id, required this.title, this.cover, this.coverSmall, this.banner});
static AniListMedia? fromJson(Object? m) {
if (m is! Map || m['id'] is! int) return null;
final t = m['title'] is Map ? m['title'] as Map : const {};
final romaji = t['romaji'] is String ? t['romaji'] as String : '';
final english = t['english'] is String ? t['english'] as String : '';
final cov = m['coverImage'] is Map ? m['coverImage'] as Map : const {};
final large = cov['large'] is String ? cov['large'] as String : null;
return AniListMedia(
id: m['id'] as int,
title: english.isNotEmpty ? english : romaji,
cover: cov['extraLarge'] is String ? cov['extraLarge'] as String : large,
coverSmall: large,
banner: m['bannerImage'] is String ? m['bannerImage'] as String : null,
);
}
}
/// "#e4a15d" -> opaque [Color]; anything else -> null.
Color? parseHexColor(Object? s) {
if (s is! String || s.length != 7 || !s.startsWith('#')) return null;
final v = int.tryParse(s.substring(1), radix: 16);
return v == null ? null : Color(0xff000000 | v);
}
class AniListException implements Exception {
final int? statusCode;
final String message;
const AniListException(this.message, {this.statusCode});
bool get isRateLimited => statusCode == 429;
@override
String toString() => 'AniListException: $message';
}
/// The airings of one range. [staleSince] is set when AniList refused and
/// older cached data (fetched then) is returned instead.
class AniListSchedule {
final List<Airing> airings;
final DateTime fetchedAt;
final DateTime? staleSince;
const AniListSchedule(this.airings, this.fetchedAt, {this.staleSince});
bool get isStale => staleSince != null;
}
class _Cached {
final DateTime fetched;
final int from;
final int to;
final List<Object?> raw;
const _Cached(this.fetched, this.from, this.to, this.raw);
}
class AniListClient {
AniListClient({
String url = aniListDefaultUrl,
HttpClient? http,
Future<Directory?> Function()? cacheDir,
this.memoryTtl = const Duration(minutes: 10),
this.diskTtl = const Duration(hours: 1),
Future<void> Function(Duration)? delay,
DateTime Function()? now,
String? userAgent,
}) : url = Uri.parse(url),
_http = http ?? HttpClient(),
_ownsHttp = http == null,
_cacheDir = cacheDir ?? _defaultCacheDir,
_delay = delay ?? Future<void>.delayed,
_now = now ?? DateTime.now,
userAgent = userAgent ?? 'watch-together/$appVersion';
final Uri url;
final Duration memoryTtl;
final Duration diskTtl;
/// AniList 403s some default agents.
final String userAgent;
final HttpClient _http;
final bool _ownsHttp;
final Future<Directory?> Function() _cacheDir;
final Future<void> Function(Duration) _delay;
final DateTime Function() _now;
/// Pages per range at most (50 airings each).
static const maxPages = 40;
static const perPage = 50;
static const query = r'''
query($from:Int,$to:Int,$page:Int){Page(page:$page,perPage:50){pageInfo{hasNextPage}
airingSchedules(airingAt_greater:$from,airingAt_lesser:$to,sort:TIME){airingAt episode
media{id title{romaji english} coverImage{large color} bannerImage isAdult popularity format}}}}''';
final _mem = <int, _Cached>{};
final _inflight = <String, Future<AniListSchedule>>{};
static Future<Directory?> _defaultCacheDir() async {
try {
return await getApplicationCacheDirectory();
} catch (_) {
return null;
}
}
/// Every airing in [from, to), cached; see the file comment. [force]
/// skips the fresh caches (stale data is still the fallback).
Future<AniListSchedule> schedule(DateTime from, DateTime to, {bool force = false}) {
final f = from.millisecondsSinceEpoch ~/ 1000, t = to.millisecondsSinceEpoch ~/ 1000;
final k = '$f-$t-$force';
return _inflight[k] ??= _schedule(f, t, force).whenComplete(() {
_inflight.remove(k);
});
}
Future<AniListSchedule> _schedule(int from, int to, bool force) async {
final now = _now();
final mem = _mem[from];
final memHit = mem != null && mem.to == to ? mem : null;
if (!force && memHit != null && now.difference(memHit.fetched) < memoryTtl) {
return _result(memHit);
}
final disk = await _readDisk(from, to);
if (!force && disk != null && now.difference(disk.fetched) < diskTtl) {
_mem[from] = disk;
return _result(disk);
}
try {
final raw = await _fetch(from, to);
final c = _mem[from] = _Cached(_now(), from, to, raw);
await _writeDisk(c);
return _result(c);
} catch (e) {
final stale = [memHit, disk].whereType<_Cached>().fold<_Cached?>(
null, (a, b) => a == null || b.fetched.isAfter(a.fetched) ? b : a);
if (stale == null) rethrow;
_log.warning('AniList failed, using cache from ${stale.fetched}: $e');
return _result(stale, stale: true);
}
}
AniListSchedule _result(_Cached c, {bool stale = false}) => AniListSchedule(
[for (final r in c.raw) ?Airing.fromJson(r)],
c.fetched,
staleSince: stale ? c.fetched : null,
);
Future<List<Object?>> _fetch(int from, int to) async {
final out = <Object?>[];
for (var page = 1; page <= maxPages; page++) {
final data = await _page(from, to, page);
final rows = data['airingSchedules'];
if (rows is List) out.addAll(rows);
final info = data['pageInfo'];
if (info is! Map || info['hasNextPage'] != true) break;
}
return out;
}
Future<Map> _page(int from, int to, int page) async {
final (data, status, j) = await _graphql(query, {'from': from - 1, 'to': to, 'page': page});
final p = data?['Page'];
if (p is! Map) {
throw AniListException('AniList: bad response${_errorText(j)}', statusCode: status);
}
return p;
}
/// One GraphQL request: `data` (null when missing), status and body. A
/// 429 waits out Retry-After once; other non-200s throw.
Future<(Map?, int, Object?)> _graphql(String query, Map<String, Object?> variables) async {
for (var attempt = 0;; attempt++) {
final (status, body, retryAfter) = await _post(jsonEncode({'query': query, 'variables': variables}));
if (status == 429 && attempt == 0) {
final wait = retryAfter ?? 60;
_log.info('AniList rate limited, retrying in ${wait}s');
await _delay(Duration(seconds: wait.clamp(1, 65)));
continue;
}
Object? j;
try {
j = jsonDecode(body);
} catch (_) {}
if (status != 200) {
throw AniListException('AniList HTTP $status${_errorText(j)}', statusCode: status);
}
final data = j is Map && j['data'] is Map ? j['data'] as Map : null;
return (data, status, j);
}
}
static const searchQuery = r'''
query($search:String){Media(search:$search,type:ANIME,isAdult:false){id title{romaji english}
coverImage{extraLarge large} bannerImage}}''';
final _search = <String, Future<AniListMedia?>>{};
/// AniList's best anime match for [title], or null when there is none.
/// One request per title for this client's life (a failure is not kept,
/// so a later call asks again).
Future<AniListMedia?> searchMedia(String title) {
final key = title.trim();
if (key.isEmpty) return Future.value(null);
return _search[key] ??= _searchMedia(key).catchError((Object e) {
_search.remove(key);
throw e;
});
}
Future<AniListMedia?> _searchMedia(String title) async {
try {
final (data, _, _) = await _graphql(searchQuery, {'search': title});
return AniListMedia.fromJson(data?['Media']);
} on AniListException catch (e) {
// No match is a 404 with "Not Found." in errors.
if (e.statusCode == 404) return null;
rethrow;
}
}
static String _errorText(Object? j) {
final errs = j is Map ? j['errors'] : null;
if (errs is! List || errs.isEmpty) return '';
final m = errs.first is Map ? (errs.first as Map)['message'] : null;
return m is String ? ': $m' : '';
}
Future<(int, String, int?)> _post(String body) async {
try {
final req = await _http.postUrl(url).timeout(const Duration(seconds: 15));
req.headers
..contentType = ContentType.json
..set(HttpHeaders.acceptHeader, 'application/json')
..set(HttpHeaders.userAgentHeader, userAgent);
req.write(body);
final res = await req.close().timeout(const Duration(seconds: 20));
final text = await res.transform(const Utf8Decoder(allowMalformed: true)).join();
return (
res.statusCode,
text,
int.tryParse(res.headers.value(HttpHeaders.retryAfterHeader) ?? ''),
);
} on TimeoutException {
throw const AniListException('AniList timed out');
} on IOException catch (e) {
throw AniListException('AniList unreachable: $e');
}
}
// ---------------------------------------------------------------- disk
Future<File?> _file(int from) async {
final dir = await _cacheDir();
return dir == null ? null : File('${dir.path}/anilist-schedule-$from.json');
}
Future<_Cached?> _readDisk(int from, int to) async {
try {
final f = await _file(from);
if (f == null || !await f.exists()) return null;
final j = jsonDecode(await f.readAsString());
if (j is! Map || j['from'] != from || j['to'] != to) return null;
final fetched = j['fetched'], raw = j['raw'];
if (fetched is! int || raw is! List) return null;
return _Cached(DateTime.fromMillisecondsSinceEpoch(fetched), from, to, raw);
} catch (_) {
return null;
}
}
Future<void> _writeDisk(_Cached c) async {
try {
final f = await _file(c.from);
if (f == null) return;
await f.parent.create(recursive: true);
await f.writeAsString(jsonEncode({
'fetched': c.fetched.millisecondsSinceEpoch,
'from': c.from,
'to': c.to,
'raw': c.raw,
}));
// Older ranges are useless once a day has passed.
await for (final e in f.parent.list()) {
final name = e.uri.pathSegments.last;
if (e is File &&
e.path != f.path &&
name.startsWith('anilist-schedule-') &&
_now().difference(await e.lastModified()) > const Duration(days: 1)) {
await e.delete();
}
}
} catch (e) {
_log.warning('AniList cache write failed: $e');
}
}
/// Drops the memory cache (the disk cache stays as the stale fallback).
void clearMemory() => _mem.clear();
void close() {
if (_ownsHttp) _http.close(force: true);
}
}
@@ -0,0 +1,754 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:sync_relay_core/core.dart';
import '../jellyfin/accounts.dart';
// AnimeFlow app API: what the media server tracks, has, downloads and was
// asked for. Authenticated with the user's own Jellyfin access token
// (X-Emby-Token) on the Jellyfin server next to it.
//
// Errors are JSON {"detail","code"} ([AnimeFlowException]); nginx in front
// may answer 429 with HTML (5 req/s, burst 20), retried with backoff.
// /schedule, /series, /series/{id} and /requests send weak ETags: the last
// body is kept and revalidated with If-None-Match. Times are UTC ISO
// ("2026-10-08T19:36:32Z"). Scopes can change at runtime, so /me is cached
// for a minute at most.
final _log = createLogger('animeflow');
const animeFlowDefaultBase = 'https://anime.senkochan.moe/api/app/v1';
// ---------------------------------------------------------------- errors
enum AnimeFlowErrorCode {
missingToken('missing_token'),
badToken('bad_token'),
revoked('revoked'),
missingScope('missing_scope'),
ownAccount('own_account'),
jellyfinUnreachable('jellyfin_unreachable'),
searchUnavailable('search_unavailable'),
invalidRequest('invalid_request'),
invalidTime('invalid_time'),
invalidRange('invalid_range'),
notFound('not_found'),
seriesNotFound('series_not_found'),
episodeNotFound('episode_not_found'),
requestNotFound('request_not_found'),
userNotFound('user_not_found'),
alreadyTracked('already_tracked'),
alreadyAvailable('already_available'),
alreadyRequested('already_requested'),
invalidState('invalid_state'),
/// nginx 429 (client side, no JSON body).
rateLimited('rate_limited'),
/// No answer: DNS, connect, timeout (client side).
network('network'),
/// Anything not in this list.
unknown('unknown');
const AnimeFlowErrorCode(this.wire);
final String wire;
static AnimeFlowErrorCode parse(Object? s) =>
values.where((c) => c.wire == s).firstOrNull ?? unknown;
/// Best guess from the status alone (body was not JSON).
static AnimeFlowErrorCode forStatus(int status) => switch (status) {
401 => badToken,
403 => missingScope,
404 => notFound,
409 => invalidState,
400 => invalidRequest,
429 => rateLimited,
503 => jellyfinUnreachable,
_ => unknown,
};
}
class AnimeFlowException implements Exception {
/// HTTP status; 0 when no answer came.
final int statusCode;
final AnimeFlowErrorCode code;
final String detail;
const AnimeFlowException(this.statusCode, this.code, this.detail);
/// Sign in again (token missing, wrong or expired).
bool get isUnauthorized => statusCode == 401;
/// Revoked, missing scope or own account.
bool get isForbidden => statusCode == 403;
bool get isNotFound => statusCode == 404;
bool get isConflict => statusCode == 409;
bool get isUnavailable => statusCode == 503 || code == AnimeFlowErrorCode.network;
bool get isRateLimited => code == AnimeFlowErrorCode.rateLimited;
@override
String toString() => 'AnimeFlowException($statusCode ${code.wire}): $detail';
}
// ---------------------------------------------------------------- models
DateTime? _time(Object? v) => v is String ? DateTime.tryParse(v)?.toUtc() : null;
int? _int(Object? v) => v is int ? v : (v is num ? v.toInt() : null);
String? _str(Object? v) => v is String ? v : null;
List<Map> _items(Object? j) =>
j is Map && j['items'] is List ? (j['items'] as List).whereType<Map>().toList() : const [];
/// Formats [d] as AnimeFlow wants it: "2026-10-08T19:36:32Z".
String animeFlowTime(DateTime d) => '${d.toUtc().toIso8601String().split('.').first.replaceAll('Z', '')}Z';
enum AnimeFlowScope {
view,
request,
manage;
static Set<AnimeFlowScope> parse(Object? j) => {
if (j is List)
for (final s in j) ...values.where((v) => v.name == s),
};
}
class AnimeFlowMe {
final String userId;
final String name;
final Set<AnimeFlowScope> scopes;
const AnimeFlowMe({required this.userId, required this.name, required this.scopes});
bool can(AnimeFlowScope s) => scopes.contains(s);
static AnimeFlowMe fromJson(Object? j) {
final m = j is Map ? j : const {};
return AnimeFlowMe(
userId: _str(m['user_id']) ?? '',
name: _str(m['name']) ?? '',
scopes: AnimeFlowScope.parse(m['scopes']),
);
}
}
enum EpisodeStatus {
upcoming,
missing,
downloading,
available,
unmonitored,
failed;
/// Unknown values read as upcoming.
static EpisodeStatus parse(Object? s) => values.where((v) => v.name == s).firstOrNull ?? upcoming;
}
/// A series on Jellyfin (`GET /library`).
class AnimeFlowLibraryItem {
final String jellyfinSeriesId;
final String name;
/// Set when AnimeFlow tracks it.
final int? seriesId;
/// AniList entry per TVDB season (the first, when a season spans more).
final Map<int, int> anilistIds;
/// Every AniList entry per season with its episode run (`anilist_map`):
/// a season AniList splits (one cour as two entries) has several rows.
final List<AniListRun> anilistMap;
const AnimeFlowLibraryItem({
required this.jellyfinSeriesId,
required this.name,
this.seriesId,
this.anilistIds = const {},
this.anilistMap = const [],
});
static AnimeFlowLibraryItem fromJson(Map j) => AnimeFlowLibraryItem(
jellyfinSeriesId: _str(j['jellyfin_series_id']) ?? '',
name: _str(j['name']) ?? '',
seriesId: _int(j['series_id']),
anilistIds: {
for (final e in ((j['anilist_ids'] as Map?) ?? const {}).entries)
if (int.tryParse('${e.key}') case final season? when _int(e.value) != null) season: _int(e.value)!,
},
anilistMap: [
for (final r in (j['anilist_map'] as List?) ?? const [])
if (r is Map && _int(r['season']) != null && _int(r['anilist_id']) != null)
AniListRun(
season: _int(r['season'])!,
anilistId: _int(r['anilist_id'])!,
firstEpisode: _int(r['first_episode']) ?? 1,
offset: _int(r['offset']) ?? 0,
),
],
);
}
/// One AniList entry's run within a TVDB season: TVDB episode e (from
/// [firstEpisode] to the next run of that season) is AniList episode
/// e - [offset].
class AniListRun {
final int season;
final int anilistId;
final int firstEpisode;
final int offset;
const AniListRun({required this.season, required this.anilistId, this.firstEpisode = 1, this.offset = 0});
}
class AnimeFlowEpisode {
/// 0 = specials.
final int season;
final int episode;
final int? absolute;
final String? title;
final DateTime? airDate;
/// The AniList entry of this episode's season (one AnimeFlow series may
/// span several AniList entries).
final int? anilistId;
/// The episode number within [anilistId] when AnimeFlow knows it (split
/// cours, absolute numbering); prefer it over [episode] for matching.
final int? anilistEpisode;
final EpisodeStatus status;
/// 0..1, only while downloading.
final double? progress;
/// Only when failed.
final String? reason;
const AnimeFlowEpisode({
required this.season,
required this.episode,
this.absolute,
this.title,
this.airDate,
this.anilistId,
this.anilistEpisode,
required this.status,
this.progress,
this.reason,
});
static AnimeFlowEpisode fromJson(Map j) => AnimeFlowEpisode(
season: _int(j['season']) ?? 0,
episode: _int(j['episode']) ?? 0,
absolute: _int(j['absolute']),
title: _str(j['episode_title']),
airDate: _time(j['air_date']),
// The episode's own entry (a split season rolls into its sequel),
// else the series' one.
anilistId: _int(j['episode_anilist_id']) ?? _int(j['anilist_id']),
anilistEpisode: _int(j['anilist_episode']),
status: EpisodeStatus.parse(j['status']),
progress: (j['progress'] as num?)?.toDouble(),
reason: _str(j['reason']),
);
}
/// A /schedule row: one episode plus its series.
class AnimeFlowScheduleItem {
final AnimeFlowEpisode episode;
final int seriesId;
final String title;
final int? anidbId;
final int? tvdbId;
final String? jellyfinSeriesId;
final bool followed;
const AnimeFlowScheduleItem({
required this.episode,
required this.seriesId,
required this.title,
this.anidbId,
this.tvdbId,
this.jellyfinSeriesId,
this.followed = false,
});
static AnimeFlowScheduleItem fromJson(Map j) => AnimeFlowScheduleItem(
episode: AnimeFlowEpisode.fromJson(j),
seriesId: _int(j['series_id']) ?? 0,
title: _str(j['title']) ?? '',
anidbId: _int(j['anidb_id']),
tvdbId: _int(j['tvdb_id']),
jellyfinSeriesId: _str(j['jellyfin_series_id']),
followed: j['followed'] == true,
);
}
class AnimeFlowScheduleWindow {
final DateTime? from;
final DateTime? to;
final List<AnimeFlowScheduleItem> items;
const AnimeFlowScheduleWindow({this.from, this.to, required this.items});
}
enum SeriesState {
releasing,
released;
static SeriesState parse(Object? s) => s == 'released' ? released : releasing;
}
class AnimeFlowSeries {
final int seriesId;
final String title;
final int? anidbId;
final int? tvdbId;
/// The series' (first) AniList entry; later seasons may have others, see
/// [AnimeFlowEpisode.anilistId].
final int? anilistId;
final String? jellyfinSeriesId;
final SeriesState state;
final bool monitored;
final bool followed;
final int episodesTotal;
final int episodesAvailable;
final DateTime? nextAirDate;
/// Only from [AnimeFlowClient.seriesDetail].
final List<AnimeFlowEpisode> episodes;
const AnimeFlowSeries({
required this.seriesId,
required this.title,
this.anidbId,
this.tvdbId,
this.anilistId,
this.jellyfinSeriesId,
this.state = SeriesState.releasing,
this.monitored = false,
this.followed = false,
this.episodesTotal = 0,
this.episodesAvailable = 0,
this.nextAirDate,
this.episodes = const [],
});
static AnimeFlowSeries fromJson(Map j) => AnimeFlowSeries(
seriesId: _int(j['series_id']) ?? 0,
title: _str(j['title']) ?? '',
anidbId: _int(j['anidb_id']),
tvdbId: _int(j['tvdb_id']),
anilistId: _int(j['anilist_id']),
jellyfinSeriesId: _str(j['jellyfin_series_id']),
state: SeriesState.parse(j['state']),
monitored: j['monitored'] == true,
followed: j['followed'] == true,
episodesTotal: _int(j['episodes_total']) ?? 0,
episodesAvailable: _int(j['episodes_available']) ?? 0,
nextAirDate: _time(j['next_air_date']),
episodes: [
if (j['episodes'] is List)
for (final e in (j['episodes'] as List).whereType<Map>()) AnimeFlowEpisode.fromJson(e),
],
);
}
class AnimeFlowSearchHit {
final int anidbId;
final String title;
final int? anilistId;
/// Set when AnimeFlow already tracks it.
final int? seriesId;
const AnimeFlowSearchHit({required this.anidbId, required this.title, this.anilistId, this.seriesId});
static AnimeFlowSearchHit fromJson(Map j) => AnimeFlowSearchHit(
anidbId: _int(j['anidb_id']) ?? 0,
title: _str(j['title']) ?? '',
anilistId: _int(j['anilist_id']),
seriesId: _int(j['series_id']),
);
}
enum RequestKind {
series,
season,
episode;
static RequestKind parse(Object? s) => values.where((v) => v.name == s).firstOrNull ?? series;
}
enum RequestStatus {
/// Briefly, before auto-approval or a manager decides.
pending,
approved,
searching,
done,
rejected,
failed;
static RequestStatus parse(Object? s) => values.where((v) => v.name == s).firstOrNull ?? pending;
}
class AnimeFlowRequest {
final int id;
final RequestKind kind;
final RequestStatus status;
final String title;
final int? anidbId;
final int? seriesId;
final int? season;
final int? episode;
final String? reason;
final DateTime? createdAt;
final DateTime? updatedAt;
/// Only with scope=all.
final String? userId;
final String? userName;
const AnimeFlowRequest({
required this.id,
required this.kind,
required this.status,
required this.title,
this.anidbId,
this.seriesId,
this.season,
this.episode,
this.reason,
this.createdAt,
this.updatedAt,
this.userId,
this.userName,
});
static AnimeFlowRequest fromJson(Map j) => AnimeFlowRequest(
id: _int(j['id']) ?? 0,
kind: RequestKind.parse(j['kind']),
status: RequestStatus.parse(j['status']),
title: _str(j['title']) ?? '',
anidbId: _int(j['anidb_id']),
seriesId: _int(j['series_id']),
season: _int(j['season']),
episode: _int(j['episode']),
reason: _str(j['reason']),
createdAt: _time(j['created_at']),
updatedAt: _time(j['updated_at']),
userId: _str(j['user_id']),
userName: _str(j['user_name']),
);
}
class AnimeFlowUser {
/// The Jellyfin user id.
final String userId;
final String name;
final Set<AnimeFlowScope> scopes;
final bool revoked;
final DateTime? lastSeenAt;
const AnimeFlowUser({
required this.userId,
required this.name,
required this.scopes,
this.revoked = false,
this.lastSeenAt,
});
static AnimeFlowUser fromJson(Map j) => AnimeFlowUser(
userId: _str(j['user_id']) ?? '',
name: _str(j['name']) ?? '',
scopes: AnimeFlowScope.parse(j['scopes']),
revoked: j['revoked'] == true,
lastSeenAt: _time(j['last_seen_at']),
);
}
// ---------------------------------------------------------------- client
class _Etagged {
final String etag;
final Object? body;
const _Etagged(this.etag, this.body);
}
class AnimeFlowClient {
/// [token] wins over [tokenProvider]; with neither, every call fails with
/// [AnimeFlowErrorCode.missingToken].
AnimeFlowClient({
String baseUrl = animeFlowDefaultBase,
this._token,
this._tokenProvider,
HttpClient? http,
Future<void> Function(Duration)? delay,
DateTime Function()? now,
this.meTtl = const Duration(seconds: 60),
String? userAgent,
}) : baseUrl = baseUrl.endsWith('/') ? baseUrl.substring(0, baseUrl.length - 1) : baseUrl,
_http = http ?? HttpClient(),
_ownsHttp = http == null,
_delay = delay ?? Future<void>.delayed,
_now = now ?? DateTime.now,
userAgent = userAgent ?? 'watch-together/$appVersion';
/// Authenticated with the linked Jellyfin account on the same domain
/// (see [tokenFor]), looked up on every call so a re-link applies.
factory AnimeFlowClient.fromAccounts(
JellyfinAccounts accounts, {
String baseUrl = animeFlowDefaultBase,
HttpClient? http,
}) =>
AnimeFlowClient(
baseUrl: baseUrl,
http: http,
tokenProvider: () async {
await accounts.ensureLoaded(validate: false);
return tokenFor(accounts.accounts, baseUrl);
},
);
final String baseUrl;
final Duration meTtl;
final String userAgent;
final String? _token;
final Future<String?> Function()? _tokenProvider;
final HttpClient _http;
final bool _ownsHttp;
final Future<void> Function(Duration) _delay;
final DateTime Function() _now;
/// Request bodies above this are refused by the server.
static const maxBodyBytes = 16 * 1024;
/// Retries after an nginx 429 (backoff 1 s, 2 s unless Retry-After).
static const rateLimitRetries = 2;
/// The longest /schedule window.
static const maxScheduleRange = Duration(days: 45);
String? _lastToken;
final _etags = <String, _Etagged>{};
({DateTime at, AnimeFlowMe me})? _me;
/// The token of the linked Jellyfin account whose host shares
/// [animeFlowBase]'s parent domain (anime.example.org -> example.org,
/// jellyfin.example.org, ...).
static String? tokenFor(Iterable<JellyfinAccount> accounts, String animeFlowBase) {
final host = Uri.tryParse(animeFlowBase)?.host.toLowerCase() ?? '';
if (host.isEmpty) return null;
final labels = host.split('.');
final parent = labels.length > 2 ? labels.skip(1).join('.') : host;
bool matches(String url) {
final h = Uri.tryParse(url)?.host.toLowerCase() ?? '';
return h.isNotEmpty && (h == parent || h.endsWith('.$parent'));
}
for (final a in accounts) {
if (a.urls.any(matches)) return a.token;
}
return null;
}
Future<String> _authToken() async {
final t = _token ?? await _tokenProvider?.call();
if (t == null || t.isEmpty) {
throw const AnimeFlowException(
401, AnimeFlowErrorCode.missingToken, 'No linked Jellyfin account for AnimeFlow');
}
if (t != _lastToken) {
// Another user: nothing cached belongs to them.
_lastToken = t;
_etags.clear();
_me = null;
}
return t;
}
Uri _uri(String path, [Map<String, String>? query]) =>
Uri.parse('$baseUrl$path').replace(queryParameters: query);
/// Sends one call; JSON body or null (204). [etag]: revalidate the last
/// body of this URL.
Future<Object?> _call(String method, String path,
{Map<String, String>? query, Object? body, bool etag = false}) async {
final token = await _authToken();
final uri = _uri(path, query);
final key = uri.toString();
List<int>? bytes;
if (body != null) {
bytes = utf8.encode(jsonEncode(body));
if (bytes.length > maxBodyBytes) {
throw const AnimeFlowException(400, AnimeFlowErrorCode.invalidRequest, 'Request body too large');
}
}
for (var attempt = 0;; attempt++) {
final cached = etag ? _etags[key] : null;
final (status, text, retryAfter, tag) = await _send(method, uri, token, bytes, cached?.etag);
if (status == 304 && cached != null) return cached.body;
if (status >= 200 && status < 300) {
final j = text.trim().isEmpty ? null : _decode(text, status);
if (etag && tag != null && tag.isNotEmpty) _etags[key] = _Etagged(tag, j);
return j;
}
final err = _error(status, text);
if (err.isRateLimited && attempt < rateLimitRetries) {
final wait = retryAfter != null ? Duration(seconds: retryAfter.clamp(1, 30)) : Duration(seconds: 1 << attempt);
_log.info('AnimeFlow rate limited on $path, retrying in ${wait.inSeconds}s');
await _delay(wait);
continue;
}
throw err;
}
}
static Object? _decode(String text, int status) {
try {
return jsonDecode(text);
} catch (_) {
throw AnimeFlowException(status, AnimeFlowErrorCode.unknown, 'Response is not JSON');
}
}
static AnimeFlowException _error(int status, String text) {
Object? j;
try {
j = jsonDecode(text);
} catch (_) {}
if (j is Map && j['code'] is String) {
final code = AnimeFlowErrorCode.parse(j['code']);
return AnimeFlowException(status, code == AnimeFlowErrorCode.unknown ? AnimeFlowErrorCode.forStatus(status) : code,
_str(j['detail']) ?? 'HTTP $status');
}
// Not ours: nginx (HTML 429/502), a proxy, ...
return AnimeFlowException(status, AnimeFlowErrorCode.forStatus(status),
status == 429 ? 'Too many requests, try again shortly' : 'HTTP $status');
}
Future<(int, String, int?, String?)> _send(
String method, Uri uri, String token, List<int>? body, String? ifNoneMatch) async {
try {
final req = await _http.openUrl(method, uri).timeout(const Duration(seconds: 15));
req.headers
..set(HttpHeaders.acceptHeader, 'application/json')
..set(HttpHeaders.userAgentHeader, userAgent)
..set('X-Emby-Token', token);
if (ifNoneMatch != null) req.headers.set(HttpHeaders.ifNoneMatchHeader, ifNoneMatch);
if (body != null) {
req.headers.contentType = ContentType.json;
req.contentLength = body.length;
req.add(body);
}
final res = await req.close().timeout(const Duration(seconds: 20));
final text = await res.transform(const Utf8Decoder(allowMalformed: true)).join();
return (
res.statusCode,
text,
int.tryParse(res.headers.value(HttpHeaders.retryAfterHeader) ?? ''),
res.headers.value(HttpHeaders.etagHeader),
);
} on TimeoutException {
throw const AnimeFlowException(0, AnimeFlowErrorCode.network, 'AnimeFlow timed out');
} on IOException catch (e) {
throw AnimeFlowException(0, AnimeFlowErrorCode.network, 'AnimeFlow unreachable: $e');
}
}
// ------------------------------------------------------------ endpoints
/// Who the token belongs to and what they may do. Cached for [meTtl]
/// (scopes change at runtime).
Future<AnimeFlowMe> me({bool force = false}) async {
await _authToken();
final c = _me;
if (!force && c != null && _now().difference(c.at) < meTtl) return c.me;
final me = AnimeFlowMe.fromJson(await _call('GET', '/me'));
_me = (at: _now(), me: me);
return me;
}
/// Episodes airing in [from, to) (at most 45 days) [view].
Future<AnimeFlowScheduleWindow> schedule(DateTime from, DateTime to) async {
if (!from.isBefore(to) || to.difference(from) > maxScheduleRange) {
throw const AnimeFlowException(400, AnimeFlowErrorCode.invalidRange, 'from must be before to, at most 45 days');
}
final j = await _call('GET', '/schedule',
query: {'from': animeFlowTime(from), 'to': animeFlowTime(to)}, etag: true);
final m = j is Map ? j : const {};
return AnimeFlowScheduleWindow(
from: _time(m['from']),
to: _time(m['to']),
items: [for (final e in _items(j)) AnimeFlowScheduleItem.fromJson(e)],
);
}
/// Every tracked series [view].
Future<List<AnimeFlowSeries>> series() async =>
[for (final s in _items(await _call('GET', '/series', etag: true))) AnimeFlowSeries.fromJson(s)];
/// Every series on Jellyfin (as the caller sees it), tracked by
/// AnimeFlow or not [view]. Empty on servers without the endpoint.
Future<List<AnimeFlowLibraryItem>> library() async {
try {
return [for (final s in _items(await _call('GET', '/library', etag: true))) AnimeFlowLibraryItem.fromJson(s)];
} on AnimeFlowException catch (e) {
if (e.isNotFound) return const [];
rethrow;
}
}
/// One series with all its episodes [view].
Future<AnimeFlowSeries> seriesDetail(int seriesId) async {
final j = await _call('GET', '/series/$seriesId', etag: true);
return AnimeFlowSeries.fromJson(j is Map ? j : const {});
}
/// Follow (or unfollow) a series [view].
Future<void> setFollowed(int seriesId, bool followed) =>
_call(followed ? 'PUT' : 'DELETE', '/series/$seriesId/follow');
/// Titles that can be requested [request].
Future<List<AnimeFlowSearchHit>> search(String q) async => [
for (final h in _items(await _call('GET', '/search', query: {'q': q}))) AnimeFlowSearchHit.fromJson(h),
];
/// Asks for a whole new series by its AniDB id [request].
Future<AnimeFlowRequest> requestSeries(int anidbId) => _request({'anidb_id': anidbId});
/// Asks for one season of a tracked series [request].
Future<AnimeFlowRequest> requestSeason(int seriesId, int season) =>
_request({'series_id': seriesId, 'season': season});
/// Asks for one episode of a tracked series [request].
Future<AnimeFlowRequest> requestEpisode(int seriesId, int season, int episode) =>
_request({'series_id': seriesId, 'season': season, 'episode': episode});
Future<AnimeFlowRequest> _request(Map<String, Object> body) async {
final j = await _call('POST', '/requests', body: body);
return AnimeFlowRequest.fromJson(j is Map ? j : const {});
}
/// Own requests, newest first [view]; [all] = everyone's, with the user
/// [manage].
Future<List<AnimeFlowRequest>> requests({bool all = false}) async => [
for (final r in _items(await _call('GET', '/requests', query: all ? {'scope': 'all'} : null, etag: true)))
AnimeFlowRequest.fromJson(r),
];
/// [manage]. Returns the updated request when the server sends one.
Future<AnimeFlowRequest?> approve(int requestId) async =>
_maybeRequest(await _call('POST', '/requests/$requestId/approve'));
/// [manage].
Future<AnimeFlowRequest?> reject(int requestId, {String? reason}) async => _maybeRequest(
await _call('POST', '/requests/$requestId/reject', body: {'reason': ?reason}));
static AnimeFlowRequest? _maybeRequest(Object? j) =>
j is Map && j['id'] != null ? AnimeFlowRequest.fromJson(j) : null;
/// [manage].
Future<List<AnimeFlowUser>> users() async =>
[for (final u in _items(await _call('GET', '/users'))) AnimeFlowUser.fromJson(u)];
/// Sets a user's scopes (and revocation) [manage]. Returns the updated
/// user when the server sends one.
Future<AnimeFlowUser?> setScopes(String jellyfinUserId, Set<AnimeFlowScope> scopes, {bool? revoked}) async {
final j = await _call('PUT', '/users/${Uri.encodeComponent(jellyfinUserId)}/scopes', body: {
'scopes': [for (final s in AnimeFlowScope.values) if (scopes.contains(s)) s.name],
'revoked': ?revoked,
});
_me = null; // could be our own
return j is Map && j['user_id'] != null ? AnimeFlowUser.fromJson(j) : null;
}
/// Forgets cached bodies and /me (e.g. on a manual refresh).
void clearCache() {
_etags.clear();
_me = null;
}
void close() {
if (_ownsHttp) _http.close(force: true);
}
}
@@ -0,0 +1,296 @@
import 'dart:async';
import 'anilist.dart';
import 'animeflow.dart';
// The Schedule tab's data: AniList says what airs when, AnimeFlow says
// what the media server has of it. [matchSchedule] merges the two and
// [ScheduleRepository] loads a window of both, degrading to AniList alone
// when AnimeFlow is unreachable or not linked.
enum ScheduleStatus {
available,
downloading,
upcoming,
missing,
failed,
unmonitored,
/// AnimeFlow does not track this series (or is unreachable).
notTracked,
/// Not tracked by AnimeFlow, but the series is on Jellyfin (`/library`):
/// the episode may be there; Put it up finds out.
inLibrary;
static ScheduleStatus of(EpisodeStatus s) => switch (s) {
EpisodeStatus.available => available,
EpisodeStatus.downloading => downloading,
EpisodeStatus.upcoming => upcoming,
EpisodeStatus.missing => missing,
EpisodeStatus.failed => failed,
EpisodeStatus.unmonitored => unmonitored,
};
}
/// One airing with what AnimeFlow knows about it.
class ScheduleEntry {
final Airing airing;
final ScheduleStatus status;
/// 0..1 while downloading.
final double? progress;
/// Why it failed.
final String? reason;
/// AnimeFlow tracks the series.
final bool tracked;
final bool followed;
/// AnimeFlow's series id (follow, requests).
final int? seriesId;
/// The AnimeFlow season of the matched episode (requests).
final int? season;
/// Play target once available.
final String? jellyfinSeriesId;
/// The series is on Jellyfin (tracked or not).
bool get onJellyfin => jellyfinSeriesId != null;
const ScheduleEntry({
required this.airing,
required this.status,
this.progress,
this.reason,
this.tracked = false,
this.followed = false,
this.seriesId,
this.season,
this.jellyfinSeriesId,
});
@override
String toString() => 'ScheduleEntry(${airing.anilistId}#${airing.episode} $status)';
}
/// Without AnimeFlow: future airings are upcoming, past ones unknown.
ScheduleEntry untrackedEntry(Airing a, DateTime now) => ScheduleEntry(
airing: a,
status: a.airingAt.isAfter(now) ? ScheduleStatus.upcoming : ScheduleStatus.notTracked,
);
String _day(DateTime d) {
final l = d.toLocal();
return '${l.year}-${l.month}-${l.day}';
}
class _SeriesRef {
final int seriesId;
final String? jellyfinSeriesId;
bool followed;
_SeriesRef(this.seriesId, this.jellyfinSeriesId, this.followed);
}
/// Merges AniList [airings] with AnimeFlow's [schedule] rows and tracked
/// [series]. Per airing: the episode with the same AniList id (that
/// episode's season's entry) and episode number, else absolute number,
/// else the same local air day; unmatched airings of a tracked AniList id
/// are missing once aired, upcoming before; the rest are not tracked.
List<ScheduleEntry> matchSchedule(
List<Airing> airings, {
required List<AnimeFlowScheduleItem> schedule,
List<AnimeFlowSeries> series = const [],
List<AnimeFlowLibraryItem> library = const [],
DateTime? now,
}) {
final at = now ?? DateTime.now();
// AniList id -> (Jellyfin series, its TVDB season) from /library.
final lib = <int, (String, int)>{};
for (final item in library) {
if (item.jellyfinSeriesId.isEmpty) continue;
for (final r in item.anilistMap) {
lib[r.anilistId] ??= (item.jellyfinSeriesId, r.season);
}
for (final e in item.anilistIds.entries) {
lib[e.value] ??= (item.jellyfinSeriesId, e.key);
}
}
final refs = <int, _SeriesRef>{};
for (final s in series) {
final id = s.anilistId;
if (id != null) refs[id] ??= _SeriesRef(s.seriesId, s.jellyfinSeriesId, s.followed);
}
final byEp = <String, AnimeFlowScheduleItem>{};
final byAbs = <String, AnimeFlowScheduleItem>{};
final byDay = <String, AnimeFlowScheduleItem>{};
for (final it in schedule) {
final e = it.episode, id = e.anilistId;
if (id == null) continue;
final ref = refs[id] ??= _SeriesRef(it.seriesId, it.jellyfinSeriesId, it.followed);
if (it.followed) ref.followed = true;
// AnimeFlow's AniList episode number first: TVDB numbering can run on
// across split cours.
if (e.anilistEpisode != null) byEp.putIfAbsent('$id#${e.anilistEpisode}', () => it);
byEp.putIfAbsent('$id#${e.episode}', () => it);
if (e.absolute != null) byAbs.putIfAbsent('$id#${e.absolute}', () => it);
if (e.airDate != null) byDay.putIfAbsent('$id@${_day(e.airDate!)}', () => it);
}
return [
for (final a in airings)
() {
final id = a.anilistId;
final ref = refs[id];
final it = byEp['$id#${a.episode}'] ?? byAbs['$id#${a.episode}'] ?? byDay['$id@${_day(a.airingAt)}'];
if (it != null) {
final e = it.episode;
return ScheduleEntry(
airing: a,
status: ScheduleStatus.of(e.status),
progress: e.status == EpisodeStatus.downloading ? e.progress : null,
reason: e.status == EpisodeStatus.failed ? e.reason : null,
tracked: true,
followed: it.followed || (ref?.followed ?? false),
seriesId: it.seriesId,
season: e.season,
jellyfinSeriesId: it.jellyfinSeriesId ?? ref?.jellyfinSeriesId ?? lib[id]?.$1,
);
}
if (ref == null) {
final onJf = lib[id];
if (onJf == null) return ScheduleEntry(airing: a, status: ScheduleStatus.notTracked);
return ScheduleEntry(
airing: a,
status: a.airingAt.isAfter(at) ? ScheduleStatus.upcoming : ScheduleStatus.inLibrary,
season: onJf.$2,
jellyfinSeriesId: onJf.$1,
);
}
return ScheduleEntry(
airing: a,
status: a.airingAt.isAfter(at) ? ScheduleStatus.upcoming : ScheduleStatus.missing,
tracked: true,
followed: ref.followed,
seriesId: ref.seriesId,
jellyfinSeriesId: ref.jellyfinSeriesId ?? lib[id]?.$1,
);
}(),
];
}
/// One loaded window. Errors are display text; [entries] is empty only
/// when AniList failed with nothing cached.
class ScheduleSnapshot {
final DateTime from;
final DateTime to;
/// Sorted by air time.
final List<ScheduleEntry> entries;
/// AniList failed and nothing was cached.
final String? aniListError;
/// AniList failed; [entries] come from the cache fetched then.
final DateTime? aniListStaleSince;
/// AnimeFlow is unreachable, refused or not linked: statuses are
/// AniList-only (upcoming / not tracked).
final String? animeFlowError;
/// The structured AnimeFlow failure behind [animeFlowError].
final AnimeFlowException? animeFlowException;
/// Who we are on AnimeFlow (null when it failed).
final AnimeFlowMe? me;
const ScheduleSnapshot({
required this.from,
required this.to,
required this.entries,
this.aniListError,
this.aniListStaleSince,
this.animeFlowError,
this.animeFlowException,
this.me,
});
bool get hasAnimeFlow => me != null;
}
/// Loads AniList and AnimeFlow for a window and merges them.
class ScheduleRepository {
ScheduleRepository({required this.aniList, this.animeFlow, DateTime Function()? now})
: _now = now ?? DateTime.now;
final AniListClient aniList;
/// Null = not configured (AniList only).
final AnimeFlowClient? animeFlow;
final DateTime Function() _now;
/// Last week's Monday 00:00 local .. 4 weeks later.
static ({DateTime from, DateTime to}) defaultWindow(DateTime now) {
final l = now.toLocal();
final from = DateTime(l.year, l.month, l.day - (l.weekday - 1) - 7);
return (from: from, to: DateTime(from.year, from.month, from.day + 28));
}
Future<ScheduleSnapshot> load({DateTime? from, DateTime? to, bool force = false}) async {
final w = defaultWindow(_now());
final start = from ?? w.from, end = to ?? w.to;
final flowF = _animeFlow(start, end, force);
final AniListSchedule ani;
try {
ani = await aniList.schedule(start, end, force: force);
} catch (e) {
unawaited(flowF.then((_) {}, onError: (_) {}));
return ScheduleSnapshot(from: start, to: end, entries: const [], aniListError: _text(e));
}
final now = _now();
final flow = await flowF;
final airings = [...ani.airings]..sort((a, b) => a.airingAt.compareTo(b.airingAt));
final entries = flow.error == null
? matchSchedule(airings, schedule: flow.schedule, series: flow.series, library: flow.library, now: now)
: [for (final a in airings) untrackedEntry(a, now)];
return ScheduleSnapshot(
from: start,
to: end,
entries: entries,
aniListStaleSince: ani.staleSince,
animeFlowError: flow.error == null ? null : _text(flow.error!),
animeFlowException: flow.error is AnimeFlowException ? flow.error as AnimeFlowException : null,
me: flow.me,
);
}
Future<({AnimeFlowMe? me, List<AnimeFlowScheduleItem> schedule, List<AnimeFlowSeries> series, List<AnimeFlowLibraryItem> library, Object? error})>
_animeFlow(DateTime from, DateTime to, bool force) async {
final c = animeFlow;
if (c == null) {
return (me: null, schedule: const <AnimeFlowScheduleItem>[], series: const <AnimeFlowSeries>[],
library: const <AnimeFlowLibraryItem>[],
error: 'AnimeFlow is not configured');
}
try {
if (force) c.clearCache();
final me = await c.me();
final (sched, series) = await (c.schedule(from, to), c.series()).wait;
// The library only adds "on Jellyfin" for untracked shows: its
// failure never fails the schedule.
List<AnimeFlowLibraryItem> library = const [];
try {
library = await c.library();
} catch (_) {}
return (me: me, schedule: sched.items, series: series, library: library, error: null);
} on ParallelWaitError catch (e) {
final errs = e.errors as (AsyncError?, AsyncError?);
return (
me: null,
schedule: const <AnimeFlowScheduleItem>[],
series: const <AnimeFlowSeries>[],
library: const <AnimeFlowLibraryItem>[],
error: (errs.$1 ?? errs.$2)!.error,
);
} catch (e) {
return (me: null, schedule: const <AnimeFlowScheduleItem>[], series: const <AnimeFlowSeries>[], library: const <AnimeFlowLibraryItem>[], error: e);
}
}
static String _text(Object e) => switch (e) {
AnimeFlowException(:final code, :final detail) => switch (code) {
AnimeFlowErrorCode.missingToken => 'Link the Jellyfin server to see AnimeFlow statuses',
AnimeFlowErrorCode.badToken => 'AnimeFlow: sign in to Jellyfin again',
AnimeFlowErrorCode.revoked => 'AnimeFlow: access revoked',
AnimeFlowErrorCode.rateLimited => 'AnimeFlow: too many requests, try again shortly',
_ => 'AnimeFlow: $detail',
},
AniListException(:final message) => message,
_ => '$e',
};
}
@@ -0,0 +1,175 @@
import 'package:flutter/material.dart';
import '../../shell/avatar.dart';
import '../animeflow.dart';
// The Schedule page's side sheet: AnimeFlow requests (everyone's for the
// owner, who can retry failed ones and decline the rest; own otherwise).
Color requestColor(RequestStatus s) => switch (s) {
RequestStatus.pending || RequestStatus.searching => Colors.amber,
RequestStatus.approved => Colors.lightBlueAccent,
RequestStatus.rejected || RequestStatus.failed => Colors.redAccent,
RequestStatus.done => Colors.greenAccent,
};
String requestWhat(AnimeFlowRequest r) => switch (r.kind) {
RequestKind.series => r.title,
RequestKind.season => '${r.title} · season ${r.season ?? '?'}',
RequestKind.episode => '${r.title} · S${r.season ?? '?'}E${r.episode ?? '?'}',
};
class RequestsSheet extends StatelessWidget {
/// Scope manage: everyone's requests, Retry and Decline.
final bool owner;
/// Null while loading.
final List<AnimeFlowRequest>? requests;
final String? error;
/// Our own name, for requests without a user name.
final String selfName;
/// Requests with a call in flight.
final Set<int> busy;
final void Function(AnimeFlowRequest r) onRetry;
final void Function(AnimeFlowRequest r) onDecline;
final VoidCallback onClose;
const RequestsSheet({
super.key,
required this.owner,
required this.requests,
required this.selfName,
required this.onRetry,
required this.onDecline,
required this.onClose,
this.error,
this.busy = const {},
});
Widget _tile(BuildContext context, AnimeFlowRequest r) {
final scheme = Theme.of(context).colorScheme;
final who = r.userName ?? (selfName.isEmpty ? 'you' : selfName);
final canRetry = owner && r.status == RequestStatus.failed;
final canDecline =
owner &&
(r.status == RequestStatus.pending ||
r.status == RequestStatus.approved ||
r.status == RequestStatus.searching);
final inFlight = busy.contains(r.id);
return Padding(
key: ValueKey('request-${r.id}'),
padding: const EdgeInsets.symmetric(vertical: 6),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StatusAvatar(
info: AvatarInfo(name: who, color: colorForName(who)),
size: 32,
ring: false,
badges: false,
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(requestWhat(r), style: const TextStyle(fontWeight: FontWeight.w600)),
const SizedBox(height: 2),
Wrap(
spacing: 8,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Text(who, style: TextStyle(color: scheme.onSurfaceVariant, fontSize: 12)),
Pill(r.status.name, requestColor(r.status)),
],
),
if (r.reason != null && r.reason!.isNotEmpty)
Text(r.reason!, style: TextStyle(color: scheme.onSurfaceVariant, fontSize: 11)),
if (canRetry || canDecline)
Padding(
padding: const EdgeInsets.only(top: 6),
child: Row(
children: [
if (canRetry)
FilledButton.tonal(
key: ValueKey('retry-${r.id}'),
onPressed: inFlight ? null : () => onRetry(r),
child: const Text('Retry'),
),
if (canDecline)
TextButton(
key: ValueKey('decline-${r.id}'),
onPressed: inFlight ? null : () => onDecline(r),
child: const Text('Decline'),
),
if (inFlight) ...[
const SizedBox(width: 8),
const SizedBox(width: 14, height: 14, child: CircularProgressIndicator(strokeWidth: 2)),
],
],
),
),
],
),
),
],
),
);
}
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final reqs = requests;
return Material(
color: scheme.surfaceContainerLow,
child: ListView(
padding: const EdgeInsets.fromLTRB(16, 10, 8, 16),
children: [
Row(
children: [
Text(
owner ? 'Requests' : 'My requests',
style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 16),
),
const Spacer(),
IconButton(
key: const ValueKey('requests-close'),
onPressed: onClose,
tooltip: 'Close',
icon: const Icon(Icons.close, size: 18),
),
],
),
Text(
owner
? 'Everyone\'s requests. AnimeFlow approves them automatically; you can decline or retry.'
: 'AnimeFlow downloads requests automatically; they appear on Jellyfin when done.',
style: TextStyle(color: scheme.onSurfaceVariant, fontSize: 12),
),
const SizedBox(height: 12),
if (error != null)
Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Text(error!, style: TextStyle(color: scheme.error, fontSize: 12)),
),
if (reqs == null && error == null)
const Padding(
padding: EdgeInsets.only(top: 24),
child: Center(child: SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2))),
)
else if (reqs != null && reqs.isEmpty)
Padding(
padding: const EdgeInsets.only(top: 24),
child: Center(
child: Text('No requests yet', style: TextStyle(color: scheme.onSurfaceVariant)),
),
),
for (final r in reqs ?? const <AnimeFlowRequest>[]) _tile(context, r),
],
),
);
}
}
@@ -0,0 +1,143 @@
import 'package:sync_relay_core/core.dart';
import '../../jellyfin/accounts.dart';
import '../../jellyfin/format.dart' show pickFor;
import '../../screens/home/picked_media.dart';
import '../anilist.dart';
import '../animeflow.dart';
import '../schedule_model.dart';
// What the Schedule page's buttons do beyond one client call: friendly
// AnimeFlow errors, finding AnimeFlow's own numbering of an airing, the
// AniDB id of an untracked AniList entry, and the Jellyfin episode to put
// up.
/// A failure meant for the user as is.
class ScheduleActionException implements Exception {
final String message;
const ScheduleActionException(this.message);
@override
String toString() => message;
}
/// [e] as one short sentence for a toast.
String friendlyError(Object e) => switch (e) {
ScheduleActionException(:final message) => message,
AnimeFlowException(:final code, :final detail) => switch (code) {
AnimeFlowErrorCode.alreadyRequested => 'Already requested',
AnimeFlowErrorCode.alreadyTracked => 'AnimeFlow already tracks this series',
AnimeFlowErrorCode.alreadyAvailable => 'Already on Jellyfin',
AnimeFlowErrorCode.missingScope => 'The owner has not allowed you to do that on AnimeFlow',
AnimeFlowErrorCode.revoked => 'Your AnimeFlow access was revoked',
AnimeFlowErrorCode.missingToken => 'Link the Jellyfin server to use AnimeFlow',
AnimeFlowErrorCode.badToken => 'AnimeFlow: sign in to Jellyfin again',
AnimeFlowErrorCode.ownAccount => 'You cannot change your own account',
AnimeFlowErrorCode.searchUnavailable => 'AniDB search is unavailable right now',
AnimeFlowErrorCode.seriesNotFound => 'AnimeFlow does not know this series',
AnimeFlowErrorCode.episodeNotFound => 'AnimeFlow does not know this episode',
AnimeFlowErrorCode.requestNotFound => 'That request is gone',
AnimeFlowErrorCode.notFound => 'AnimeFlow: not found',
AnimeFlowErrorCode.invalidState => 'That cannot be done in its current state',
AnimeFlowErrorCode.rateLimited => 'AnimeFlow is busy, try again shortly',
AnimeFlowErrorCode.network => 'AnimeFlow is unreachable',
AnimeFlowErrorCode.jellyfinUnreachable => 'AnimeFlow cannot reach Jellyfin right now',
_ => 'AnimeFlow: $detail',
},
JellyfinException(:final message) => 'Jellyfin: $message',
_ => '$e',
};
bool _sameDay(DateTime a, DateTime b) {
final x = a.toLocal(), y = b.toLocal();
return x.year == y.year && x.month == y.month && x.day == y.day;
}
/// AnimeFlow's episode for [e] (its season and numbering), from the
/// series detail: same AniList id and episode, else absolute number, else
/// the same air day. Null when the entry is not tracked or nothing fits.
Future<AnimeFlowEpisode?> flowEpisodeFor(AnimeFlowClient client, ScheduleEntry e) async {
final id = e.seriesId;
if (id == null) return null;
final eps = (await client.seriesDetail(id)).episodes;
final a = e.airing;
final mine = eps.where((x) => x.anilistId == null || x.anilistId == a.anilistId).toList();
return mine.where((x) => x.anilistId == a.anilistId && x.episode == a.episode).firstOrNull ??
mine.where((x) => x.absolute == a.episode).firstOrNull ??
mine.where((x) => x.airDate != null && _sameDay(x.airDate!, a.airingAt)).firstOrNull;
}
/// The (season, episode) AnimeFlow uses for [e]; falls back to the
/// matched season and AniList's number when the detail has nothing.
Future<({int season, int episode})> flowNumbering(AnimeFlowClient? client, ScheduleEntry e) async {
AnimeFlowEpisode? ep;
if (client != null) {
try {
ep = await flowEpisodeFor(client, e);
} on AnimeFlowException catch (err) {
if (err.isUnauthorized || err.isForbidden) rethrow;
}
}
if (ep != null) return (season: ep.season, episode: ep.episode);
return (season: e.season ?? 1, episode: e.airing.episode);
}
/// The AniDB id of an untracked AniList entry, from AnimeFlow's search:
/// the hit with the same AniList id, searched by English then romaji
/// title. Otherwise every hit seen, for the user to choose from.
Future<({AnimeFlowSearchHit? match, List<AnimeFlowSearchHit> hits})> findOnAniDb(
AnimeFlowClient client,
Airing a,
) async {
final hits = <AnimeFlowSearchHit>[];
final queries = {a.title, if (a.titleRomaji.isNotEmpty) a.titleRomaji};
for (final q in queries) {
final found = await client.search(q);
final match = found.where((h) => h.anilistId == a.anilistId).firstOrNull;
if (match != null) return (match: match, hits: found);
for (final h in found) {
if (hits.every((x) => x.anidbId != h.anidbId)) hits.add(h);
}
}
return (match: null, hits: hits);
}
/// The linked Jellyfin account AnimeFlow's series ids belong to: the one
/// AnimeFlow authenticates with, else the default.
JellyfinAccount? jellyfinAccountFor(JellyfinAccounts accounts, AnimeFlowClient? flow) {
if (flow != null) {
final token = AnimeFlowClient.tokenFor(accounts.accounts, flow.baseUrl);
final a = accounts.accounts.where((a) => token != null && a.token == token).firstOrNull;
if (a != null) return a;
}
return accounts.defaultAccount;
}
/// The Jellyfin episode of an available [e] as a media pick. Throws
/// [ScheduleActionException] (or a client error) when it cannot be found.
Future<JellyfinMedia> resolveJellyfinEpisode(JellyfinAccounts accounts, AnimeFlowClient? flow, ScheduleEntry e) async {
final seriesId = e.jellyfinSeriesId;
if (seriesId == null) throw const ScheduleActionException('This series is not on Jellyfin yet');
await accounts.ensureLoaded(validate: false);
final account = jellyfinAccountFor(accounts, flow);
final client = account == null ? null : accounts.clientFor(account.serverId);
if (account == null || client == null) {
throw const ScheduleActionException('Link the Jellyfin server first (Settings)');
}
final n = await flowNumbering(flow, e);
final List<JellyfinItem> eps;
try {
eps = await client.episodes(seriesId);
} on JellyfinException catch (err) {
if (err.isUnauthorized) accounts.markExpired(account.serverId);
rethrow;
}
final a = e.airing;
final item =
eps.where((x) => x.parentIndexNumber == n.season && x.indexNumber == n.episode).firstOrNull ??
eps.where((x) => x.premiereDate != null && _sameDay(x.premiereDate!, a.airingAt)).singleOrNull ??
eps.where((x) => x.indexNumber == a.episode).singleOrNull;
if (item == null) {
throw ScheduleActionException('Episode ${a.episode} of ${a.title} is not on Jellyfin yet');
}
return pickFor(account, item);
}
@@ -0,0 +1,389 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import '../schedule_model.dart';
// One day of the Schedule board and its airing cards: cover poster on the
// left (air time on it, download progress under it), title, episode and
// status on the right, one full-width action below. Compact mode is a
// slim row.
/// What a card's action does, decided by the page.
enum CardAction {
none,
putUp,
/// Available, nobody else watching (offline or solo): plays it here.
play,
request,
requestSeries,
requested,
}
String _two(int n) => n.toString().padLeft(2, '0');
String clockText(DateTime t) {
final l = t.toLocal();
return '${_two(l.hour)}:${_two(l.minute)}';
}
String countdownText(DateTime at, DateTime now) {
final d = at.difference(now);
if (d.isNegative) return 'aired';
if (d.inDays > 0) return 'in ${d.inDays}d ${d.inHours % 24}h';
if (d.inHours > 0) return 'in ${d.inHours}h ${d.inMinutes % 60}m';
return 'in ${d.inMinutes}m';
}
/// Status line: text, colour, icon.
(String, Color, IconData) statusBadge(ScheduleEntry e, DateTime now) => switch (e.status) {
ScheduleStatus.available => ('on Jellyfin', Colors.greenAccent, Icons.check_circle),
ScheduleStatus.downloading => (
e.progress == null ? 'downloading' : 'downloading ${(e.progress! * 100).round()}%',
Colors.lightBlueAccent,
Icons.downloading,
),
ScheduleStatus.upcoming => (countdownText(e.airing.airingAt, now), Colors.amber, Icons.schedule),
ScheduleStatus.missing => ('missing', Colors.orangeAccent, Icons.report_gmailerrorred),
ScheduleStatus.failed => ('failed${e.reason == null ? '' : ' · ${e.reason}'}', Colors.redAccent, Icons.error_outline),
ScheduleStatus.unmonitored => ('not monitored', Colors.grey, Icons.visibility_off_outlined),
ScheduleStatus.notTracked => ('not in AnimeFlow', Colors.grey, Icons.public),
ScheduleStatus.inLibrary => ('on Jellyfin', Colors.greenAccent, Icons.video_library_outlined),
};
class DayColumn extends StatelessWidget {
final DateTime day;
final bool today, past;
final List<Widget> cards;
final bool compact;
const DayColumn({
super.key,
required this.day,
required this.today,
required this.past,
required this.cards,
required this.compact,
});
static const _wd = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return Container(
decoration: BoxDecoration(
color: today ? scheme.primary.withValues(alpha: 0.06) : scheme.surfaceContainerLow.withValues(alpha: 0.5),
borderRadius: BorderRadius.circular(14),
border: today ? Border.all(color: scheme.primary.withValues(alpha: 0.5)) : null,
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(12, 10, 12, 8),
child: Row(
children: [
Text(
today ? 'Today' : _wd[day.weekday - 1],
style: TextStyle(fontWeight: FontWeight.w700, color: today ? scheme.primary : null),
),
const SizedBox(width: 6),
Text('${day.day}.${day.month}.', style: TextStyle(color: scheme.onSurfaceVariant)),
const Spacer(),
if (cards.isNotEmpty)
Text('${cards.length}', style: TextStyle(color: scheme.onSurfaceVariant, fontSize: 12)),
],
),
),
Expanded(
child: Opacity(
opacity: past ? 0.85 : 1,
child: cards.isEmpty
? Center(
child: Text('—', style: TextStyle(color: scheme.onSurfaceVariant)),
)
// The board's mouse drag is sideways only: a column scrolls by
// wheel (or touch), never by dragging the mouse.
: ScrollConfiguration(
behavior: ScrollConfiguration.of(context)
.copyWith(dragDevices: {...PointerDeviceKind.values}..remove(PointerDeviceKind.mouse)),
child: ListView.separated(
padding: const EdgeInsets.fromLTRB(8, 0, 8, 10),
itemCount: cards.length,
separatorBuilder: (_, _) => SizedBox(height: compact ? 6 : 10),
itemBuilder: (context, i) => cards[i],
),
),
),
),
],
),
);
}
}
class AiringCard extends StatelessWidget {
final ScheduleEntry entry;
final DateTime now;
final bool compact;
final bool followed;
final CardAction action;
/// Why [action] is disabled (no request scope, not on Jellyfin, ...);
/// null = enabled.
final String? disabledHint;
/// A call for this card is in flight.
final bool busy;
final VoidCallback onAction;
/// Null hides the bookmark (untracked series).
final VoidCallback? onFollow;
const AiringCard({
super.key,
required this.entry,
required this.now,
required this.compact,
required this.followed,
required this.action,
required this.onAction,
this.disabledHint,
this.busy = false,
this.onFollow,
});
String get _id => '${entry.airing.anilistId}-${entry.airing.episode}';
Widget _image(String? url, Widget fallback) =>
url == null ? fallback : Image.network(url, fit: BoxFit.cover, errorBuilder: (_, _, _) => fallback);
Widget? _button(BuildContext context) {
final enabled = disabledHint == null && !busy;
final spinner = const SizedBox(width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2));
Widget? b = switch (action) {
CardAction.none => null,
CardAction.putUp || CardAction.play => FilledButton.tonalIcon(
onPressed: enabled ? onAction : null,
icon: busy ? spinner : const Icon(Icons.play_arrow, size: 18),
label: Text(action == CardAction.play ? 'Play' : 'Put it up'),
),
CardAction.request || CardAction.requestSeries => OutlinedButton.icon(
onPressed: enabled ? onAction : null,
icon: busy ? spinner : const Icon(Icons.download, size: 18),
label: Text(action == CardAction.request ? 'Request' : 'Request series'),
),
CardAction.requested => const OutlinedButton(onPressed: null, child: Text('Requested')),
};
if (b == null) return null;
b = KeyedSubtree(key: ValueKey('action-$_id'), child: b);
return disabledHint == null ? b : Tooltip(message: disabledHint!, child: b);
}
Widget? _follow(BuildContext context, double size) {
final f = onFollow;
if (f == null) return null;
return IconButton(
key: ValueKey('follow-$_id'),
onPressed: f,
tooltip: followed ? 'Stop following' : 'Follow',
visualDensity: VisualDensity.compact,
padding: EdgeInsets.zero,
constraints: BoxConstraints.tightFor(width: size + 10, height: size + 10),
iconSize: size,
color: followed ? Theme.of(context).colorScheme.primary : null,
icon: Icon(followed ? Icons.bookmark : Icons.bookmark_outline),
);
}
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final a = entry.airing;
final (label, color, icon) = statusBadge(entry, now);
final time = clockText(a.airingAt);
final tintColor = a.color ?? Colors.blueGrey;
final tint = DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [tintColor.withValues(alpha: 0.85), tintColor.withValues(alpha: 0.35)],
),
),
);
final tap = (action == CardAction.putUp || action == CardAction.play) && disabledHint == null && !busy ? onAction : null;
if (compact) {
final requestable = action == CardAction.request || action == CardAction.requestSeries;
return Material(
key: ValueKey('card-$_id'),
color: scheme.surfaceContainer,
borderRadius: BorderRadius.circular(10),
child: InkWell(
borderRadius: BorderRadius.circular(10),
onTap: tap,
child: Padding(
padding: const EdgeInsets.all(6),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(6),
child: SizedBox(width: 34, height: 48, child: _image(a.cover, tint)),
),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
a.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 13),
),
Text('$time · E${a.episode}', style: TextStyle(color: scheme.onSurfaceVariant, fontSize: 11)),
],
),
),
if (followed && onFollow == null) Icon(Icons.bookmark, size: 14, color: scheme.primary),
if (requestable)
Tooltip(
message: disabledHint ?? (action == CardAction.request ? 'Request' : 'Request series'),
child: IconButton(
key: ValueKey('action-$_id'),
onPressed: disabledHint == null && !busy ? onAction : null,
visualDensity: VisualDensity.compact,
iconSize: 18,
icon: Icon(Icons.download, color: color),
),
)
else
Tooltip(
message: label,
child: Icon(icon, size: 16, color: color),
),
],
),
),
),
);
}
final button = _button(context);
final follow = _follow(context, 16);
// The cover poster leads: it is what people recognise at a glance.
return Material(
key: ValueKey('card-$_id'),
color: scheme.surfaceContainer,
borderRadius: BorderRadius.circular(12),
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: tap,
child: Padding(
padding: const EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: SizedBox(
width: 84,
height: 120,
child: Stack(
fit: StackFit.expand,
children: [
_image(a.cover, tint),
Positioned(
left: 4,
top: 4,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 1),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.7),
borderRadius: BorderRadius.circular(5),
),
child: Text(
time,
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 12),
),
),
),
if (entry.status == ScheduleStatus.downloading)
Positioned(
left: 0,
right: 0,
bottom: 0,
child: LinearProgressIndicator(
value: entry.progress,
minHeight: 3,
color: Colors.lightBlueAccent,
),
),
],
),
),
),
const SizedBox(width: 10),
Expanded(
child: SizedBox(
height: 120,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
a.title,
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w600, height: 1.25),
),
const SizedBox(height: 2),
Row(
children: [
Flexible(
child: Text(
'Episode ${a.episode}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: scheme.onSurfaceVariant, fontSize: 12),
),
),
const SizedBox(width: 2),
if (follow != null)
follow
else if (followed)
Padding(
padding: const EdgeInsets.only(left: 4),
child: Icon(Icons.bookmark, size: 14, color: scheme.primary),
),
],
),
const Spacer(),
Row(
children: [
Icon(icon, size: 14, color: color),
const SizedBox(width: 4),
Expanded(
child: Text(
label,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: color, fontSize: 12),
),
),
],
),
],
),
),
),
],
),
if (button != null) ...[const SizedBox(height: 8), SizedBox(width: double.infinity, child: button)],
],
),
),
),
);
}
}
@@ -0,0 +1,587 @@
import 'dart:async';
import 'package:flutter/material.dart';
import '../../jellyfin/accounts.dart';
import '../../screens/home/picked_media.dart';
import '../../shell/avatar.dart';
import '../../shell/motion.dart';
import '../anilist.dart';
import '../animeflow.dart';
import '../schedule_model.dart';
import 'requests_sheet.dart';
import 'schedule_actions.dart';
import 'schedule_card.dart';
// The Schedule tab: one column per day from last week's Monday to four
// weeks out (AniList), each airing with what AnimeFlow has of it, and the
// one thing to do about it (put it up, request it). Refreshes every
// minute while visible; ETags keep that cheap.
enum ScheduleFilter { jellyfin, following, all }
class SchedulePage extends StatefulWidget {
/// Nobody else watches (offline or solo): available episodes say Play
/// (offline that starts a solo session).
final bool solo;
/// Puts a Jellyfin episode up in the server; returns an error to show.
final Future<String?> Function(JellyfinMedia pick) onPutUp;
final void Function(String message) onToast;
/// Null = the page makes (and closes) its own.
final ScheduleRepository? repository;
/// Linked Jellyfin servers for Put it up; null = [JellyfinAccounts.instance].
final JellyfinAccounts? accounts;
const SchedulePage({
super.key,
this.solo = false,
required this.onPutUp,
required this.onToast,
this.repository,
this.accounts,
});
static const columnWidth = 252.0;
static const columnGap = 12.0;
static const refreshEvery = Duration(seconds: 60);
/// "All airing" hides entries below this AniList popularity unless
/// "Niche too" is on (tracked ones always show).
static const nicheBelow = 5000;
static const sheetWidth = 360.0;
@override
State<SchedulePage> createState() => _SchedulePageState();
}
class _SchedulePageState extends State<SchedulePage> {
late final ScheduleRepository _repo;
late final bool _ownsRepo;
ScheduleSnapshot? _snap;
bool _refreshing = false;
int _gen = 0;
Timer? _timer;
final _scroll = ScrollController();
bool _jumped = false;
ScheduleFilter _filter = ScheduleFilter.jellyfin;
bool _compact = false;
bool _niche = false;
bool _sheet = false;
/// The sheet stays built while it slides out.
bool _sheetBuilt = false;
/// Follow toggles not yet confirmed by a reload (series id -> followed).
final _followOverride = <int, bool>{};
/// Requested in this run: "anilistId#episode" or "series:anilistId".
final _requested = <String>{};
/// Cards with a call in flight (same keys as [_requested], "play:...").
final _busy = <String>{};
List<AnimeFlowRequest>? _requests;
String? _requestsError;
final _reqBusy = <int>{};
JellyfinAccounts get _accounts => widget.accounts ?? JellyfinAccounts.instance;
AnimeFlowClient? get _flow => _repo.animeFlow;
AnimeFlowMe? get _me => _snap?.me;
bool get _owner => _me?.can(AnimeFlowScope.manage) ?? false;
@override
void initState() {
super.initState();
_ownsRepo = widget.repository == null;
_repo =
widget.repository ??
ScheduleRepository(
aniList: AniListClient(),
animeFlow: AnimeFlowClient.fromAccounts(JellyfinAccounts.instance),
);
_load();
_timer = Timer.periodic(SchedulePage.refreshEvery, (_) {
if (mounted && !_refreshing && TickerMode.valuesOf(context).enabled) _load();
});
}
@override
void dispose() {
_timer?.cancel();
_scroll.dispose();
if (_ownsRepo) {
_repo.aniList.close();
_repo.animeFlow?.close();
}
super.dispose();
}
void _toast(String s) {
if (mounted) widget.onToast(s);
}
Future<void> _load({bool force = false}) async {
final gen = ++_gen;
if (_snap != null) setState(() => _refreshing = true);
final snap = await _repo.load(force: force);
if (!mounted || gen != _gen) return;
final first = _snap == null;
setState(() {
_snap = snap;
_refreshing = false;
// Without AnimeFlow nothing is "on Jellyfin": start on everything.
if (first && !snap.hasAnimeFlow) _filter = ScheduleFilter.all;
});
if (!_jumped && snap.entries.isNotEmpty) {
_jumped = true;
WidgetsBinding.instance.addPostFrameCallback((_) => _jumpToday());
}
await _loadRequests();
}
Future<void> _loadRequests() async {
final c = _flow, me = _me;
if (c == null || me == null) {
if (_requests != null || _requestsError != null) {
setState(() {
_requests = null;
_requestsError = null;
});
}
return;
}
try {
final reqs = await c.requests(all: me.can(AnimeFlowScope.manage));
if (mounted) {
setState(() {
_requests = reqs;
_requestsError = null;
});
}
} catch (e) {
if (mounted) setState(() => _requestsError = friendlyError(e));
}
}
// ------------------------------------------------------------- layout
int _todayIndex(DateTime from) {
final now = DateTime.now();
final today = DateTime(now.year, now.month, now.day);
final f = DateTime(from.year, from.month, from.day);
return (today.difference(f).inHours / 24).round();
}
void _jumpToday({bool animate = false}) {
final snap = _snap;
if (!_scroll.hasClients || snap == null) return;
final i = _todayIndex(snap.from.toLocal());
final x = (i * (SchedulePage.columnWidth + SchedulePage.columnGap))
.clamp(0.0, _scroll.position.maxScrollExtent)
.toDouble();
animate ? _scroll.animateTo(x, duration: easeTime, curve: ease) : _scroll.jumpTo(x);
}
bool _followed(ScheduleEntry e) => (e.seriesId == null ? null : _followOverride[e.seriesId]) ?? e.followed;
bool _shows(ScheduleEntry e) => switch (_filter) {
ScheduleFilter.jellyfin => e.tracked || e.onJellyfin,
ScheduleFilter.following => _followed(e),
ScheduleFilter.all => _niche || e.tracked || e.airing.popularity >= SchedulePage.nicheBelow,
};
static String _key(ScheduleEntry e) =>
e.tracked ? '${e.airing.anilistId}#${e.airing.episode}' : 'series:${e.airing.anilistId}';
static const _active = {RequestStatus.pending, RequestStatus.approved, RequestStatus.searching};
bool _isRequested(ScheduleEntry e) {
if (_requested.contains(_key(e))) return true;
final a = e.airing;
for (final r in _requests ?? const <AnimeFlowRequest>[]) {
if (!_active.contains(r.status)) continue;
if (e.tracked) {
if (r.kind == RequestKind.episode && r.seriesId == e.seriesId && r.episode == a.episode) return true;
if (r.kind == RequestKind.season && r.seriesId == e.seriesId && r.season == e.season) return true;
} else if (r.kind == RequestKind.series && (r.title == a.title || r.title == a.titleRomaji)) {
return true;
}
}
return false;
}
(CardAction, String?) _actionFor(ScheduleEntry e) {
if (e.status == ScheduleStatus.available || e.status == ScheduleStatus.inLibrary) {
final put = widget.solo ? CardAction.play : CardAction.putUp;
if (e.jellyfinSeriesId == null) return (put, 'Not linked to a Jellyfin series yet');
return (put, null);
}
final me = _me;
final CardAction kind;
if (e.tracked) {
kind = e.status == ScheduleStatus.missing || e.status == ScheduleStatus.failed
? CardAction.request
: CardAction.none;
} else {
// On Jellyfin already (owner added it by hand): nothing to request.
kind = me != null && !e.onJellyfin ? CardAction.requestSeries : CardAction.none;
}
if (kind == CardAction.none) return (kind, null);
if (_isRequested(e)) return (CardAction.requested, null);
if (me == null || !me.can(AnimeFlowScope.request)) {
return (kind, 'Requests need the owner to allow them for you on AnimeFlow');
}
return (kind, null);
}
// ------------------------------------------------------------- actions
Future<void> _onAction(ScheduleEntry e, CardAction action) async {
switch (action) {
case CardAction.putUp:
case CardAction.play:
return _putUp(e);
case CardAction.request:
case CardAction.requestSeries:
return _request(e);
default:
return;
}
}
Future<void> _putUp(ScheduleEntry e) async {
final key = 'play:${_key(e)}';
if (_busy.contains(key)) return;
setState(() => _busy.add(key));
try {
final pick = await resolveJellyfinEpisode(_accounts, _flow, e);
final err = await widget.onPutUp(pick);
if (err != null) _toast(err);
} catch (err) {
_toast(friendlyError(err));
} finally {
if (mounted) setState(() => _busy.remove(key));
}
}
Future<void> _request(ScheduleEntry e) async {
final c = _flow;
if (c == null) return;
final key = _key(e);
if (_busy.contains(key)) return;
setState(() => _busy.add(key));
final a = e.airing;
try {
final seriesId = e.seriesId;
if (e.tracked && seriesId != null) {
final n = await flowNumbering(c, e);
await c.requestEpisode(seriesId, n.season, n.episode);
_requested.add(key);
_toast('Requested ${a.title}, episode ${a.episode}');
} else {
final found = await findOnAniDb(c, a);
final hit = found.match ?? (mounted ? await _chooseHit(a, found.hits) : null);
if (hit == null) return;
await c.requestSeries(hit.anidbId);
_requested.add(key);
_toast('Requested ${hit.title}');
}
unawaited(_loadRequests());
} on AnimeFlowException catch (err) {
if (err.code == AnimeFlowErrorCode.alreadyRequested) _requested.add(key);
_toast(friendlyError(err));
} catch (err) {
_toast(friendlyError(err));
} finally {
if (mounted) setState(() => _busy.remove(key));
}
}
/// No search hit has [a]'s AniList id: let the user pick one (or learn
/// that AniDB does not know it).
Future<AnimeFlowSearchHit?> _chooseHit(Airing a, List<AnimeFlowSearchHit> hits) => showDialog<AnimeFlowSearchHit>(
context: context,
builder: (context) {
final scheme = Theme.of(context).colorScheme;
return AlertDialog(
key: const ValueKey('anidb-choose'),
title: Text(hits.isEmpty ? 'Not found on AniDB' : 'Which one is "${a.title}"?'),
content: SizedBox(
width: 420,
child: hits.isEmpty
? Text(
'AnimeFlow can only add series AniDB knows, and its search found nothing for this title.',
style: TextStyle(color: scheme.onSurfaceVariant),
)
: ListView(
shrinkWrap: true,
children: [
for (final h in hits)
ListTile(
key: ValueKey('anidb-${h.anidbId}'),
title: Text(h.title),
subtitle: Text('AniDB ${h.anidbId}${h.seriesId != null ? ' · already on AnimeFlow' : ''}'),
onTap: () => Navigator.of(context).pop(h),
),
],
),
),
actions: [
TextButton(onPressed: () => Navigator.of(context).pop(), child: Text(hits.isEmpty ? 'OK' : 'Cancel')),
],
);
},
);
Future<void> _toggleFollow(ScheduleEntry e) async {
final c = _flow, id = e.seriesId;
if (c == null || id == null) return;
final next = !_followed(e);
setState(() => _followOverride[id] = next);
try {
await c.setFollowed(id, next);
await _load();
} catch (err) {
_toast(friendlyError(err));
} finally {
if (mounted) setState(() => _followOverride.remove(id));
}
}
Future<void> _decide(AnimeFlowRequest r, {required bool approve}) async {
final c = _flow;
if (c == null || _reqBusy.contains(r.id)) return;
setState(() => _reqBusy.add(r.id));
try {
approve ? await c.approve(r.id) : await c.reject(r.id);
_toast(approve ? 'Retrying ${r.title}' : 'Declined ${r.title}');
await _loadRequests();
} catch (err) {
_toast(friendlyError(err));
} finally {
if (mounted) setState(() => _reqBusy.remove(r.id));
}
}
// ------------------------------------------------------------- build
Widget _header(BuildContext context) {
final snap = _snap;
final me = _me;
final stale = snap?.aniListStaleSince;
return Padding(
padding: const EdgeInsets.fromLTRB(20, 16, 20, 8),
child: Wrap(
spacing: 12,
runSpacing: 8,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
SegmentedButton<ScheduleFilter>(
key: const ValueKey('schedule-filter'),
showSelectedIcon: false,
segments: const [
ButtonSegment(
value: ScheduleFilter.jellyfin,
icon: Icon(Icons.video_library_outlined),
label: Text('On Jellyfin'),
),
ButtonSegment(
value: ScheduleFilter.following,
icon: Icon(Icons.bookmark_outline),
label: Text('Following'),
),
ButtonSegment(value: ScheduleFilter.all, icon: Icon(Icons.public), label: Text('All airing')),
],
selected: {_filter},
onSelectionChanged: (s) => setState(() => _filter = s.first),
),
FilterChip(
key: const ValueKey('schedule-compact'),
label: const Text('Compact'),
selected: _compact,
onSelected: (v) => setState(() => _compact = v),
),
if (_filter == ScheduleFilter.all)
FilterChip(
key: const ValueKey('schedule-niche'),
label: const Text('Niche too'),
selected: _niche,
onSelected: (v) => setState(() => _niche = v),
),
TextButton.icon(
key: const ValueKey('schedule-today'),
onPressed: () => _jumpToday(animate: true),
icon: const Icon(Icons.today, size: 18),
label: const Text('Today'),
),
IconButton(
key: const ValueKey('schedule-refresh'),
tooltip: 'Refresh',
onPressed: _refreshing || snap == null ? null : () => _load(force: true),
icon: _refreshing
? const SizedBox(width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2))
: const Icon(Icons.refresh, size: 20),
),
Tooltip(
message: me == null ? (snap?.animeFlowError ?? 'AnimeFlow is not available') : '',
child: OutlinedButton.icon(
key: const ValueKey('schedule-requests'),
onPressed: me == null
? null
: () => setState(() {
_sheet = !_sheet;
if (_sheet) _sheetBuilt = true;
}),
icon: const Icon(Icons.inbox_outlined, size: 18),
label: Text(_owner ? 'Requests' : 'My requests'),
),
),
if (stale != null)
Pill(
key: const ValueKey('schedule-stale'),
'AniList busy · showing data from ${clockText(stale)}',
Colors.amber,
),
if (me != null)
Pill(
key: const ValueKey('schedule-animeflow'),
'AnimeFlow · ${me.name} · ${[for (final s in AnimeFlowScope.values)
if (me.can(s)) s.name].join(', ')}',
Colors.greenAccent.shade400,
)
else if (snap?.animeFlowError != null)
Pill(key: const ValueKey('schedule-animeflow'), snap!.animeFlowError!, Colors.orangeAccent),
],
),
);
}
Widget _board(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final snap = _snap;
if (snap == null) return const Center(child: CircularProgressIndicator());
if (snap.aniListError != null && snap.entries.isEmpty) {
return Center(
key: const ValueKey('schedule-error'),
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.cloud_off, color: scheme.error),
const SizedBox(height: 8),
Text(
'Could not load the schedule: ${snap.aniListError}',
textAlign: TextAlign.center,
style: TextStyle(color: scheme.error),
),
const SizedBox(height: 12),
FilledButton.tonal(
onPressed: _refreshing ? null : () => _load(force: true),
child: const Text('Try again'),
),
],
),
),
);
}
final now = DateTime.now();
final today = DateTime(now.year, now.month, now.day);
final from = snap.from.toLocal();
final start = DateTime(from.year, from.month, from.day);
final to = snap.to.toLocal();
final count = (DateTime(to.year, to.month, to.day).difference(start).inHours / 24).round().clamp(1, 60);
final days = [for (var d = 0; d < count; d++) DateTime(start.year, start.month, start.day + d)];
final byDay = <DateTime, List<ScheduleEntry>>{};
for (final e in snap.entries) {
if (!_shows(e)) continue;
final l = e.airing.airingAt.toLocal();
(byDay[DateTime(l.year, l.month, l.day)] ??= []).add(e);
}
return HScroll(
controller: _scroll,
builder: (c) => ListView.separated(
key: const ValueKey('schedule-board'),
controller: c,
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.fromLTRB(20, 4, 20, 16),
itemCount: days.length,
separatorBuilder: (_, _) => const SizedBox(width: SchedulePage.columnGap),
itemBuilder: (context, i) {
final d = days[i];
final items = byDay[d] ?? const <ScheduleEntry>[];
return SizedBox(
key: ValueKey('day-${d.year}-${d.month}-${d.day}'),
width: SchedulePage.columnWidth,
child: DayColumn(
day: d,
today: d == today,
past: d.isBefore(today),
compact: _compact,
cards: [for (final e in items) _card(e, now)],
),
);
},
),
);
}
Widget _card(ScheduleEntry e, DateTime now) {
final (action, hint) = _actionFor(e);
final busy = _busy.contains(action == CardAction.putUp || action == CardAction.play ? 'play:${_key(e)}' : _key(e));
return AiringCard(
entry: e,
now: now,
compact: _compact,
followed: _followed(e),
action: action,
disabledHint: hint,
busy: busy,
onAction: () => _onAction(e, action),
onFollow: e.tracked && e.seriesId != null && _flow != null ? () => _toggleFollow(e) : null,
);
}
@override
Widget build(BuildContext context) => Row(
children: [
Expanded(
child: Column(
children: [
_header(context),
Expanded(child: _board(context)),
],
),
),
ClipRect(
child: AnimatedContainer(
duration: easeTime,
curve: ease,
width: _sheet ? SchedulePage.sheetWidth : 0,
child: OverflowBox(
alignment: Alignment.centerLeft,
minWidth: SchedulePage.sheetWidth,
maxWidth: SchedulePage.sheetWidth,
child: _sheetBuilt && _me != null
? RequestsSheet(
key: const ValueKey('requests-sheet'),
owner: _owner,
requests: _requests,
error: _requestsError,
selfName: _me?.name ?? '',
busy: _reqBusy,
onRetry: (r) => _decide(r, approve: true),
onDecline: (r) => _decide(r, approve: false),
onClose: () => setState(() => _sheet = false),
)
: const SizedBox.shrink(),
),
),
),
],
);
}
@@ -1,359 +0,0 @@
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
import 'package:window_manager/window_manager.dart';
import '../session/session_coordinator.dart';
import '../state/session_state.dart';
import '../widgets/avatar_pipeline.dart';
import '../widgets/media_input.dart';
import '../widgets/user_section.dart';
import '../widgets/window_buttons.dart';
class ConnectScreen extends StatefulWidget {
const ConnectScreen({super.key});
@override
State<ConnectScreen> createState() => _ConnectScreenState();
}
class _ConnectScreenState extends State<ConnectScreen> {
late final TextEditingController _host;
late final TextEditingController _port;
late final TextEditingController _secret;
late final TextEditingController _name;
late final TextEditingController _media;
late final void Function() _stopKickWatch;
String? _error;
String? _notice;
bool _busy = false;
bool _dialogOpen = false;
int _attempt = 0;
String? _lastKickShown;
@override
void initState() {
super.initState();
final f = SessionState.instance.lastForm;
_host = TextEditingController(text: f.host);
_port = TextEditingController(text: f.port);
_secret = TextEditingController(text: f.secret);
_name = TextEditingController(text: f.name);
_media = TextEditingController(text: f.linkUrl.isNotEmpty ? f.linkUrl : f.mediaPath);
_stopKickWatch = effect(() {
final kick = SessionState.instance.coordinator.kickReason.value;
if (kick == null || kick.isEmpty) {
_lastKickShown = null;
return;
}
if (kick == _lastKickShown) return;
_lastKickShown = kick;
final reason = kick;
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) _showKickedDialog(reason);
});
});
}
@override
void dispose() {
_stopKickWatch();
_host.dispose();
_port.dispose();
_secret.dispose();
_name.dispose();
_media.dispose();
super.dispose();
}
String _actionable(Object e) {
if (e is FormatException) return e.message;
return 'check the form and try again';
}
Future<ConnectParams> _buildParams(ConnectMode mode) async {
final port = int.tryParse(_port.text.trim());
if (port == null || port <= 0 || port > 65535) {
throw const FormatException('port must be 1–65535');
}
if (_secret.text.isEmpty) {
throw const FormatException('room password required');
}
if (_name.text.trim().isEmpty) {
throw const FormatException('display name required');
}
String avatar = '';
try {
avatar = await encodeAvatar(SessionState.instance.lastForm.avatarPath);
} on FormatException catch (e) {
throw FormatException(e.message);
} catch (_) {
throw const FormatException('could not process that avatar — pick a PNG, JPG or WebP photo');
}
final parsed = parseMediaInput(_media.text);
if (mode == ConnectMode.host && parsed.mediaPath.isNotEmpty && !await File(parsed.mediaPath).exists()) {
throw const FormatException('media file not found — check the path or pick again with Browse');
}
return ConnectParams(
host: _host.text.trim().isEmpty ? '127.0.0.1' : _host.text.trim(),
port: port,
secret: _secret.text,
name: _name.text.trim(),
avatar: avatar,
mediaPath: mode == ConnectMode.host ? parsed.mediaPath : '',
linkUrl: mode == ConnectMode.host ? parsed.linkUrl : '',
);
}
Future<void> _submit(ConnectMode mode) async {
final myAttempt = ++_attempt;
final session = SessionState.instance;
setState(() {
_error = null;
_notice = null;
_busy = true;
});
late final ConnectParams params;
try {
params = await _buildParams(mode);
} catch (e) {
if (!mounted || myAttempt != _attempt) return;
setState(() {
_error = _actionable(e);
_busy = false;
});
return;
}
final parsed = parseMediaInput(_media.text);
final form = SessionState.instance.lastForm
..host = _host.text.trim()
..port = _port.text.trim()
..secret = _secret.text
..name = _name.text.trim()
..mediaPath = parsed.mediaPath
..linkUrl = parsed.linkUrl;
try {
if (mode == ConnectMode.host) {
await session.host(params, form);
} else {
await session.join(params, form);
}
} catch (e) {
if (!mounted || myAttempt != _attempt) return;
final kick = session.coordinator.kickReason.peek();
setState(() => _busy = false);
if (kick != null && kick.isNotEmpty) {
await _showKickedDialog(kick);
} else {
await _showConnectionDialog('$e');
}
return;
}
if (myAttempt != _attempt) {
try {
await session.leave();
await session.stopServer();
} catch (_) {}
return;
}
if (mounted) setState(() => _busy = false);
}
Future<void> _cancel() async {
_attempt++;
try {
await SessionState.instance.coordinator.cancelConnect();
await SessionState.instance.stopServer();
} catch (_) {}
if (!mounted) return;
setState(() {
_busy = false;
_notice = 'connection cancelled';
});
}
Future<void> _showKickedDialog(String reason) async {
if (!mounted || _dialogOpen) return;
_dialogOpen = true;
try {
await showDialog<void>(
context: context,
builder: (context) => AlertDialog(
title: const Text('You were removed'),
content: Text(reason.isEmpty ? 'The host removed you from the room.' : reason),
actions: [TextButton(onPressed: () => Navigator.pop(context), child: const Text('OK'))],
),
);
} finally {
_dialogOpen = false;
}
}
Future<void> _showConnectionDialog(String message) async {
if (!mounted || _dialogOpen) return;
_dialogOpen = true;
try {
await showDialog<void>(
context: context,
builder: (context) => AlertDialog(
title: const Text('Could not connect'),
content: Text(message),
actions: [TextButton(onPressed: () => Navigator.pop(context), child: const Text('OK'))],
),
);
} finally {
_dialogOpen = false;
}
}
Future<void> _browse(TextEditingController target, {required String dialogTitle, List<String>? allowedExtensions}) async {
try {
final files = await FilePicker.pickFiles(
dialogTitle: dialogTitle,
type: allowedExtensions == null ? FileType.any : FileType.custom,
allowedExtensions: allowedExtensions,
);
if (files.isEmpty) return;
final path = files.first.path;
if (path != null && mounted) setState(() => target.text = path);
} catch (e) {
if (!mounted) return;
setState(() => _notice = 'file dialog unavailable (${e.runtimeType}) — paste the path manually');
}
}
@override
Widget build(BuildContext context) {
final session = SessionState.instance;
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(kToolbarHeight),
child: DragToMoveArea(
child: AppBar(
leading: IconButton(icon: const Icon(Icons.arrow_back), onPressed: () => session.route.value = AppRoute.welcome),
title: SignalBuilder(builder: (context) => Text(session.connectMode.value == ConnectMode.host ? 'Host room' : 'Join room')),
actions: const [WindowButtons()],
),
),
),
body: Stack(
alignment: AlignmentGeometry.center,
children: [
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.all(20),
child: Card(
child: Padding(
padding: const EdgeInsets.all(12),
child: ConnectProfileCard(name: _name),
),
),
),
),
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 480),
child: ListView(
shrinkWrap: true,
padding: const EdgeInsets.all(24),
children: [
if (_error != null)
Card(
child: Padding(padding: const EdgeInsets.all(12), child: Text(_error!)),
),
if (_notice != null) Text(_notice!),
SignalBuilder(
builder: (context) {
final mode = session.connectMode.value;
return SegmentedButton<ConnectMode>(
segments: const [
ButtonSegment(value: ConnectMode.host, label: Text('Host')),
ButtonSegment(value: ConnectMode.join, label: Text('Join')),
],
selected: {mode},
onSelectionChanged: (s) => session.connectMode.value = s.first,
);
},
),
const SizedBox(height: 16),
SignalBuilder(
builder: (context) {
if (session.connectMode.value == ConnectMode.host) {
return const SizedBox.shrink();
}
return TextField(
controller: _host,
decoration: const InputDecoration(labelText: 'Host address'),
);
},
),
TextField(
controller: _port,
decoration: const InputDecoration(labelText: 'Port'),
keyboardType: TextInputType.number,
),
TextField(
controller: _secret,
decoration: const InputDecoration(labelText: 'Room password'),
obscureText: true,
),
const SizedBox(height: 8),
SignalBuilder(
builder: (context) {
if (session.connectMode.value == ConnectMode.host) {
return Row(
children: [
Expanded(
child: TextField(
controller: _media,
decoration: const InputDecoration(labelText: 'Media (file path or http(s) URL)'),
),
),
TextButton(
onPressed: () => _browse(
_media,
dialogTitle: 'Pick media file',
allowedExtensions: const ['mp4', 'mkv', 'webm', 'avi', 'mov', 'm4v', 'ts'],
),
child: const Text('Browse'),
),
],
);
}
return const SizedBox.shrink();
},
),
const SizedBox(height: 16),
SignalBuilder(
builder: (context) {
final mode = session.connectMode.value;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ElevatedButton(
onPressed: _busy ? null : () => _submit(mode),
child: Text(
_busy
? 'Connecting…'
: mode == ConnectMode.host
? 'Start hosting'
: 'Join',
),
),
if (_busy) TextButton(onPressed: _cancel, child: const Text('Cancel')),
],
);
},
),
const SizedBox(height: 12),
const Text('Host opens ports and serves. Clients open nothing.', textAlign: TextAlign.center),
],
),
),
],
),
);
}
}
@@ -0,0 +1,77 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:sync_relay_core/core.dart';
import '../../update/update_controller.dart';
import 'settings_page.dart';
import 'ui.dart';
import 'update_indicator.dart';
/// Opens [url] in the default browser (no url_launcher dependency).
Future<bool> openInBrowser(String url) async {
try {
final ProcessResult r;
if (Platform.isWindows) {
r = await Process.run('rundll32', ['url.dll,FileProtocolHandler', url]);
} else if (Platform.isMacOS) {
r = await Process.run('open', [url]);
} else {
r = await Process.run('xdg-open', [url]);
}
return r.exitCode == 0;
} catch (_) {
return false;
}
}
/// Name, version, update state and the source link (Settings → About).
class AboutPanel extends StatelessWidget {
final UpdateController? updates;
const AboutPanel({super.key, this.updates});
Future<void> _open(BuildContext context) async {
final messenger = ScaffoldMessenger.maybeOf(context);
if (await openInBrowser(repoUrl)) return;
await Clipboard.setData(const ClipboardData(text: repoUrl));
messenger?.showSnackBar(
const SnackBar(content: Text('Could not open a browser — link copied')));
}
@override
Widget build(BuildContext context) {
final t = Theme.of(context).textTheme;
return SettingsPanel(
title: 'About',
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(appName, style: t.titleLarge),
const SizedBox(height: 4),
SelectableText('Version $appVersion',
style: t.bodyMedium?.copyWith(color: Colors.grey)),
const SizedBox(height: 4),
UpdateStatusLine(controller: updates),
const SizedBox(height: kGap),
Row(
children: [
Expanded(child: SelectableText(repoUrl, style: t.bodySmall)),
IconButton(
tooltip: 'Copy link',
icon: const Icon(Icons.copy, size: 18),
onPressed: () =>
Clipboard.setData(const ClipboardData(text: repoUrl)),
),
FilledButton.tonalIcon(
onPressed: () => _open(context),
icon: const Icon(Icons.open_in_new, size: 18),
label: const Text('Source'),
),
],
),
],
),
);
}
}
@@ -0,0 +1,146 @@
import 'package:flutter/foundation.dart';
import '../../session/session_coordinator.dart';
import '../../state/session_state.dart';
import 'profile.dart';
/// What a card shows under its fields after a failed attempt.
class CardError {
final String title;
final String message;
const CardError(this.title, this.message);
}
/// Host/Join submit state for the strip's two connect cards. One attempt at a time;
/// [cancel] abandons it via [SessionState.cancelPending].
class ConnectFlow extends ChangeNotifier {
ConnectFlow({SessionState? session}) : _session = session ?? SessionState.instance;
final SessionState _session;
ConnectMode? busy;
final errors = <ConnectMode, CardError?>{};
final notices = <ConnectMode, String?>{};
int _attempt = 0;
bool _disposed = false;
@override
void dispose() {
_disposed = true;
super.dispose();
}
void _notify() {
if (!_disposed) notifyListeners();
}
void showError(ConnectMode mode, CardError error) {
errors[mode] = error;
notices[mode] = null;
_notify();
}
void clearError(ConnectMode mode) {
if (errors[mode] == null && notices[mode] == null) return;
errors[mode] = null;
notices[mode] = null;
_notify();
}
/// Validates and builds the params; throws [FormatException] with a
/// user-facing message. [avatar] is the base64 picture (the profile's).
/// Hosting puts nothing up: what to watch is picked in the library.
static ConnectParams buildParams(ConnectMode mode, ConnectForm f, {String avatar = ''}) {
final rawPort = mode == ConnectMode.host ? f.hostPort : f.port;
final port = int.tryParse(rawPort.trim());
if (port == null || port <= 0 || port > 65535) {
throw const FormatException('port must be 1–65535');
}
if (f.name.trim().isEmpty) {
throw const FormatException('set your display name first (click your avatar at the top left)');
}
if (mode == ConnectMode.join && f.host.trim().isEmpty) {
throw const FormatException('enter the server address');
}
return ConnectParams(
host: f.host.trim(),
port: port,
secret: mode == ConnectMode.host ? f.hostSecret : f.secret,
name: f.name.trim(),
avatar: avatar,
);
}
/// [edit] fills the card's fields into a copy of the last form, so the
/// other card's saved values survive.
Future<void> submit(ConnectMode mode, void Function(ConnectForm f) edit) async {
if (busy != null) return;
final myAttempt = ++_attempt;
final form = _session.lastForm.copy();
edit(form);
form.name = form.name.trim();
form.host = form.host.trim();
busy = mode;
errors[mode] = null;
notices[mode] = null;
_notify();
ConnectParams params;
try {
final profile = Profile.instance;
await profile.ensureLoaded();
params = buildParams(mode, form, avatar: profile.avatarBase64());
} catch (e) {
if (myAttempt != _attempt) return;
busy = null;
errors[mode] = CardError('Check the form',
e is FormatException ? e.message : 'check the form and try again');
_notify();
return;
}
final fromSolo = mode == ConnectMode.host && _session.solo.value;
try {
if (mode == ConnectMode.host) {
await _session.host(params, form);
} else {
await _session.join(params, form);
}
} catch (e) {
if (myAttempt != _attempt) return;
busy = null;
errors[mode] = CardError('Could not connect', '$e');
_notify();
return;
}
if (myAttempt != _attempt) {
// Cancelled after it got through: back out (an Invite: back to solo).
try {
if (fromSolo) {
await _session.cancelInvite();
} else {
await _session.leave();
await _session.stopServer();
}
} catch (_) {}
return;
}
busy = null;
_notify();
await _session.rememberServer(RecentServer(
hosted: mode == ConnectMode.host,
host: mode == ConnectMode.host ? '' : form.host,
port: mode == ConnectMode.host ? form.hostPort.trim() : form.port.trim(),
secret: mode == ConnectMode.host ? form.hostSecret : form.secret,
));
}
Future<void> cancel() async {
final mode = busy;
_attempt++;
try {
await _session.cancelPending();
} catch (_) {}
busy = null;
if (mode != null) notices[mode] = 'Cancelled';
_notify();
}
}
@@ -0,0 +1,82 @@
import 'package:flutter/material.dart';
import '../../state/session_state.dart';
import 'connect_flow.dart';
import 'ui.dart';
/// The submit button for one [ConnectMode], swapped for a progress bar and
/// Cancel while that attempt runs. Disabled while the other mode is busy.
class ConnectButton extends StatelessWidget {
final ConnectFlow flow;
final ConnectMode mode;
final String label;
final VoidCallback onSubmit;
final bool expand;
const ConnectButton({
super.key,
required this.flow,
required this.mode,
required this.label,
required this.onSubmit,
this.expand = false,
});
@override
Widget build(BuildContext context) {
final busyHere = flow.busy == mode;
if (busyHere) {
return SizedBox(
width: expand ? null : 220,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const LinearProgressIndicator(),
const SizedBox(height: 4),
Row(
children: [
Expanded(
child: Text(mode == ConnectMode.host ? 'Starting server…' : 'Connecting…',
style: Theme.of(context).textTheme.bodySmall),
),
TextButton(onPressed: flow.cancel, child: const Text('Cancel')),
],
),
],
),
);
}
final button = FilledButton(
onPressed: flow.busy != null ? null : onSubmit,
child: Text(label),
);
return expand ? SizedBox(width: double.infinity, child: button) : button;
}
}
/// Last error or notice for [mode], or nothing.
class ConnectFeedback extends StatelessWidget {
final ConnectFlow flow;
final ConnectMode mode;
const ConnectFeedback({super.key, required this.flow, required this.mode});
@override
Widget build(BuildContext context) {
final error = flow.errors[mode];
final notice = flow.notices[mode];
if (error != null) {
return Padding(
padding: const EdgeInsets.only(top: 12),
child: ErrorBox(title: error.title, message: error.message),
);
}
if (notice != null) {
return Padding(
padding: const EdgeInsets.only(top: 12),
child: Text(notice,
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: Colors.grey)),
);
}
return const SizedBox.shrink();
}
}
@@ -0,0 +1,77 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:sync_relay_core/core.dart';
/// Fetches a URL body; injectable so tests fake the server's `/health`.
typedef HealthGetter = Future<String> Function(Uri uri);
const healthPollEvery = Duration(seconds: 10);
const _timeout = Duration(seconds: 4);
/// What a recent-server tile shows: unknown until the first probe answers.
class ServerHealth {
final bool online;
final String name;
final int? proto;
final int viewers;
const ServerHealth.offline()
: online = false,
name = '',
proto = null,
viewers = 0;
const ServerHealth.online({required this.name, this.proto, this.viewers = 0})
: online = true;
/// True when the server speaks another protocol than this build.
bool get needsUpdate =>
online && proto != null && proto != RelayConstants.protoVersion;
/// Lenient parse of `GET /health`; anything unexpected reads as offline.
static ServerHealth parse(String body) {
try {
final j = jsonDecode(body);
if (j is! Map || j['ok'] != true) return const ServerHealth.offline();
final name = j['name'], proto = j['proto'], viewers = j['viewers'];
return ServerHealth.online(
name: name is String ? name.trim() : '',
proto: proto is num ? proto.toInt() : null,
viewers: viewers is num ? viewers.toInt() : 0,
);
} catch (_) {
return const ServerHealth.offline();
}
}
}
Uri healthUri(String host, String port) {
final h = host.trim();
// Bare IPv6 literals need brackets in a URL.
final authority = h.contains(':') && !h.startsWith('[') ? '[$h]' : h;
return Uri.parse('http://$authority:${port.trim()}/health');
}
Future<ServerHealth> probeHealth(String host, String port, HealthGetter get) async {
try {
return ServerHealth.parse(await get(healthUri(host, port)).timeout(_timeout));
} catch (_) {
return const ServerHealth.offline();
}
}
/// Plain dart:io GET; non-200 throws.
Future<String> defaultHealthGet(Uri uri) async {
final client = HttpClient()..connectionTimeout = _timeout;
try {
final req = await client.getUrl(uri);
final res = await req.close().timeout(_timeout);
if (res.statusCode != 200) {
await res.drain<void>();
throw HttpException('status ${res.statusCode}', uri: uri);
}
return await res.transform(utf8.decoder).join().timeout(_timeout);
} finally {
client.close(force: true);
}
}
@@ -0,0 +1,99 @@
import 'package:flutter/material.dart';
import '../../jellyfin/browse_scope.dart' show JellyfinSelection;
import '../../widgets/media_input.dart';
import 'picked_media.dart';
export 'picked_media.dart';
/// The Library's sources.
enum MediaKind { jellyfin, link, file }
/// State of the Library tab, owned by the shell so it survives rebuilds
/// and tab switches: the source shown, the link field and its relay mode,
/// the selected Jellyfin card (details panel) and the last Jellyfin pick.
class MediaPickerController extends ChangeNotifier {
MediaPickerController(
{String mediaPath = '', String linkUrl = '', JellyfinMedia? jellyfin, MediaKind? source})
: link = TextEditingController(text: linkUrl),
file = TextEditingController(text: mediaPath),
_jellyfin = jellyfin,
_selected = jellyfin == null ? null : JellyfinSelection(jellyfin.serverId, jellyfin.itemId),
_source = source ?? MediaKind.jellyfin {
link.addListener(notifyListeners);
file.addListener(notifyListeners);
}
final TextEditingController link;
final TextEditingController file;
MediaKind _source;
LinkRelay _linkRelay = LinkRelay.direct;
JellyfinMedia? _jellyfin;
JellyfinSelection? _selected;
LinkRelay get linkRelay => _linkRelay;
set linkRelay(LinkRelay v) {
if (v == _linkRelay) return;
_linkRelay = v;
notifyListeners();
}
/// The last Jellyfin item picked (restored from the last put-up).
JellyfinMedia? get jellyfin => _jellyfin;
/// Picks a Jellyfin item: selects its card and switches to Jellyfin.
void pickJellyfin(JellyfinMedia? media) {
_jellyfin = media;
_selected = media == null ? null : JellyfinSelection(media.serverId, media.itemId);
_source = MediaKind.jellyfin;
notifyListeners();
}
/// The selected Jellyfin card (details panel open), or null.
JellyfinSelection? get selected => _selected;
void select(JellyfinSelection? s) {
if (s == _selected && s?.item == _selected?.item) return;
_selected = s;
notifyListeners();
}
MediaKind get source => _source;
set source(MediaKind v) {
if (v == _source) return;
_source = v;
notifyListeners();
}
/// What the Link or File source holds; for Jellyfin the last pick.
PickedMedia? get selection {
switch (_source) {
case MediaKind.link:
final url = link.text.trim();
return url.isEmpty ? null : LinkMedia(url, relay: _linkRelay);
case MediaKind.file:
final path = file.text.trim();
return path.isEmpty ? null : FileMedia(path);
case MediaKind.jellyfin:
return _jellyfin;
}
}
/// Drops pasted text where it belongs: http(s) into Link, else File.
void pasteText(String text) {
final parsed = parseMediaInput(text);
if (parsed.linkUrl.isNotEmpty) {
link.text = parsed.linkUrl;
source = MediaKind.link;
} else if (parsed.mediaPath.isNotEmpty) {
file.text = parsed.mediaPath;
source = MediaKind.file;
}
}
@override
void dispose() {
link.dispose();
file.dispose();
super.dispose();
}
}
@@ -0,0 +1,121 @@
/// What the media picker selected. Null selection = nothing (hosting
/// without media is allowed).
sealed class PickedMedia {
const PickedMedia();
/// One line for summaries ("Playing: …").
String get label;
}
/// How the others get a link: each fetches it ([direct]), the server
/// fetches it once for everyone ([server]), or this computer does and
/// passes it on through the server ([me]; for links only you can reach).
enum LinkRelay { direct, server, me }
/// An http(s) link.
final class LinkMedia extends PickedMedia {
final String url;
final LinkRelay relay;
const LinkMedia(this.url, {this.relay = LinkRelay.direct});
@override
String get label {
final uri = Uri.tryParse(url);
final segs = uri?.pathSegments.where((s) => s.isNotEmpty).toList() ?? const [];
if (segs.isEmpty) return uri?.host.isNotEmpty == true ? uri!.host : url;
try {
return Uri.decodeComponent(segs.last);
} catch (_) {
return segs.last;
}
}
@override
bool operator ==(Object other) => other is LinkMedia && other.url == url && other.relay == relay;
@override
int get hashCode => Object.hash(url, relay);
@override
String toString() => 'LinkMedia($url)';
}
/// A file on this computer; the others stream it from here (the host's
/// app serves it itself, anyone else's goes through the server's relay).
final class FileMedia extends PickedMedia {
final String path;
const FileMedia(this.path);
@override
String get label =>
path.split(RegExp(r'[/\\]')).lastWhere((p) => p.isNotEmpty, orElse: () => path);
@override
bool operator ==(Object other) => other is FileMedia && other.path == path;
@override
int get hashCode => path.hashCode;
@override
String toString() => 'FileMedia($path)';
}
/// A movie or episode on a linked Jellyfin server. [serverId] keys the
/// account (`JellyfinAccounts.clientFor`); [baseUrl] is the address it was
/// picked through. No credentials in here: each side authorises its own.
final class JellyfinMedia extends PickedMedia {
final String serverId;
final String baseUrl;
final String itemId;
/// Null = the item's default source.
final String? mediaSourceId;
final String title;
/// "Series · S1E6" for episodes, the year for movies; may be empty.
final String subtitle;
final Duration? runtime;
const JellyfinMedia({
required this.serverId,
required this.baseUrl,
required this.itemId,
this.mediaSourceId,
required this.title,
this.subtitle = '',
this.runtime,
});
@override
String get label => subtitle.isEmpty ? title : '$title — $subtitle';
Map<String, Object?> toJson() => {
'serverId': serverId,
'baseUrl': baseUrl,
'itemId': itemId,
'mediaSourceId': ?mediaSourceId,
'title': title,
'subtitle': subtitle,
if (runtime != null) 'runtimeMs': runtime!.inMilliseconds,
};
static JellyfinMedia? fromJson(Object? j) {
if (j is! Map) return null;
final serverId = j['serverId'], baseUrl = j['baseUrl'], itemId = j['itemId'];
if (serverId is! String || baseUrl is! String || itemId is! String) return null;
final ms = j['runtimeMs'];
return JellyfinMedia(
serverId: serverId,
baseUrl: baseUrl,
itemId: itemId,
mediaSourceId: j['mediaSourceId'] is String ? j['mediaSourceId'] as String : null,
title: j['title'] is String ? j['title'] as String : '',
subtitle: j['subtitle'] is String ? j['subtitle'] as String : '',
runtime: ms is int ? Duration(milliseconds: ms) : null,
);
}
@override
bool operator ==(Object other) =>
other is JellyfinMedia &&
other.serverId == serverId &&
other.itemId == itemId &&
other.mediaSourceId == mediaSourceId;
@override
int get hashCode => Object.hash(serverId, itemId, mediaSourceId);
@override
String toString() => 'JellyfinMedia($serverId/$itemId "$title")';
}
@@ -0,0 +1,131 @@
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
import '../../state/connect_prefs.dart';
import '../../state/session_state.dart';
import '../../widgets/avatar_pipeline.dart';
/// Colours offered in the profile card; the first is the default.
const profileColors = <Color>[
Colors.deepPurple,
Colors.indigo,
Colors.teal,
Colors.pink,
Colors.orange,
Colors.blueGrey,
];
const _keyPicture = 'avatarPng';
const _keyColor = 'profileColor';
/// Sends a mid-session name/picture change (null = unchanged, empty
/// avatar = removed). Swappable so tests can watch it.
typedef ProfilePush = void Function({String? name, String? avatar});
/// The one place a profile change reaches the server.
void pushProfileToSession({String? name, String? avatar}) {
final session = SessionState.instance;
if (session.route.peek() != AppRoute.session) return;
session.coordinator.updateProfile(name: name, avatar: avatar);
}
/// Display name, picture and colour of this app's user. Backed by
/// [SessionState.lastForm] (name) and the connect-form box (the cropped
/// 256 px PNG and the colour); every change is persisted, and while in a
/// server it is pushed through [push] so everyone sees it at once.
class Profile {
static final Profile instance = Profile._();
Profile._();
final name = signal('');
/// The cropped PNG; null shows the initial on [color].
final picture = signal<Uint8List?>(null);
final color = signal<Color>(profileColors.first);
final error = signal<String?>(null);
ProfilePush push = pushProfileToSession;
bool _loaded = false;
Timer? _nameTimer;
/// Reads the stored profile once (the app shell starts it). Old installs
/// stored a picture path: it is shrunk through [encodeAvatar] once and
/// kept as bytes. A caller during the first read does not wait for it
/// (no shared Future: tests run each in its own zone).
Future<void> ensureLoaded() async {
if (_loaded) return;
_loaded = true;
await _load();
}
Future<void> _load() async {
final form = SessionState.instance.lastForm;
name.value = form.name;
try {
final box = connectFormBoxIfOpen() ?? await openConnectFormBox();
final png = box.get(_keyPicture);
final c = box.get(_keyColor);
if (c is int) color.value = Color(c);
if (png is Uint8List && png.isNotEmpty) {
picture.value = png;
} else if (form.avatarPath.isNotEmpty) {
final b64 = await encodeAvatar(form.avatarPath);
if (b64.isNotEmpty) picture.value = base64Decode(b64);
form.avatarPath = '';
await _save();
}
} catch (e) {
// No store (tests) or an unreadable old picture: start without.
debugPrint('profile load: $e');
}
}
/// The picture as sent in hello and profile updates; '' when none.
String avatarBase64() {
final png = picture.peek();
return png == null ? '' : avatarWireBase64(png);
}
void setName(String value) {
final v = value.trim();
if (v.isEmpty || v == name.peek()) return;
name.value = v;
SessionState.instance.lastForm.name = v;
unawaited(_save());
// Typing renames once it settles, not on every key.
_nameTimer?.cancel();
_nameTimer = Timer(const Duration(milliseconds: 600), () => push(name: name.peek()));
}
Future<void> setPicture(Uint8List? png) async {
picture.value = png;
error.value = null;
await _save();
try {
push(avatar: png == null ? '' : avatarWireBase64(png));
} on FormatException catch (e) {
error.value = e.message;
}
}
Future<void> setColor(Color c) async {
color.value = c;
await _save();
}
Future<void> _save() async {
try {
await SessionState.instance.saveForm();
final box = connectFormBoxIfOpen() ?? await openConnectFormBox();
final png = picture.peek();
await box.putAll({
_keyPicture: png ?? Uint8List(0),
_keyColor: color.peek().toARGB32(),
});
} catch (_) {
error.value = 'could not save profile — try again';
}
}
}
@@ -0,0 +1,289 @@
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
import '../../state/settings.dart';
import '../../update/update_controller.dart';
import 'ui.dart';
import 'update_indicator.dart';
/// Session keys: Space and Tab anywhere in a server (`AppKeys`), the
/// rest by the session player's key handler (while it fills the window).
const sessionShortcuts = [
('Space', 'Toggle ready'),
('← / →', 'Seek back / forward 10 s'),
('↑ / ↓', 'Volume up / down (up to 150 %)'),
('Shift+→', 'Skip 1:30 (for everyone)'),
('PgUp / PgDn', 'Previous / next chapter'),
('F', 'Toggle fullscreen'),
('Tab', 'Open / close chat & activity'),
('Ctrl+R', 'Reload the video'),
('> or Shift+N', 'Put up the next Jellyfin episode'),
('< or Shift+P', 'Put up the previous Jellyfin episode'),
('Ctrl+1…6', 'Shaders: Anime4K mode A, B, C, A+A, B+B, C+A'),
('Ctrl+0', 'Shaders off'),
('Esc', 'Close the panel or fullscreen, shrink the player, then ask to leave'),
];
/// One titled card of the Settings tab (rounded Material, so list tiles
/// paint their ripple on it).
class SettingsPanel extends StatelessWidget {
final String title;
final Widget child;
const SettingsPanel({super.key, required this.title, required this.child});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: kGap),
child: Material(
color: Theme.of(context).colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(16),
clipBehavior: Clip.antiAlias,
child: Padding(
padding: const EdgeInsets.fromLTRB(18, 14, 18, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(title, style: const TextStyle(fontWeight: FontWeight.w700)),
const SizedBox(height: 6),
child,
],
),
),
),
);
}
}
/// A tinted hint card at the top of a Settings page.
class SettingsHintCard extends StatelessWidget {
final IconData icon;
final String text;
const SettingsHintCard({super.key, required this.icon, required this.text});
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return Container(
margin: const EdgeInsets.only(bottom: kGap),
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: scheme.primary.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(16),
),
child: Row(
children: [
Icon(icon, color: scheme.primary),
const SizedBox(width: 12),
Expanded(child: Text(text)),
],
),
);
}
}
/// The .glsl files of the "Custom shaders" preset, in mpv order.
class CustomShaderFiles extends StatelessWidget {
const CustomShaderFiles({super.key});
Future<void> _addFiles() async {
final List<PlatformFile> files;
try {
files = await FilePicker.pickFiles(
dialogTitle: 'Pick mpv GLSL shaders',
type: FileType.custom,
allowedExtensions: const ['glsl', 'hook'],
);
} catch (_) {
return;
}
final s = AppSettings.instance;
final current = s.customShaders.peek();
final added = [
for (final f in files)
if (f.path case final p? when !current.contains(p)) p,
];
if (added.isNotEmpty) s.setCustomShaders([...current, ...added]);
}
@override
Widget build(BuildContext context) {
final s = AppSettings.instance;
final muted = TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant);
return SignalBuilder(builder: (context) {
final custom = s.customShaders.value;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text('Your own mpv shaders for the "Custom shaders" choice; they run in this order.',
style: muted),
),
TextButton.icon(
onPressed: _addFiles,
icon: const Icon(Icons.add, size: 18),
label: const Text('Add .glsl files'),
),
],
),
if (custom.isEmpty)
Text('None picked.', style: muted)
else
for (final (i, path) in custom.indexed)
Row(
children: [
Expanded(
child: Tooltip(
message: path,
child: Text(path.split(RegExp(r'[/\\]')).last, overflow: TextOverflow.ellipsis),
),
),
IconButton(
tooltip: 'Remove',
icon: const Icon(Icons.close, size: 18),
onPressed: () => s.setCustomShaders([...custom]..removeAt(i)),
),
],
),
],
);
});
}
}
class UpdateSettings extends StatelessWidget {
final UpdateController? controller;
const UpdateSettings({super.key, this.controller});
@override
Widget build(BuildContext context) {
final c = controller ?? UpdateController.instance;
final s = AppSettings.instance;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Expanded(child: UpdateStatusLine(controller: c)),
ValueListenableBuilder(
valueListenable: c.status,
builder: (context, st, _) {
if (st.phase == UpdatePhase.idle || st.manifest == null) {
return const SizedBox.shrink();
}
return Padding(
padding: const EdgeInsets.only(right: 8),
child: FilledButton.tonal(
onPressed: () => showUpdateDialog(context, controller: c),
child: const Text('Show update'),
),
);
},
),
ValueListenableBuilder(
valueListenable: updateCheck,
builder: (context, check, _) => OutlinedButton(
onPressed: check == UpdateCheck.checking ? null : () => runUpdateCheck(c, true),
child: const Text('Check now'),
),
),
],
),
const SizedBox(height: 8),
SignalBuilder(
builder: (context) => SwitchListTile(
key: const ValueKey('auto-check-updates'),
contentPadding: EdgeInsets.zero,
title: const Text('Check for updates on start'),
subtitle: const Text('Looks for a new version when the app opens; nothing installs without asking'),
value: s.autoCheckUpdates.value,
onChanged: s.setAutoCheckUpdates,
),
),
],
);
}
}
class AutoAdvanceSetting extends StatelessWidget {
const AutoAdvanceSetting({super.key});
@override
Widget build(BuildContext context) {
final s = AppSettings.instance;
return SignalBuilder(
builder: (context) => SwitchListTile(
key: const ValueKey('auto-advance'),
contentPadding: EdgeInsets.zero,
title: const Text('Play the next episode automatically'),
subtitle: const Text('When a Jellyfin episode ends, a 10 s countdown puts up the next one; '
'anyone can cancel it'),
value: s.autoAdvance.value,
onChanged: s.setAutoAdvance,
),
);
}
}
class HideReReleasesSetting extends StatelessWidget {
const HideReReleasesSetting({super.key});
@override
Widget build(BuildContext context) {
final s = AppSettings.instance;
return SignalBuilder(
builder: (context) => SwitchListTile(
key: const ValueKey('hide-re-releases'),
contentPadding: EdgeInsets.zero,
title: const Text('Hide re-releases in Latest'),
subtitle: const Text('Skips episodes added more than 30 days after they aired '
'(re-uploads, upgrades); also in each Latest row\'s ⋮ menu'),
value: s.hideReReleases.value,
onChanged: s.setHideReReleases,
),
);
}
}
class ShortcutList extends StatelessWidget {
const ShortcutList({super.key});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('While watching:', style: TextStyle(color: Colors.grey)),
const SizedBox(height: 8),
for (final (key, what) in sessionShortcuts)
Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
SizedBox(
width: 96,
child: Align(
alignment: Alignment.centerLeft,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(6),
),
child: Text(key,
style: const TextStyle(fontFamily: 'monospace', fontSize: 13)),
),
),
),
Expanded(child: Text(what)),
],
),
),
],
);
}
}
+110
View File
@@ -0,0 +1,110 @@
import 'package:flutter/material.dart';
/// Product name (working title). Change it here only.
const appName = 'Starsync';
const repoUrl = 'https://git.senkochan.moe/Foxyz/sync_replay_project';
const kRadius = 12.0;
const kGap = 16.0;
/// Rounded panel used by every Settings section.
class Panel extends StatelessWidget {
final Widget child;
final EdgeInsetsGeometry padding;
final bool enabled;
const Panel(
{super.key,
required this.child,
this.padding = const EdgeInsets.all(20),
this.enabled = true});
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
// Material (not a DecoratedBox) so ink from tiles/chips inside shows.
final panel = Material(
color: scheme.surfaceContainer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(kRadius),
side: BorderSide(color: scheme.outlineVariant.withValues(alpha: 0.5)),
),
clipBehavior: Clip.antiAlias,
child: Padding(padding: padding, child: child),
);
if (enabled) return panel;
return Opacity(opacity: 0.5, child: IgnorePointer(child: panel));
}
}
/// Small grey heading inside a panel.
class SectionLabel extends StatelessWidget {
final String text;
final Widget? trailing;
const SectionLabel(this.text, {super.key, this.trailing});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Row(
children: [
Expanded(
child: Text(text, style: Theme.of(context).textTheme.titleMedium),
),
?trailing,
],
),
);
}
}
/// Outlined text field style (settings, Jellyfin sign-in).
InputDecoration fieldDecoration(String label, {String? hint, Widget? suffix}) =>
InputDecoration(
labelText: label,
hintText: hint,
isDense: true,
suffixIcon: suffix,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
);
/// Widest a single text field gets, however wide the window.
const kFieldMaxWidth = 720.0;
/// Inline failure under a connect form: bold title, selectable message.
class ErrorBox extends StatelessWidget {
final String title;
final String message;
const ErrorBox({super.key, required this.title, required this.message});
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: scheme.errorContainer.withValues(alpha: 0.35),
borderRadius: BorderRadius.circular(8),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(Icons.error_outline, size: 18, color: scheme.error),
const SizedBox(width: 8),
Expanded(
child: SelectableText.rich(
TextSpan(children: [
TextSpan(
text: '$title\n',
style: const TextStyle(fontWeight: FontWeight.w600)),
TextSpan(text: message),
]),
style: const TextStyle(fontSize: 13),
),
),
],
),
);
}
}
@@ -0,0 +1,139 @@
import 'package:flutter/material.dart';
import '../../update/update_controller.dart';
import '../../widgets/update_banner.dart';
import 'ui.dart';
/// Compact "Update vX" pill for the party strip; hidden until there is
/// something to install. Click opens the full [UpdateBanner].
class UpdateIndicator extends StatelessWidget {
final UpdateController? controller;
const UpdateIndicator({super.key, this.controller});
@override
Widget build(BuildContext context) {
final c = controller ?? UpdateController.instance;
return ValueListenableBuilder<UpdateStatus>(
valueListenable: c.status,
builder: (context, s, _) {
final m = s.manifest;
if (s.phase == UpdatePhase.idle || m == null) {
return const SizedBox.shrink();
}
final scheme = Theme.of(context).colorScheme;
final busy = s.phase == UpdatePhase.downloading ||
s.phase == UpdatePhase.applying ||
s.phase == UpdatePhase.restarting;
final label = switch (s.phase) {
UpdatePhase.downloading =>
'Updating ${((s.progress ?? 0) * 100).round()}%',
UpdatePhase.applying => 'Installing…',
UpdatePhase.restarting => 'Restarting…',
UpdatePhase.failed => 'Update failed',
_ => m.isRequired ? 'Required update' : 'Update v${m.version}',
};
return Tooltip(
message: 'Show update',
child: FilledButton.tonalIcon(
style: FilledButton.styleFrom(
backgroundColor: m.isRequired || s.phase == UpdatePhase.failed
? scheme.errorContainer
: null,
visualDensity: VisualDensity.compact,
),
onPressed: () => showUpdateDialog(context, controller: c),
icon: busy
? const SizedBox(
width: 14,
height: 14,
child: CircularProgressIndicator(strokeWidth: 2))
: const Icon(Icons.system_update_alt, size: 18),
label: Text(label),
),
);
},
);
}
}
/// The existing update card in a dialog; closes itself when the card
/// would render nothing (e.g. "Later").
Future<void> showUpdateDialog(BuildContext context, {UpdateController? controller}) {
final c = controller ?? UpdateController.instance;
return showDialog<void>(
context: context,
builder: (context) => Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(kRadius)),
child: SizedBox(
width: 420,
child: ValueListenableBuilder<UpdateStatus>(
valueListenable: c.status,
builder: (context, s, _) {
if (s.phase == UpdatePhase.idle || s.manifest == null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (context.mounted) Navigator.of(context).maybePop();
});
}
return Padding(
padding: const EdgeInsets.all(8),
child: UpdateBanner(controller: c),
);
},
),
),
),
);
}
enum UpdateCheck { notRun, checking, done }
/// Progress of this run's update check, for Settings/About wording.
final updateCheck = ValueNotifier(UpdateCheck.notRun);
/// [force] re-checks even if this process already checked ("Check now").
Future<void> runUpdateCheck([UpdateController? controller, bool force = false]) async {
final c = controller ?? UpdateController.instance;
updateCheck.value = UpdateCheck.checking;
try {
await (force ? c.checkNow() : c.checkOnce());
} finally {
updateCheck.value = UpdateCheck.done;
}
}
/// One-line update state: "Up to date", "Update v1.2 available", ...
class UpdateStatusLine extends StatelessWidget {
final UpdateController? controller;
const UpdateStatusLine({super.key, this.controller});
@override
Widget build(BuildContext context) {
final c = controller ?? UpdateController.instance;
return ListenableBuilder(
listenable: Listenable.merge([c.status, c.lastCheck, updateCheck]),
builder: (context, _) {
final s = c.status.value;
final m = s.manifest;
final String text;
if (c.skipDevBuilds && c.currentVersion.endsWith('-dev')) {
text = 'Dev build — update checks are off';
} else if (s.phase != UpdatePhase.idle && m != null) {
text = s.phase == UpdatePhase.failed
? 'Update to v${m.version} failed'
: 'Update v${m.version} available';
} else {
text = switch (updateCheck.value) {
UpdateCheck.notRun => 'Not checked yet',
UpdateCheck.checking => 'Checking…',
UpdateCheck.done => c.lastCheck.value ?? 'Not checked yet',
};
}
return Text(text,
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(color: Colors.grey));
},
);
}
}
@@ -0,0 +1,190 @@
import 'package:flutter/material.dart';
import '../../jellyfin/session_media.dart';
import '../../session/session_coordinator.dart' show AutoAdvance, autoAdvanceSecs;
import 'player_controls.dart' show FloatingPill;
// Jellyfin pieces of the session player. Plain widgets fed by the
// coordinator's signals, so tests can pump them without the player.
/// Previous / next episode in the video controls. Nothing when there is
/// no such episode or this viewer cannot put it up (no account).
class EpisodeSkipButton extends StatelessWidget {
final EpisodeLink? episode;
final bool next;
final VoidCallback onPressed;
final FocusNode? focusNode;
const EpisodeSkipButton(
{super.key, required this.episode, required this.next, required this.onPressed, this.focusNode});
@override
Widget build(BuildContext context) {
final e = episode;
if (e == null) return const SizedBox.shrink();
return Tooltip(
message: '${next ? 'Next' : 'Previous'}: ${e.label} (${next ? '> or Shift+N' : '< or Shift+P'})',
child: IconButton(
key: ValueKey(next ? 'episode-next' : 'episode-previous'),
focusNode: focusNode,
color: Colors.white,
iconSize: 22,
visualDensity: VisualDensity.compact,
icon: Icon(next ? Icons.skip_next_rounded : Icons.skip_previous_rounded),
onPressed: onPressed,
),
);
}
}
/// Floating "Next episode ›" from the ED (or the last two minutes):
/// faint at rest, lit on hover. Lives outside the auto-hiding controls, so
/// it shows in fullscreen and with the controls hidden.
class NextEpisodePill extends StatelessWidget {
final EpisodeLink episode;
final VoidCallback onPressed;
const NextEpisodePill({super.key, required this.episode, required this.onPressed});
@override
Widget build(BuildContext context) => FloatingPill(
label: 'Next episode',
tooltip: 'Next: ${episode.label}',
tapKey: const ValueKey('next-episode-pill'),
onPressed: onPressed,
);
}
/// At the end of an episode: "Next: S1E7 title" with Play now, and while
/// the auto-advance countdown runs, the seconds left and Cancel.
class EndOfEpisodeCard extends StatelessWidget {
final EpisodeLink next;
final AutoAdvance? countdown;
final VoidCallback onPlayNow;
final VoidCallback onCancel;
const EndOfEpisodeCard({
super.key,
required this.next,
required this.countdown,
required this.onPlayNow,
required this.onCancel,
});
@override
Widget build(BuildContext context) {
final t = Theme.of(context).textTheme;
final c = countdown;
return Card(
key: const ValueKey('end-of-episode'),
color: Colors.black.withValues(alpha: 0.85),
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 12, 12, 12),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 380),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(c == null ? 'Up next' : 'Next episode in ${c.secondsLeft} s',
style: t.labelLarge?.copyWith(color: Colors.white70)),
const SizedBox(height: 4),
Text('Next: ${next.label}',
maxLines: 2, overflow: TextOverflow.ellipsis, style: t.titleMedium),
if (c != null) ...[
const SizedBox(height: 8),
LinearProgressIndicator(value: c.secondsLeft / autoAdvanceSecs),
],
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (c != null)
TextButton(
key: const ValueKey('auto-advance-cancel'),
onPressed: onCancel,
child: const Text('Cancel')),
const SizedBox(width: 8),
FilledButton.icon(
key: const ValueKey('play-next'),
onPressed: onPlayNow,
icon: const Icon(Icons.skip_next_rounded, size: 18),
label: Text(c == null ? 'Play next' : 'Play now'),
),
],
),
],
),
),
),
);
}
}
/// This viewer cannot play the Jellyfin media: say why and offer to link
/// an account (never a silent stall).
class JellyfinBlockedCard extends StatelessWidget {
final BlockedPlay blocked;
final VoidCallback onLinkAccount;
final VoidCallback onRetry;
const JellyfinBlockedCard(
{super.key, required this.blocked, required this.onLinkAccount, required this.onRetry});
@override
Widget build(BuildContext context) {
final t = Theme.of(context).textTheme;
return Card(
key: const ValueKey('jellyfin-blocked'),
color: Colors.black.withValues(alpha: 0.85),
child: Padding(
padding: const EdgeInsets.all(20),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 440),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.lock_outline, color: Colors.orange, size: 32),
const SizedBox(height: 10),
Text(blocked.message, textAlign: TextAlign.center, style: t.titleSmall),
const SizedBox(height: 6),
Text(
'Whoever put it up passes it on automatically while their app is open; '
'if they left, ask them to put it up again. Or link your own account in Integrations.',
textAlign: TextAlign.center, style: t.bodySmall?.copyWith(color: Colors.grey)),
const SizedBox(height: 14),
Wrap(
spacing: 8,
children: [
FilledButton(
key: const ValueKey('link-jellyfin-account'),
onPressed: onLinkAccount,
child: const Text('Link a Jellyfin account')),
TextButton(onPressed: onRetry, child: const Text('Try again')),
],
),
],
),
),
),
);
}
}
/// Small member-list badge: plays the Jellyfin item through the host.
class ViaHostBadge extends StatelessWidget {
const ViaHostBadge({super.key});
@override
Widget build(BuildContext context) {
return Tooltip(
message: 'No Jellyfin account here: watches through the host',
child: Container(
key: const ValueKey('via-host'),
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 1),
decoration: BoxDecoration(
color: Colors.white10,
borderRadius: BorderRadius.circular(999),
border: Border.all(color: Colors.white24),
),
child: const Text('via host', style: TextStyle(fontSize: 10, color: Colors.white70)),
),
);
}
}
@@ -0,0 +1,624 @@
import 'dart:async';
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:media_kit/media_kit.dart';
import 'package:sync_relay_core/core.dart' show JellyfinStreamType;
import '../../jellyfin/track_prefs.dart' show MpvTrack;
import '../../playback/media_marks.dart';
// Session control-bar pieces built on the playback signals (chapters,
// tracks, skip offers): the seek bar with chapter marks, the chapter and
// track menus, the 1:30 skip button and the floating skip pill. Plain
// widgets fed by values and callbacks, so tests pump them without a player.
/// Keeps media_kit's desktop controls shown, as a mouse hover would.
/// While a seek bar drag runs no hover events arrive, and the controls
/// would hide (and unmount the bar) mid-drag. A guarded dynamic call on
/// the private controls state; nothing happens outside those controls.
void keepVideoControlsAwake(BuildContext context) {
context.visitAncestorElements((e) {
if (e is StatefulElement &&
e.state.runtimeType.toString() == '_MaterialDesktopVideoControlsState') {
try {
(e.state as dynamic).onHover();
} catch (_) {}
return false;
}
return true;
});
}
/// The seek bar: played / buffered track, a diamond at each chapter start
/// (not the one at 0), and on hover the time with the chapter title above
/// it. Like media_kit's bar it reports a seek on every drag move and on
/// release ([onSeek]); the session routes those through the user-seek path
/// (debounced, everyone follows).
class ChapterSeekBar extends StatefulWidget {
final Duration position;
final Duration duration;
final Duration buffer;
final List<MediaChapter> chapters;
final ValueChanged<Duration> onSeek;
/// Any pointer activity on the bar (keeps the controls up).
final VoidCallback? onActivity;
const ChapterSeekBar({
super.key,
required this.position,
required this.duration,
this.buffer = Duration.zero,
this.chapters = const [],
required this.onSeek,
this.onActivity,
});
static const height = 28.0;
@override
State<ChapterSeekBar> createState() => _ChapterSeekBarState();
}
class _ChapterSeekBarState extends State<ChapterSeekBar> {
double? _hoverX;
bool _dragging = false;
double _dragFraction = 0;
double _fraction(Duration d) {
final total = widget.duration.inMicroseconds;
if (total <= 0) return 0;
return (d.inMicroseconds / total).clamp(0.0, 1.0);
}
Duration _at(double fraction) => widget.duration * fraction.clamp(0.0, 1.0);
void _seekTo(double x, double width) {
if (widget.duration <= Duration.zero || width <= 0) return;
_dragFraction = (x / width).clamp(0.0, 1.0);
widget.onSeek(_at(_dragFraction));
}
@override
Widget build(BuildContext context) {
final primary = Theme.of(context).colorScheme.primary;
return LayoutBuilder(builder: (context, constraints) {
final width = constraints.maxWidth;
final active = _hoverX != null || _dragging;
final barHeight = active ? 5.0 : 3.0;
final played = _dragging ? _dragFraction : _fraction(widget.position);
const mid = ChapterSeekBar.height / 2;
final hoverX = _hoverX;
final hoverAt = hoverX == null || width <= 0 ? null : _at(hoverX / width);
final hoverChapter = hoverAt == null ? null : chapterIndexAt(widget.chapters, hoverAt);
final hoverTitle = hoverChapter == null ? '' : widget.chapters[hoverChapter].title;
return MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: (e) => setState(() => _hoverX = e.localPosition.dx.clamp(0, width)),
onHover: (e) {
setState(() => _hoverX = e.localPosition.dx.clamp(0, width));
widget.onActivity?.call();
},
onExit: (_) => setState(() => _hoverX = null),
child: Listener(
behavior: HitTestBehavior.opaque,
onPointerDown: (e) {
widget.onActivity?.call();
setState(() {
_dragging = true;
_dragFraction = width <= 0 ? 0 : (e.localPosition.dx / width).clamp(0.0, 1.0);
});
},
onPointerMove: (e) {
widget.onActivity?.call();
setState(() => _hoverX = e.localPosition.dx.clamp(0, width));
_seekTo(e.localPosition.dx, width);
},
onPointerUp: (e) {
_seekTo(e.localPosition.dx, width);
setState(() => _dragging = false);
widget.onActivity?.call();
},
onPointerCancel: (_) => setState(() => _dragging = false),
child: SizedBox(
key: const ValueKey('seek-bar'),
width: width,
height: ChapterSeekBar.height,
child: Stack(
clipBehavior: Clip.none,
children: [
Positioned(
left: 0,
right: 0,
top: mid - barHeight / 2,
height: barHeight,
child: Container(color: Colors.white24),
),
Positioned(
left: 0,
top: mid - barHeight / 2,
height: barHeight,
width: width * _fraction(widget.buffer),
child: Container(color: Colors.white24),
),
if (hoverX != null && !_dragging)
Positioned(
left: 0,
top: mid - barHeight / 2,
height: barHeight,
width: hoverX,
child: Container(color: Colors.white24),
),
Positioned(
left: 0,
top: mid - barHeight / 2,
height: barHeight,
width: width * played,
child: Container(color: primary),
),
for (final c in widget.chapters)
if (c.start > Duration.zero && widget.duration > Duration.zero)
Positioned(
key: ValueKey('chapter-mark-${c.index}'),
left: width * _fraction(c.start) - 4,
top: mid - 4,
width: 8,
height: 8,
child: IgnorePointer(
child: Transform.rotate(
angle: math.pi / 4,
child: Container(
margin: const EdgeInsets.all(1),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.black54, width: 0.8),
),
),
),
),
),
if (active)
Positioned(
left: width * played - 6,
top: mid - 6,
width: 12,
height: 12,
child: IgnorePointer(
child: DecoratedBox(
decoration: BoxDecoration(color: primary, shape: BoxShape.circle),
),
),
),
if (hoverAt != null)
Positioned(
// Kept clear of the window edges (the tip is centred).
left: hoverX!.clamp(math.min(60.0, width / 2), math.max(width - 60, width / 2)),
bottom: ChapterSeekBar.height - 2,
child: IgnorePointer(
child: FractionalTranslation(
translation: const Offset(-0.5, 0),
child: _HoverTip(title: hoverTitle, time: formatClock(hoverAt)),
),
),
),
],
),
),
),
);
});
}
}
class _HoverTip extends StatelessWidget {
final String title;
final String time;
const _HoverTip({required this.title, required this.time});
@override
Widget build(BuildContext context) {
return Container(
constraints: const BoxConstraints(maxWidth: 320),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.8),
borderRadius: BorderRadius.circular(6),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (title.isNotEmpty)
Text(title,
key: const ValueKey('seek-hover-chapter'),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w600)),
Text(time,
key: const ValueKey('seek-hover-time'),
style: const TextStyle(color: Colors.white70, fontSize: 11)),
],
),
);
}
}
/// [ChapterSeekBar] on a live player: position, duration and buffer from
/// its streams. Seeks go to `player.seek`, which the session's
/// `SyncPlayer` turns into user seeks.
class PlayerSeekBar extends StatefulWidget {
final Player player;
final List<MediaChapter> chapters;
const PlayerSeekBar({super.key, required this.player, required this.chapters});
@override
State<PlayerSeekBar> createState() => _PlayerSeekBarState();
}
class _PlayerSeekBarState extends State<PlayerSeekBar> {
final List<StreamSubscription<Object>> _subs = [];
late Duration _position, _duration, _buffer;
@override
void initState() {
super.initState();
_listen();
}
@override
void didUpdateWidget(PlayerSeekBar old) {
super.didUpdateWidget(old);
if (!identical(old.player, widget.player)) {
_cancel();
_listen();
}
}
void _listen() {
final p = widget.player;
_position = p.state.position;
_duration = p.state.duration;
_buffer = p.state.buffer;
void set(VoidCallback f) {
if (mounted) setState(f);
}
_subs.addAll([
p.stream.position.listen((d) => set(() => _position = d)),
p.stream.duration.listen((d) => set(() => _duration = d)),
p.stream.buffer.listen((d) => set(() => _buffer = d)),
]);
}
void _cancel() {
for (final s in _subs) {
s.cancel();
}
_subs.clear();
}
@override
void dispose() {
_cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ChapterSeekBar(
position: _position,
duration: _duration,
buffer: _buffer,
chapters: widget.chapters,
onSeek: (d) => widget.player.seek(d),
onActivity: () => keepVideoControlsAwake(context),
);
}
}
RelativeRect _menuPosition(BuildContext context) {
final button = context.findRenderObject() as RenderBox;
final overlay = Navigator.of(context).overlay!.context.findRenderObject() as RenderBox;
return RelativeRect.fromRect(
Rect.fromPoints(
button.localToGlobal(Offset.zero, ancestor: overlay),
button.localToGlobal(button.size.bottomRight(Offset.zero), ancestor: overlay),
),
Offset.zero & overlay.size,
);
}
/// Control-bar icon with a small count in the corner.
class _BadgedIcon extends StatelessWidget {
final IconData icon;
final int count;
const _BadgedIcon(this.icon, this.count);
@override
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
children: [
Icon(icon),
if (count > 0)
Positioned(
right: -7,
top: -5,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 0.5),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
borderRadius: BorderRadius.circular(999),
),
child: Text('$count',
style: const TextStyle(color: Colors.white, fontSize: 9, fontWeight: FontWeight.bold)),
),
),
],
);
}
}
/// Chapter list for the controls: current chapter checked, a pick seeks
/// to its start. Nothing when the file has no chapters.
class ChaptersControlButton extends StatelessWidget {
final List<MediaChapter> chapters;
final int? current;
final ValueChanged<MediaChapter> onPick;
final FocusNode? focusNode;
final ValueChanged<bool>? onMenuOpen;
const ChaptersControlButton({
super.key,
required this.chapters,
required this.current,
required this.onPick,
this.focusNode,
this.onMenuOpen,
});
@override
Widget build(BuildContext context) {
if (chapters.isEmpty) return const SizedBox.shrink();
return Tooltip(
message: 'Chapters (PgUp / PgDn)',
child: IconButton(
key: const ValueKey('chapters-button'),
focusNode: focusNode,
color: Colors.white,
iconSize: 20,
visualDensity: VisualDensity.compact,
icon: _BadgedIcon(Icons.format_list_bulleted, chapters.length),
onPressed: () => _open(context),
),
);
}
Future<void> _open(BuildContext context) async {
final position = _menuPosition(context);
onMenuOpen?.call(true);
final pick = await showMenu<MediaChapter>(
context: context,
position: position,
popUpAnimationStyle: AnimationStyle.noAnimation,
constraints: const BoxConstraints(minWidth: 240, maxWidth: 420),
items: [
for (final c in chapters)
CheckedPopupMenuItem(
value: c,
checked: c.index == current,
height: 36,
child: Row(
children: [
Expanded(
child: Text(c.title.isEmpty ? 'Chapter ${c.index + 1}' : c.title,
maxLines: 1, overflow: TextOverflow.ellipsis),
),
const SizedBox(width: 12),
Text(formatClock(c.start), style: const TextStyle(fontSize: 12, color: Colors.grey)),
],
),
),
],
);
onMenuOpen?.call(false);
if (pick != null) onPick(pick);
}
}
/// A track pick from [TrackMenuButton]: audio id, subtitle id, or off.
typedef TrackPick = ({int? aid, int? sid, bool subtitlesOff});
/// Audio or subtitle menu for the controls bar (the count of tracks on the
/// icon): "Japanese · E-AC3 2.0 · title", mpv's selection checked,
/// subtitles with Off. The choice is remembered for the next Jellyfin item
/// (per viewer).
class TrackMenuButton extends StatelessWidget {
final JellyfinStreamType type;
/// All tracks; only those of [type] are listed.
final List<MpvTrack> tracks;
final void Function(TrackPick pick) onPick;
final FocusNode? focusNode;
final ValueChanged<bool>? onMenuOpen;
const TrackMenuButton({
super.key,
required this.type,
required this.tracks,
required this.onPick,
this.focusNode,
this.onMenuOpen,
});
bool get _audio => type == JellyfinStreamType.audio;
@override
Widget build(BuildContext context) {
final list = tracks.where((t) => t.type == type).toList();
return Tooltip(
message: _audio ? 'Audio' : 'Subtitles',
child: IconButton(
key: ValueKey(_audio ? 'audio-button' : 'subtitles-button'),
focusNode: focusNode,
color: Colors.white,
iconSize: 20,
visualDensity: VisualDensity.compact,
icon: _BadgedIcon(_audio ? Icons.audiotrack_outlined : Icons.subtitles_outlined, list.length),
onPressed: () => _open(context, list),
),
);
}
Future<void> _open(BuildContext context, List<MpvTrack> list) async {
final position = _menuPosition(context);
onMenuOpen?.call(true);
final pick = await showMenu<TrackPick>(
context: context,
position: position,
popUpAnimationStyle: AnimationStyle.noAnimation,
constraints: const BoxConstraints(minWidth: 280, maxWidth: 460),
items: [
if (!_audio)
CheckedPopupMenuItem(
value: (aid: null, sid: null, subtitlesOff: true),
checked: !list.any((t) => t.selected),
height: 36,
child: const Text('Off'),
),
if (_audio && list.isEmpty)
const PopupMenuItem(enabled: false, height: 32, child: Text('None')),
for (final t in list)
CheckedPopupMenuItem(
value: _audio
? (aid: t.id, sid: null, subtitlesOff: false)
: (aid: null, sid: t.id, subtitlesOff: false),
checked: t.selected,
height: 36,
child: Text(t.label, maxLines: 1, overflow: TextOverflow.ellipsis),
),
],
);
onMenuOpen?.call(false);
if (pick != null) onPick(pick);
}
}
/// "1:30" in the controls: skip forward [fallbackSkip] (Shift+Right).
class SkipForwardButton extends StatelessWidget {
final VoidCallback onPressed;
final FocusNode? focusNode;
const SkipForwardButton({super.key, required this.onPressed, this.focusNode});
@override
Widget build(BuildContext context) {
return Tooltip(
message: 'Skip 1:30 for everyone (Shift+Right)',
child: TextButton.icon(
key: const ValueKey('skip-forward-button'),
focusNode: focusNode,
style: TextButton.styleFrom(
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 8),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
onPressed: onPressed,
icon: const Icon(Icons.fast_forward_rounded, size: 16),
label: const Text('1:30', style: TextStyle(fontSize: 12)),
),
);
}
}
/// Floating pill over the video (Next episode, Skip OP / ED / 1:30):
/// faint at rest, lit on hover. Lives outside the auto-hiding controls, so
/// it shows in fullscreen and with the controls hidden.
class FloatingPill extends StatefulWidget {
final String label;
final String tooltip;
final IconData icon;
final VoidCallback onPressed;
/// Key of the tap target (tests find the pill by it).
final Key? tapKey;
const FloatingPill({
super.key,
required this.label,
required this.tooltip,
required this.onPressed,
this.icon = Icons.chevron_right,
this.tapKey,
});
@override
State<FloatingPill> createState() => _FloatingPillState();
}
class _FloatingPillState extends State<FloatingPill> {
bool _hover = false;
@override
Widget build(BuildContext context) {
final primary = Theme.of(context).colorScheme.primary;
return MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: (_) => setState(() => _hover = true),
onExit: (_) => setState(() => _hover = false),
child: Tooltip(
message: widget.tooltip,
child: AnimatedScale(
duration: const Duration(milliseconds: 150),
scale: _hover ? 1.06 : 1,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 150),
opacity: _hover ? 1 : 0.45,
child: GestureDetector(
key: widget.tapKey,
onTap: widget.onPressed,
child: AnimatedContainer(
duration: const Duration(milliseconds: 150),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 9),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.75),
borderRadius: BorderRadius.circular(999),
border: Border.all(color: _hover ? primary : Colors.white24),
boxShadow: _hover
? [BoxShadow(color: primary.withValues(alpha: 0.45), blurRadius: 16)]
: const [],
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(widget.label, style: const TextStyle(color: Colors.white, fontSize: 13)),
const SizedBox(width: 4),
Icon(widget.icon, color: Colors.white, size: 18),
],
),
),
),
),
),
),
);
}
}
/// "Skip OP" / "Skip ED" / "Skip 1:30" for a [SkipOffer].
class SkipPill extends StatelessWidget {
final SkipOffer offer;
final VoidCallback onPressed;
const SkipPill({super.key, required this.offer, required this.onPressed});
static String labelFor(SkipKind kind) => switch (kind) {
SkipKind.opening => 'Skip OP',
SkipKind.ending => 'Skip ED',
SkipKind.fixed => 'Skip 1:30',
};
@override
Widget build(BuildContext context) {
return FloatingPill(
label: labelFor(offer.kind),
tooltip: offer.kind == SkipKind.fixed
? 'Skip 1:30 for everyone (Shift+Right)'
: 'Skip to ${formatClock(offer.target)} for everyone',
icon: Icons.fast_forward_rounded,
tapKey: const ValueKey('skip-pill'),
onPressed: onPressed,
);
}
}
@@ -1,941 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:media_kit_video/media_kit_video.dart';
import 'package:signals/signals_flutter.dart';
import 'package:sync_relay_core/core.dart';
import '../session/health.dart';
import '../state/session_state.dart';
import '../update/updater.dart';
import '../widgets/dialogs.dart';
import '../widgets/member_avatar.dart';
import '../widgets/window_buttons.dart';
import '../widgets/window_drag_layer.dart';
class SessionScreen extends StatefulWidget {
const SessionScreen({super.key});
@override
State<SessionScreen> createState() => _SessionScreenState();
}
class _SessionScreenState extends State<SessionScreen> {
VideoController? _video;
final _videoKey = GlobalKey<VideoState>();
final _sheetOpen = signal(false);
final _confirmLeave = signal(false);
final _isFullscreen = signal(false);
DateTime? _lastKeyAt;
final _chat = TextEditingController();
final _chatFocus = FocusNode();
final _swapLink = TextEditingController();
late final void Function() _stopKickWatch;
late final void Function() _stopSheetWatch;
late final FocusNode _noFocus;
@override
void initState() {
super.initState();
final session = SessionState.instance;
_noFocus = FocusNode(skipTraversal: true, canRequestFocus: false);
_video = VideoController(session.coordinator.playback.player);
HardwareKeyboard.instance.addHandler(_globalKey);
_stopKickWatch = effect(() {
if (session.coordinator.kickReason.value != null) {
session.route.value = AppRoute.connect;
}
});
_stopSheetWatch = effect(() {
if (!_sheetOpen.value) {
FocusManager.instance.primaryFocus?.unfocus();
}
});
}
void _toggleFullscreen() {
if (_isFullscreen.peek()) {
if (mounted) {
Navigator.of(context, rootNavigator: true).maybePop();
}
} else {
_videoKey.currentState?.enterFullscreen();
}
}
@override
void dispose() {
HardwareKeyboard.instance.removeHandler(_globalKey);
_stopKickWatch();
_stopSheetWatch();
_chat.dispose();
_chatFocus.dispose();
_swapLink.dispose();
_noFocus.dispose();
super.dispose();
}
bool get _editing {
var n = FocusManager.instance.primaryFocus;
while (n != null) {
final w = n.context?.widget;
if (w is EditableText) return true;
n = n.parent;
}
return false;
}
bool _globalKey(KeyEvent event) {
if (event is! KeyDownEvent) return false;
if (SessionState.instance.route.peek() != AppRoute.session) return false;
if (_editing) return false;
final now = DateTime.now().toUtc();
if (_lastKeyAt != null && now.difference(_lastKeyAt!).inMilliseconds < 350) {
return false;
}
final coord = SessionState.instance.coordinator;
if (event.logicalKey == LogicalKeyboardKey.space) {
_lastKeyAt = now;
coord.toggleReady();
return true;
}
if (event.logicalKey == LogicalKeyboardKey.tab) {
_lastKeyAt = now;
_sheetOpen.value = !_sheetOpen.peek();
coord.markInput();
return true;
}
if (event.logicalKey == LogicalKeyboardKey.arrowRight) {
_lastKeyAt = now;
coord.nudge(10);
return true;
}
if (event.logicalKey == LogicalKeyboardKey.arrowLeft) {
_lastKeyAt = now;
coord.nudge(-10);
return true;
}
if (event.logicalKey == LogicalKeyboardKey.escape) {
if (_sheetOpen.peek()) {
_sheetOpen.value = false;
return true;
}
if (_confirmLeave.peek()) {
_confirmLeave.value = false;
return true;
}
if (_isFullscreen.peek()) {
if (mounted) {
Navigator.of(context, rootNavigator: true).maybePop();
}
return true;
}
_lastKeyAt = now;
_confirmLeave.value = true;
return true;
}
if (event.logicalKey == LogicalKeyboardKey.keyF) {
_lastKeyAt = now;
if (_isFullscreen.peek()) {
if (mounted) {
Navigator.of(context, rootNavigator: true).maybePop();
}
} else {
_videoKey.currentState?.enterFullscreen();
}
return true;
}
return false;
}
@override
Widget build(BuildContext context) {
final coord = SessionState.instance.coordinator;
final controlsTheme = MaterialDesktopVideoControlsThemeData(
playAndPauseOnTap: false,
modifyVolumeOnScroll: false,
keyboardShortcuts: {},
visibleOnMount: true,
hideMouseOnControlsRemoval: true,
controlsHoverDuration: const Duration(seconds: 3),
topButtonBar: [
const Spacer(),
MaterialDesktopCustomButton(
icon: const Icon(Icons.group),
onPressed: () {
_sheetOpen.value = true;
coord.markInput();
},
),
SignalBuilder(
builder: (context) => WindowButtons(focusNode: _noFocus, fullscreen: _isFullscreen.value, onToggleFullscreen: _toggleFullscreen),
),
],
bottomButtonBar: [
const MaterialDesktopVolumeButton(),
_ReadyControlButton(focusNode: _noFocus),
_MarkAllReadyControlButton(focusNode: _noFocus),
const MaterialDesktopPositionIndicator(),
const Spacer(),
const MaterialDesktopFullscreenButton(),
],
);
return FocusTraversalGroup(
descendantsAreTraversable: false,
child: Scaffold(
backgroundColor: Colors.black,
body: _video == null
? const Center(
child: Text('player starting…', style: TextStyle(color: Colors.white)),
)
: MaterialDesktopVideoControlsTheme(
normal: controlsTheme,
fullscreen: controlsTheme,
child: Video(
key: _videoKey,
controller: _video!,
subtitleViewConfiguration: const SubtitleViewConfiguration(visible: false),
controls: _sessionControls,
onEnterFullscreen: () async {
_isFullscreen.value = true;
await defaultEnterNativeFullscreen();
},
onExitFullscreen: () async {
_isFullscreen.value = false;
await defaultExitNativeFullscreen();
},
),
),
),
);
}
Widget _sessionControls(VideoState state) {
return Stack(
children: [
MaterialDesktopVideoControls(state),
_healthOverlay(),
_waitingOverlay(),
_toastsOverlay(),
_sheetOverlay(),
_leaveOverlay(),
Positioned.fill(
child: SignalBuilder(
builder: (context) {
final sheetOpen = _sheetOpen.value;
final confirm = _confirmLeave.value;
return WindowDragLayer(
enabled: !confirm,
exclusions: [
(s) => Rect.fromLTWH(0, s.height - 120, s.width, 120),
if (sheetOpen) (s) => Rect.fromLTWH(s.width - 380, 0, 380, s.height),
],
);
},
),
),
],
);
}
Widget _healthOverlay() {
final coord = SessionState.instance.coordinator;
return Positioned(
top: 12,
left: 12,
child: SignalBuilder(
builder: (context) {
final h = coord.health.value;
final starve = coord.starving.value;
final retrying = coord.reconnecting.value;
final kicked = coord.kickReason.value;
final unwell = h.severity == HealthSeverity.warn || h.severity == HealthSeverity.error || retrying || starve != null;
if (!unwell) return const SizedBox.shrink();
final color = switch (h.severity) {
HealthSeverity.ok => Colors.green,
HealthSeverity.info => Colors.grey,
HealthSeverity.warn => Colors.orange,
HealthSeverity.error => Colors.red,
};
final IconData icon;
if (retrying) {
icon = Icons.sync;
} else if (h.severity == HealthSeverity.error) {
icon = Icons.error_outline;
} else if (h.severity == HealthSeverity.warn) {
icon = Icons.warning_amber_outlined;
} else {
icon = Icons.hourglass_empty;
}
final text = [h.message, if (starve != null) starve, if (retrying) 'reconnecting…'].join(' · ');
final canRetry = !retrying && kicked == null && (h.severity == HealthSeverity.error || h.severity == HealthSeverity.warn);
return Container(
decoration: BoxDecoration(color: Colors.black.withValues(alpha: 0.65), borderRadius: BorderRadius.circular(999)),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: color, size: 16),
const SizedBox(width: 8),
Flexible(
child: Text(text, style: const TextStyle(color: Colors.white, fontSize: 12)),
),
if (retrying)
TextButton(
style: TextButton.styleFrom(foregroundColor: Colors.white, tapTargetSize: MaterialTapTargetSize.shrinkWrap),
onPressed: () => coord.cancelReconnect(),
child: const Text('Cancel', style: TextStyle(fontSize: 12)),
),
if (canRetry)
TextButton(
style: TextButton.styleFrom(foregroundColor: Colors.white, tapTargetSize: MaterialTapTargetSize.shrinkWrap),
onPressed: () => coord.retry(),
child: const Text('Retry', style: TextStyle(fontSize: 12)),
),
],
),
);
},
),
);
}
Widget _waitingOverlay() {
final coord = SessionState.instance.coordinator;
return SignalBuilder(
builder: (context) {
final r = coord.room.value;
final waiting = [
for (final m in r?.members ?? [])
if (!m.ready) m,
];
final show = r != null && r.transport == 'held' && waiting.isNotEmpty;
final selfReady = coord.selfMember.value?.ready ?? false;
return Positioned.fill(
child: Center(
child: Visibility(
visible: show,
maintainState: true,
maintainAnimation: true,
maintainSize: true,
child: Column(
mainAxisSize: .min,
spacing: 12,
children: [
IgnorePointer(
child: Container(
decoration: BoxDecoration(color: Colors.black.withValues(alpha: 0.7), borderRadius: BorderRadiusGeometry.circular(20)),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: .min,
spacing: 10,
crossAxisAlignment: .center,
children: [
Text("Waiting for", style: TextTheme.of(context).labelLarge),
Wrap(
alignment: WrapAlignment.center,
spacing: -20,
runSpacing: 8,
children: [
for (final m in waiting)
Opacity(
opacity: m.stale ? 0.5 : 1.0,
child: MemberAvatar(avatarBase64: m.avatar, name: m.name, size: 96),
),
],
),
],
),
),
),
),
if (!selfReady)
FilledButton(
focusNode: _noFocus,
onPressed: () => coord.toggleReady(),
child: const Text("I'm Ready"),
),
],
),
),
),
);
},
);
}
Widget _toastsOverlay() {
final coord = SessionState.instance.coordinator;
return Positioned(
right: 12,
top: 56,
child: IgnorePointer(
child: SignalBuilder(
builder: (context) {
final items = coord.toasts.value;
return Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [for (final t in items.reversed.take(5)) _ToastCard(key: ValueKey(t.id), text: t.text)],
);
},
),
),
);
}
Widget _sheetOverlay() {
return SignalBuilder(
builder: (context) {
final open = _sheetOpen.value;
return ExcludeFocus(
excluding: !open,
child: Stack(
fit: StackFit.expand,
children: [
Positioned.fill(
child: IgnorePointer(
ignoring: !open,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 200),
opacity: open ? 1 : 0,
child: GestureDetector(
onTap: () => _sheetOpen.value = false,
child: Container(color: Colors.black54),
),
),
),
),
Positioned(
top: 20,
bottom: 20,
right: 0,
child: IgnorePointer(
ignoring: !open,
child: AnimatedSlide(
duration: const Duration(milliseconds: 250),
curve: Curves.easeOut,
offset: open ? Offset.zero : const Offset(1, 0),
child: SizedBox(
width: 380,
height: double.infinity,
child: _SheetPanel(
chat: _chat,
chatFocus: _chatFocus,
swapLink: _swapLink,
onClose: () => _sheetOpen.value = false,
onLeave: () {
_sheetOpen.value = false;
_confirmLeave.value = true;
},
),
),
),
),
),
],
),
);
},
);
}
Widget _leaveOverlay() {
final session = SessionState.instance;
return SignalBuilder(
builder: (context) {
if (!_confirmLeave.value) return const SizedBox.shrink();
return Positioned.fill(
child: GestureDetector(
onTap: () => _confirmLeave.value = false,
child: Container(
color: Colors.black54,
child: Center(
child: GestureDetector(
onTap: () {},
child: AlertDialog(
title: const Text('Leave room?'),
content: const Text('You can rejoin right away.'),
actions: [
TextButton(onPressed: () => _confirmLeave.value = false, child: const Text('Cancel')),
TextButton(
onPressed: () {
_confirmLeave.value = false;
session.leave();
},
child: const Text('Leave'),
),
],
),
),
),
),
),
);
},
);
}
}
class _ReadyControlButton extends StatelessWidget {
final FocusNode focusNode;
const _ReadyControlButton({required this.focusNode});
@override
Widget build(BuildContext context) {
return SignalBuilder(
builder: (context) {
final ready = SessionState.instance.coordinator.selfMember.value?.ready ?? false;
return TextButton(
focusNode: focusNode,
style: TextButton.styleFrom(
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 8),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
onPressed: () => SessionState.instance.coordinator.toggleReady(),
child: Text(ready ? 'Ready ✓' : 'Not ready', style: const TextStyle(fontSize: 12)),
);
},
);
}
}
class _MarkAllReadyControlButton extends StatelessWidget {
final FocusNode focusNode;
const _MarkAllReadyControlButton({required this.focusNode});
@override
Widget build(BuildContext context) {
return TextButton(
focusNode: focusNode,
style: TextButton.styleFrom(
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 8),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
onPressed: () => SessionState.instance.coordinator.forcePlayAll(),
child: const Text('Mark everyone ready', style: TextStyle(fontSize: 12)),
);
}
}
class _ToastCard extends StatefulWidget {
final String text;
const _ToastCard({super.key, required this.text});
@override
State<_ToastCard> createState() => _ToastCardState();
}
class _ToastCardState extends State<_ToastCard> {
bool _shown = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) setState(() => _shown = true);
});
}
@override
Widget build(BuildContext context) {
return AnimatedSlide(
duration: const Duration(milliseconds: 250),
curve: Curves.easeOut,
offset: _shown ? Offset.zero : const Offset(1, 0),
child: AnimatedOpacity(
duration: const Duration(milliseconds: 250),
opacity: _shown ? 1 : 0,
child: Card(
child: Padding(padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Text(widget.text)),
),
),
);
}
}
class _SheetPanel extends StatelessWidget {
final TextEditingController chat;
final FocusNode chatFocus;
final TextEditingController swapLink;
final VoidCallback onClose;
final VoidCallback onLeave;
const _SheetPanel({required this.chat, required this.chatFocus, required this.swapLink, required this.onClose, required this.onLeave});
@override
Widget build(BuildContext context) {
final coord = SessionState.instance.coordinator;
return Material(
borderRadius: BorderRadius.horizontal(left: .circular(20)),
elevation: 8,
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 16),
child: SignalBuilder(
builder: (context) {
final r = coord.room.value;
final log = coord.feedLog.value;
final theme = Theme.of(context);
final sectionStyle = theme.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.bold);
final members = r?.members ?? const <MemberView>[];
final readyCount = members.where((m) => m.ready).length;
final playing = r?.transport == 'playing';
return Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text('Room', style: theme.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold)),
const SizedBox(width: 8),
_StatusChip(playing: playing, hasRoom: r != null),
],
),
const SizedBox(height: 2),
Text(
r == null ? 'connecting…' : '${members.length} member${members.length == 1 ? '' : 's'} · $readyCount ready',
style: theme.textTheme.bodySmall,
),
],
),
),
IconButton(icon: const Icon(Icons.close), onPressed: onClose),
],
),
const Divider(height: 20),
Text('Users (${members.length})', style: sectionStyle),
const SizedBox(height: 2),
for (final m in members) _MemberRow(member: m, isSelf: m.id == coord.sync.memberId),
const Divider(height: 20),
Text('Activity', style: sectionStyle),
const SizedBox(height: 6),
if (log.isEmpty) Text('No events yet', style: theme.textTheme.bodySmall),
Expanded(
child: ListView.builder(
reverse: true,
itemBuilder: (context, index) => _ActivityRow(item: log.reversed.toList()[index]),
itemCount: log.length,
),
),
const SizedBox(height: 12),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: TextField(
controller: chat,
focusNode: chatFocus,
decoration: const InputDecoration(labelText: 'Chat message', border: OutlineInputBorder(), isDense: true),
onSubmitted: (v) {
coord.sendChat(v);
chat.clear();
chatFocus.requestFocus();
},
),
),
const SizedBox(width: 8),
IconButton(
icon: const Icon(Icons.send),
onPressed: () {
coord.sendChat(chat.text);
chat.clear();
chatFocus.requestFocus();
},
),
],
),
const SizedBox(height: 12),
Text('Share a link', style: sectionStyle),
const SizedBox(height: 8),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: TextField(
controller: swapLink,
decoration: const InputDecoration(labelText: 'Swap to link URL', border: OutlineInputBorder(), isDense: true),
),
),
const SizedBox(width: 8),
IconButton(
icon: const Icon(Icons.swap_horiz),
onPressed: () {
coord.swapLink(swapLink.text.trim());
swapLink.clear();
},
),
],
),
const Divider(height: 20),
SizedBox(
width: double.infinity,
child: OutlinedButton.icon(
style: OutlinedButton.styleFrom(foregroundColor: theme.colorScheme.error),
icon: const Icon(Icons.logout),
label: const Text('Leave room'),
onPressed: onLeave,
),
),
const SizedBox(height: 8),
const Center(
child: Text('v$appVersion', style: TextStyle(fontSize: 11, color: Colors.grey)),
),
],
);
},
),
),
);
}
}
class _StatusChip extends StatelessWidget {
final bool playing;
final bool hasRoom;
const _StatusChip({required this.playing, required this.hasRoom});
@override
Widget build(BuildContext context) {
final Color dot;
final String label;
if (!hasRoom) {
dot = Colors.grey;
label = '…';
} else if (playing) {
dot = Colors.green;
label = 'Playing';
} else {
dot = Colors.orange;
label = 'Waiting';
}
return Container(
decoration: BoxDecoration(color: Colors.white10, borderRadius: BorderRadius.circular(999)),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 7,
height: 7,
decoration: BoxDecoration(shape: BoxShape.circle, color: dot),
),
const SizedBox(width: 6),
Text(label, style: Theme.of(context).textTheme.labelSmall),
],
),
);
}
}
class _MemberRow extends StatelessWidget {
final MemberView member;
final bool isSelf;
const _MemberRow({required this.member, required this.isSelf});
@override
Widget build(BuildContext context) {
final coord = SessionState.instance.coordinator;
final theme = Theme.of(context);
final status = _memberStatus(member);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Row(
children: [
MemberAvatar(avatarBase64: member.avatar, name: member.name, size: 32),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
isSelf ? '${member.name} (you)' : member.name,
style: theme.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 2),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 8,
height: 8,
decoration: BoxDecoration(shape: BoxShape.circle, color: status.$2),
),
const SizedBox(width: 6),
Text(
status.$1,
style: theme.textTheme.bodySmall?.copyWith(color: theme.textTheme.bodySmall?.color?.withValues(alpha: 0.75)),
),
],
),
],
),
),
PopupMenuButton<String>(
icon: const Icon(Icons.more_vert, size: 18),
padding: EdgeInsets.zero,
onSelected: (v) async {
if (v == 'force') {
coord.forceTarget(member.id, !member.ready);
} else if (v == 'kick') {
final ok = await showKickConfirmDialog(context, member.name);
if (ok == true) coord.kick(member.id);
}
},
itemBuilder: (_) => [
PopupMenuItem(value: 'force', child: Text(member.ready ? 'Force unready' : 'Force ready')),
PopupMenuItem(
value: 'kick',
child: Text('Kick', style: TextStyle(color: theme.colorScheme.error)),
),
],
),
],
),
);
}
}
(String, Color) _memberStatus(MemberView m) {
if (m.stale) return ('Stale', Colors.orange);
if (!m.ready) return ('Not ready', Colors.grey);
if (m.buffered < RelayConstants.minBufferSecs) return ('Buffering ${m.buffered.toStringAsFixed(0)}s', Colors.orange);
return ('Ready', Colors.green);
}
class _ActivityRow extends StatelessWidget {
final FeedMessage item;
const _ActivityRow({required this.item});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final time = _clock(item.at);
final subtle = theme.textTheme.bodySmall?.color?.withValues(alpha: 0.6) ?? Colors.grey;
if (item.action == 'chat') {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 3),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(Icons.chat_bubble_outline, size: 15, color: Colors.grey),
const SizedBox(width: 8),
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: '${item.actor} ',
style: theme.textTheme.bodySmall?.copyWith(fontWeight: FontWeight.bold),
),
TextSpan(text: item.reason, style: theme.textTheme.bodySmall),
],
),
),
),
const SizedBox(width: 8),
Text(time, style: theme.textTheme.labelSmall?.copyWith(color: subtle)),
],
),
);
}
if (item.actor == 'server') {
final style = _serverFeedStyle(item);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 3),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(style.$1, size: 15, color: style.$2),
const SizedBox(width: 8),
Expanded(child: Text(style.$3, style: theme.textTheme.bodySmall)),
const SizedBox(width: 8),
Text(time, style: theme.textTheme.labelSmall?.copyWith(color: subtle)),
],
),
);
}
final style = _feedStyle(item, theme);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 3),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(style.$1, size: 15, color: style.$2),
const SizedBox(width: 8),
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: item.actor,
style: theme.textTheme.bodySmall?.copyWith(fontWeight: FontWeight.bold),
),
TextSpan(text: ' ${style.$3}', style: theme.textTheme.bodySmall),
],
),
),
),
const SizedBox(width: 8),
Text(time, style: theme.textTheme.labelSmall?.copyWith(color: subtle)),
],
),
);
}
}
(IconData, Color, String) _feedStyle(FeedMessage f, ThemeData theme) {
switch (f.action) {
case 'join':
return (Icons.person_add, Colors.green, 'joined');
case 'leave':
final reason = f.reason == 'silent too long' ? ' · timed out' : (f.reason.isEmpty ? '' : ' · ${f.reason}');
return (Icons.logout, Colors.grey, 'left$reason');
case 'ready':
return (Icons.check_circle, Colors.green, 'is ready');
case 'unready':
return (Icons.radio_button_unchecked, Colors.grey, "isn't ready");
case 'force-ready':
return (Icons.how_to_reg, Colors.orange, 'marked ${f.target ?? ''} ready');
case 'force-unready':
return (Icons.how_to_reg, Colors.orange, 'marked ${f.target ?? ''} not ready');
case 'force-play':
return (Icons.play_arrow, Colors.green, 'started playback for everyone');
case 'force-hold':
return (Icons.pause, Colors.orange, 'held playback for everyone');
case 'seek':
return (Icons.fast_forward, theme.colorScheme.primary, 'seeked');
case 'swap':
return (Icons.movie, theme.colorScheme.primary, 'changed the media');
case 'stale':
return (Icons.cloud_off, Colors.orange, 'lost signal');
case 'kick':
final reason = f.reason.isEmpty ? '' : ' · ${f.reason}';
return (Icons.gavel, Colors.red, 'kicked ${f.target ?? ''}$reason');
default:
final target = f.target == null || f.target!.isEmpty ? '' : ' ${f.target}';
return (Icons.info_outline, Colors.grey, '${f.action}$target');
}
}
(IconData, Color, String) _serverFeedStyle(FeedMessage f) {
switch (f.action) {
case 'resume':
return (Icons.play_arrow, Colors.green, 'Playback resumed');
default:
return (Icons.pause, Colors.orange, 'Playback paused · waiting for readiness');
}
}
String _clock(DateTime at) {
final l = at.toLocal();
String two(int n) => n.toString().padLeft(2, '0');
return '${two(l.hour)}:${two(l.minute)}';
}
@@ -1,67 +0,0 @@
import 'package:flutter/material.dart';
import '../state/session_state.dart';
import '../update/updater.dart';
import '../widgets/window_chrome_bar.dart';
class WelcomeScreen extends StatelessWidget {
const WelcomeScreen({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Column(
children: [
WindowChromeBar(title: 'Watch Together'),
Expanded(child: _WelcomeBody()),
],
),
);
}
}
class _WelcomeBody extends StatelessWidget {
const _WelcomeBody();
@override
Widget build(BuildContext context) {
final session = SessionState.instance;
return Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 420),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Watch Together', style: TextStyle(fontSize: 24)),
Text('v$appVersion', style: const TextStyle(fontSize: 12, color: Colors.grey)),
const SizedBox(height: 8),
const Text(
'Set your name + avatar on the next screen.',
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => session.goConnect(ConnectMode.host),
child: const Text('Host'),
),
const SizedBox(width: 16),
ElevatedButton(
onPressed: () => session.goConnect(ConnectMode.join),
child: const Text('Join'),
),
],
),
const SizedBox(height: 12),
const Text(
'Host opens ports and serves. Clients open nothing.',
textAlign: TextAlign.center,
),
],
),
),
);
}
}
+43
View File
@@ -0,0 +1,43 @@
enum IdleEvent { none, warn, unready }
/// Idle auto-unready clock. Idle time counts from the later of the user's
/// last input and the last moment playback was running: watching is
/// activity, so a long watch followed by a pause starts a fresh 10 min.
/// The unready also always trails its warning by the full grace period.
class IdleTracker {
static const warnAfter = Duration(minutes: 8);
static const unreadyAfter = Duration(minutes: 10);
DateTime _lastActive;
DateTime? _warnedAt;
IdleTracker(DateTime now) : _lastActive = now;
void activity(DateTime now) {
_lastActive = now;
_warnedAt = null;
}
/// Called once per tick. Warn and unready only matter while self is
/// ready; while playing the ready flag gates nothing.
IdleEvent tick(DateTime now, {required bool playing, required bool selfReady}) {
if (playing) {
activity(now);
return IdleEvent.none;
}
if (!selfReady) return IdleEvent.none;
final warnedAt = _warnedAt;
if (warnedAt != null) {
if (now.difference(warnedAt) >= unreadyAfter - warnAfter) {
activity(now);
return IdleEvent.unready;
}
return IdleEvent.none;
}
if (now.difference(_lastActive) >= warnAfter) {
_warnedAt = now;
return IdleEvent.warn;
}
return IdleEvent.none;
}
}
@@ -0,0 +1,96 @@
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:sync_relay_core/core.dart';
import '../files/name_parser.dart';
import '../schedule/anilist.dart';
// Art for media this client swapped in (`art`, see PROTOCOL.md "Media
// art"): Jellyfin images with this install's account, else an AniList
// title search. Everything here fails quietly to "no art".
/// Poster and banner bytes; either may be null.
typedef ArtBytes = ({Uint8List? poster, Uint8List? banner});
/// Series poster (the item's own for movies) and series/item backdrop,
/// else its Banner image, scaled by Jellyfin to fit the caps.
Future<ArtBytes> jellyfinArt(JellyfinClient client, String itemId,
{Future<Uint8List?> Function(Uri url, Map<String, String> headers, int maxBytes) fetch = fetchImage}) async {
final item = await client.item(itemId).timeout(const Duration(seconds: 10));
final seriesPoster = item.seriesId != null && item.seriesPrimaryImageTag != null
? JellyfinImageRef(item.seriesId!, JellyfinImageType.primary, item.seriesPrimaryImageTag!)
: null;
final poster = seriesPoster ?? item.image(JellyfinImageType.primary);
var banner = item.image(JellyfinImageType.backdrop);
if (banner == null) {
var owner = item;
if (item.seriesId != null && item.ownImage(JellyfinImageType.banner) == null) {
try {
owner = await client.item(item.seriesId!).timeout(const Duration(seconds: 10));
} catch (_) {}
}
banner = owner.ownImage(JellyfinImageType.banner);
}
final headers = client.authHeaders;
final (p, b) = await (
poster == null
? Future<Uint8List?>.value()
: fetch(client.imageUrl(poster, maxHeight: 450, quality: 85), headers, RelayConstants.maxPosterBytes),
banner == null
? Future<Uint8List?>.value()
: fetch(client.imageUrl(banner, maxWidth: 1280, quality: 85), headers, RelayConstants.maxBannerBytes),
).wait;
return (poster: p, banner: b);
}
/// The series of release name [name] on AniList: cover and banner; null
/// when the name says nothing or AniList knows no match.
Future<ArtBytes?> aniListArt(AniListClient aniList, String name,
{Future<Uint8List?> Function(Uri url, Map<String, String> headers, int maxBytes) fetch = fetchImage}) async {
final series = parseReleaseName(name).series.trim();
// "1", "2026-10-09": nothing a show could be called.
if (!RegExp(r'\p{L}', unicode: true).hasMatch(series)) return null;
final match = await aniList.searchMedia(series);
if (match == null) return null;
Future<Uint8List?> get(String? url, int maxBytes) async {
final uri = url == null ? null : Uri.tryParse(url);
return uri == null ? null : fetch(uri, const {}, maxBytes);
}
final (cover, banner) = await (
get(match.cover, RelayConstants.maxPosterBytes),
get(match.banner, RelayConstants.maxBannerBytes),
).wait;
final poster = cover ?? (match.coverSmall == match.cover ? null : await get(match.coverSmall, RelayConstants.maxPosterBytes));
return (poster: poster, banner: banner);
}
/// GET [url]: the bytes when 200, at most [maxBytes] and a known image
/// type (see `avatarImageType`); else null.
Future<Uint8List?> fetchImage(Uri url, Map<String, String> headers, int maxBytes) async {
final client = HttpClient()..connectionTimeout = const Duration(seconds: 10);
try {
final req = await client.getUrl(url);
headers.forEach(req.headers.set);
final resp = await req.close().timeout(const Duration(seconds: 15));
if (resp.statusCode != 200 || resp.contentLength > maxBytes) {
await resp.drain<void>().catchError((_) {});
return null;
}
final builder = BytesBuilder(copy: false);
await for (final chunk in resp.timeout(const Duration(seconds: 15))) {
builder.add(chunk);
if (builder.length > maxBytes) return null;
}
final bytes = builder.takeBytes();
return avatarImageType(bytes) == null ? null : bytes;
} catch (e) {
debugPrint('art: fetch failed: $e');
return null;
} finally {
client.close(force: true);
}
}
@@ -0,0 +1,24 @@
/// What the player shows about the current media: a title, an episode line
/// and art. Art is by content hash (`state.media.art`), fetched by everyone
/// (guests included) from the server they dialled; '' when there is none.
class NowPlaying {
/// Series name for episodes, else the movie, file or link name.
final String title;
/// "S2 E4 · Episode title", "E12", or null (movies, links).
final String? episode;
/// Portrait cover (2:3) and wide banner; '' when none.
final String posterHash;
final String bannerHash;
const NowPlaying({required this.title, this.episode, this.posterHash = '', this.bannerHash = ''});
@override
bool operator ==(Object other) =>
other is NowPlaying &&
other.title == title &&
other.episode == episode &&
other.posterHash == posterHash &&
other.bannerHash == bannerHash;
@override
int get hashCode => Object.hash(title, episode, posterHash, bannerHash);
}
@@ -0,0 +1,56 @@
import '../files/name_parser.dart';
import '../jellyfin/session_media.dart';
import 'now_playing.dart';
// Title and episode line of `state.media` (pure; see [NowPlaying]).
// "S1E6" (pickFor) or "S01E02".
final _episodeLabel = RegExp(r'^S(\d{1,3})\s?E(\d{1,4})$', caseSensitive: false);
final _year = RegExp(r'^(?:19|20)\d{2}$');
final _ext = RegExp(r'\.(?=[a-z0-9]*[a-z])[a-z0-9]{2,4}$', caseSensitive: false);
/// What the player shows for [media] (`state.media`); null with nothing up.
NowPlaying? nowPlayingOf(Map<String, Object?>? media) {
if (media == null) return null;
final name = media['name'] is String ? media['name'] as String : '';
final art = media['art'] is Map ? media['art'] as Map : const {};
final poster = art['poster'] is String ? art['poster'] as String : '';
final banner = art['banner'] is String ? art['banner'] as String : '';
final ref = refOfStateMedia(media);
final (title, episode) = ref != null
? jellyfinTitle(ref.title.isNotEmpty ? ref.title : name, ref.subtitle)
: fileTitle(name);
return NowPlaying(title: title, episode: episode, posterHash: poster, bannerHash: banner);
}
/// Jellyfin: [name] is the item's, [subtitle] "Series · S1E6" (or
/// "Series - S01E02") for episodes, the year for movies. Episodes give the
/// series and "S1 E6 · Name"; anything else the name alone.
(String, String?) jellyfinTitle(String name, String? subtitle) {
final sub = subtitle?.trim() ?? '';
if (sub.isEmpty || _year.hasMatch(sub)) return (name, null);
// The label is after the last separator (or the whole subtitle).
final cut = RegExp(r' [·-] ').allMatches(sub).lastOrNull;
final m = _episodeLabel.firstMatch(cut == null ? sub : sub.substring(cut.end).trim());
final series = m == null ? sub : (cut == null ? '' : sub.substring(0, cut.start).trim());
final label = m == null ? null : 'S${int.parse(m.group(1)!)} E${int.parse(m.group(2)!)}';
if (series.isEmpty) return (name, label);
final episode = [?label, if (name.isNotEmpty) name].join(' · ');
return (series, episode.isEmpty ? null : episode);
}
/// Files and links: the release name's series and "S1 E3", "E25" or its
/// extra marker (NCOP1, OVA 2); the name minus extension when it says
/// nothing better.
(String, String?) fileTitle(String name) {
final p = parseReleaseName(name);
final series = p.series.trim();
if (series.isEmpty) return (name.replaceFirst(_ext, ''), null);
final String? episode;
if (p.episode != null) {
episode = p.season != null ? 'S${p.season} E${p.episode}' : 'E${p.episode}';
} else {
episode = p.extra;
}
return (series, episode);
}
File diff suppressed because it is too large. Load diff
+23 -44
View File
@@ -1,20 +1,25 @@
/// The app's sounds. Only events that carry news get one: no sound for
/// what the video itself makes obvious (start, pause, end, seeks).
enum SfxKind {
/// Someone readies up; pitch climbs with [SfxCue.readyIndex].
ready,
unready,
/// Someone marks everyone ready.
forceReady,
forceUnready,
forcePlay,
forceHold,
kick,
join,
leave,
stale,
/// Someone sat you out (played by the session, not from a feed action).
kick,
/// A new video or episode is put up.
swap,
seek,
pause,
resume,
chat,
/// Idle warning: auto-unready soon.
warn,
/// The connection to the server dropped / came back.
lost,
back,
/// Someone pokes you for attention; plays only on your end.
nudge,
}
class SfxCue {
@@ -23,42 +28,16 @@ class SfxCue {
const SfxCue(this.kind, {this.readyIndex = 0});
}
SfxKind? sfxForFeedAction(String action) {
switch (action) {
case 'ready':
return SfxKind.ready;
case 'unready':
return SfxKind.unready;
case 'force-ready':
return SfxKind.forceReady;
case 'force-unready':
return SfxKind.forceUnready;
case 'force-play':
return SfxKind.forcePlay;
case 'force-hold':
return SfxKind.forceHold;
case 'kick':
return SfxKind.kick;
case 'join':
return SfxKind.join;
case 'leave':
return SfxKind.leave;
case 'stale':
return SfxKind.stale;
case 'swap':
return SfxKind.swap;
case 'seek':
return SfxKind.seek;
case 'pause':
return SfxKind.pause;
case 'resume':
return SfxKind.resume;
case 'chat':
return SfxKind.chat;
default:
return null;
}
}
SfxKind? sfxForFeedAction(String action) => switch (action) {
'ready' => SfxKind.ready,
'unready' => SfxKind.unready,
'force-ready' => SfxKind.forceReady,
'join' => SfxKind.join,
'leave' => SfxKind.leave,
'swap' => SfxKind.swap,
'chat' => SfxKind.chat,
_ => null,
};
abstract class SfxService {
Future<void> play(SfxCue cue);
+268
View File
@@ -0,0 +1,268 @@
import 'sfx.dart';
import 'synth.dart';
// The app's sounds come in presets; each action can take its sound from any
// of them. Every preset is quiet, short and on D, so sounds from different
// presets still agree when they overlap.
// - Cosmic: star bells, stardust, bubbles, soft pads, a space tail.
// - Abyss: deep space and its dread. Far beacons, nebula washes, slow
// beating clusters and tritone sags, on D phrygian, with a vast tail.
// - Wood & glass: plucked strings, glass bells, wood knocks, a short room.
enum SoundPreset { cosmic, abyss, woodAndGlass }
/// Shown in Settings: name and feel.
const presetInfo = {
SoundPreset.cosmic: ('Cosmic', 'Star bells, stardust and a soft space tail'),
SoundPreset.abyss: ('Abyss', 'Deep space: far beacons, nebula, a vast dark tail'),
SoundPreset.woodAndGlass: ('Wood & glass', 'Plucked strings, glass bells and wood, close and dry'),
};
/// The preset used when nothing else is chosen.
const defaultPreset = SoundPreset.abyss;
const _d3 = -19, _ab3 = -13;
const _d4 = -7, _eb4 = -6, _ab4 = -1, _a4 = 0, _b4 = 2;
const _d5 = 5, _eb5 = 6, _e5 = 7, _f5 = 8, _fs5 = 9, _g5 = 10, _a5 = 12, _bb5 = 13, _b5 = 14;
const _c6 = 15, _cs6 = 16, _d6 = 17, _e6 = 19, _f6 = 20, _fs6 = 21, _g6 = 22, _a6 = 24, _b6 = 26;
double _p(int s) => semi(s);
/// The ready sound's answer climbs with each person ready.
const _cosmicLadder = [_a5, _b5, _d6, _e6, _fs6, _a6];
const _abyssLadder = [_f5, _g5, _a5, _c6, _d6, _f6];
const _woodLadder = [_g5, _a5, _b5, _d6, _e6, _g6];
SoundSpec readySound(int readyIndex, {SoundPreset preset = defaultPreset}) =>
soundFor(SfxKind.ready, readyIndex: readyIndex, preset: preset);
SoundSpec soundFor(SfxKind kind, {int readyIndex = 1, SoundPreset preset = defaultPreset}) {
final i = (readyIndex - 1).clamp(0, 5);
return switch (preset) {
SoundPreset.cosmic => _cosmic(kind, i),
SoundPreset.abyss => _abyss(kind, i),
SoundPreset.woodAndGlass => _wood(kind, i),
};
}
SoundSpec _cosmic(SfxKind kind, int i) => switch (kind) {
SfxKind.ready => SoundSpec([
Note(0, _p(_d4), 1.0, voice: Voice.pad, gain: 0.3),
Note(0, _p(_d5), 1.2, voice: Voice.star, gain: 0.8, bright: 0.4),
Note(0.09, _p(_cosmicLadder[i]), 1.5, voice: Voice.star, bright: 0.5),
], peakDb: -17, room: 0.3),
// The same bells, dimmed and stepping down.
SfxKind.unready => SoundSpec([
Note(0, _p(_fs5), 0.6, voice: Voice.star, gain: 0.8, bright: 0.15),
Note(0.1, _p(_d5), 0.8, voice: Voice.star, bright: 0.1),
], cutoff: 3500, peakDb: -20, room: 0.25),
// Just the bell, left hanging in space: playback is the rest of the cue.
SfxKind.forceReady => SoundSpec([
Note(0, _p(_d6), 1.8, voice: Voice.star, bright: 0.7),
], cutoff: 7000, peakDb: -18, room: 0.35, tail: 1.6),
// A star arriving: dust rising into a bell.
SfxKind.join => SoundSpec([
Note(0, _p(_d6), 1.0, voice: Voice.dust, to: _p(_a6), gain: 0.5),
Note(0.15, _p(_a5), 1.4, voice: Voice.star, bright: 0.35, gain: 0.6),
], cutoff: 7000, peakDb: -19, room: 0.3),
// Dust drifting down, a low bell going out.
SfxKind.leave => SoundSpec([
Note(0, _p(_a6), 0.9, voice: Voice.dust, to: _p(_d6), gain: 0.5),
Note(0.1, _p(_d5), 1.3, voice: Voice.star, bright: 0.2, gain: 0.5),
], cutoff: 5000, peakDb: -22, room: 0.3),
// Falling into the dark: a deep tone sinking an octave under a sagging pad.
SfxKind.kick => SoundSpec([
Note(0, _p(_d4), 1.0, voice: Voice.sub, to: _p(_d3)),
Note(0, _p(_a4), 0.9, voice: Voice.pad, to: _p(_d4), gain: 0.4),
], cutoff: 3000, peakDb: -19, room: 0.25),
// Something new opens up: a rush of air, then a wide chord blooms.
SfxKind.swap => SoundSpec([
Note(0, 300, 0.6, voice: Voice.slide, to: 2400, gain: 0.35),
Note(0.35, _p(_d4), 1.4, voice: Voice.pad, gain: 0.5, bright: 0.3),
Note(0.35, _p(_a4), 1.4, voice: Voice.pad, gain: 0.5, bright: 0.3),
Note(0.35, _p(_e5), 1.4, voice: Voice.pad, gain: 0.4, bright: 0.3),
Note(0.38, _p(_fs6), 1.2, voice: Voice.star, bright: 0.3, gain: 0.3),
], peakDb: -18, room: 0.3),
// Two bubbles rising: bloop-bloop, the second higher.
SfxKind.chat => SoundSpec([
Note(0, _p(_a5), 0.18, voice: Voice.drop, gain: 0.8),
Note(0.08, _p(_e6), 0.22, voice: Voice.drop),
], peakDb: -19, room: 0.15, tail: 1.0),
// Something huge drifting past: a slow, deep swell.
SfxKind.warn => SoundSpec([
Note(0, _p(_d4), 2.0, voice: Voice.pad),
Note(0, _p(_a4), 2.0, voice: Voice.pad, gain: 0.5),
Note(0.3, _p(_d3), 1.7, voice: Voice.sub, gain: 0.4),
], cutoff: 3000, peakDb: -20, room: 0.2, tail: 1.0),
// The signal fading: a chord sagging down and dimming.
SfxKind.lost => SoundSpec([
Note(0, _p(_a4), 1.2, voice: Voice.pad, to: _p(_d4), gain: 0.7),
Note(0, _p(_e5), 1.2, voice: Voice.pad, to: _p(_a4), gain: 0.4),
], cutoff: 2500, peakDb: -19, room: 0.3, tail: 1.2),
// Re-acquired: a bell gliding up into tune, landing with dust.
SfxKind.back => SoundSpec([
Note(0, _p(_a4), 0.6, voice: Voice.star, to: _p(_d5), bright: 0.3, gain: 0.5),
Note(0.22, _p(_a5), 1.2, voice: Voice.star, bright: 0.5),
Note(0.25, _p(_d6), 0.6, voice: Voice.dust, to: _p(_a6), gain: 0.3),
], peakDb: -18, room: 0.25),
// A poke: two quick bright bubbles flicking up, a glint on the second.
// High and dry so it cuts through a video and stays a patter when spammed.
SfxKind.nudge => SoundSpec([
Note(0, _p(_d6), 0.12, voice: Voice.drop, to: _p(_fs6), gain: 0.8),
Note(0.09, _p(_a6), 0.16, voice: Voice.drop),
Note(0.09, _p(_cs6) * 2, 0.3, voice: Voice.star, bright: 0.6, gain: 0.2),
], cutoff: 7000, peakDb: -18, room: 0.05, tail: 0.6),
};
SoundSpec _abyss(SfxKind kind, int i) => switch (kind) {
// A beacon pinging far out, the void humming under it.
SfxKind.ready => SoundSpec([
Note(0, _p(_d3), 1.2, voice: Voice.pad, gain: 0.35),
Note(0, _p(_d5), 1.4, voice: Voice.star, gain: 0.8, bright: 0.25),
Note(0.12, _p(_abyssLadder[i]), 1.6, voice: Voice.star, bright: 0.3),
], cutoff: 5000, peakDb: -18, room: 0.4, tail: 1.8),
// The beacon answering itself a tritone down, dimmer.
SfxKind.unready => SoundSpec([
Note(0, _p(_a4), 0.7, voice: Voice.star, gain: 0.8, bright: 0.1),
Note(0.12, _p(_eb4), 0.9, voice: Voice.star, bright: 0.1),
], cutoff: 2500, peakDb: -21, room: 0.35, tail: 1.6),
// One bell, huge and very far away.
SfxKind.forceReady => SoundSpec([
Note(0, _p(_d5), 1.7, voice: Voice.star, bright: 0.5),
], cutoff: 5000, peakDb: -18, room: 0.45, tail: 1.8),
// Something emerging from the nebula: a dark wash brightening into a bell.
SfxKind.join => SoundSpec([
Note(0, 150, 1.2, voice: Voice.slide, to: 900, gain: 0.4),
Note(0.05, _p(_d4), 1.2, voice: Voice.pad, gain: 0.3),
Note(0.3, _p(_a5), 1.3, voice: Voice.star, bright: 0.3, gain: 0.5),
], cutoff: 4500, peakDb: -19, room: 0.4, tail: 1.6),
// Swallowed back into it: the wash darkening over a fading bell.
SfxKind.leave => SoundSpec([
Note(0, 900, 1.1, voice: Voice.slide, to: 150, gain: 0.4),
Note(0.1, _p(_d5), 1.2, voice: Voice.star, bright: 0.15, gain: 0.4),
], cutoff: 3500, peakDb: -22, room: 0.4, tail: 1.6),
// Pulled under: a deep fall with a tritone sagging over it.
SfxKind.kick => SoundSpec([
Note(0, _p(_d4), 1.3, voice: Voice.sub, to: _p(_d3)),
Note(0, _p(_ab4), 1.2, voice: Voice.pad, to: _p(_d4), gain: 0.35),
], cutoff: 2000, peakDb: -19, room: 0.35, tail: 1.6),
// A door into the dark: low air, then an unresolved chord opens.
SfxKind.swap => SoundSpec([
Note(0, 120, 0.8, voice: Voice.slide, to: 1200, gain: 0.35),
Note(0.45, _p(_d4), 1.3, voice: Voice.pad, gain: 0.5, bright: 0.2),
Note(0.45, _p(_a4), 1.3, voice: Voice.pad, gain: 0.45, bright: 0.2),
Note(0.45, _p(_eb5), 1.3, voice: Voice.pad, gain: 0.3, bright: 0.2),
Note(0.5, _p(_bb5), 1.1, voice: Voice.star, bright: 0.2, gain: 0.25),
], cutoff: 4000, peakDb: -19, room: 0.35, tail: 1.4),
// Bubbles rising from deep water.
SfxKind.chat => SoundSpec([
Note(0, _p(_d5), 0.25, voice: Voice.drop, gain: 0.8),
Note(0.1, _p(_a5), 0.3, voice: Voice.drop),
], cutoff: 4500, peakDb: -19, room: 0.3, tail: 1.4),
// Something vast drawing near: a slow swell of two notes rubbing.
SfxKind.warn => SoundSpec([
Note(0, _p(_d4), 2.0, voice: Voice.pad),
Note(0, _p(_eb4), 2.0, voice: Voice.pad, gain: 0.35),
Note(0.3, _p(_d3), 1.7, voice: Voice.sub, gain: 0.45),
], cutoff: 2200, peakDb: -20, room: 0.25, tail: 1.0),
// The signal drowning: a chord sagging by a tritone into static.
SfxKind.lost => SoundSpec([
Note(0, _p(_a4), 1.3, voice: Voice.pad, to: _p(_eb4), gain: 0.7),
Note(0, _p(_d4), 1.3, voice: Voice.pad, to: _p(_ab3), gain: 0.5),
Note(0.2, 600, 1.0, voice: Voice.slide, to: 200, gain: 0.2),
], cutoff: 2000, peakDb: -19, room: 0.35, tail: 1.4),
// Found again: a bell climbing out of the tritone into tune.
SfxKind.back => SoundSpec([
Note(0, _p(_ab4), 0.6, voice: Voice.star, to: _p(_d5), bright: 0.25, gain: 0.5),
Note(0.25, _p(_a5), 1.3, voice: Voice.star, bright: 0.4),
Note(0.28, _p(_d5), 0.6, voice: Voice.dust, to: _p(_a5), gain: 0.25),
], cutoff: 5000, peakDb: -18, room: 0.35, tail: 1.6),
// A poke from the dark: two quick bubbles flicking up, a pale glint.
// Still high and fairly dry so it cuts through a video when spammed.
SfxKind.nudge => SoundSpec([
Note(0, _p(_a5), 0.13, voice: Voice.drop, to: _p(_c6), gain: 0.8),
Note(0.1, _p(_d6), 0.18, voice: Voice.drop),
Note(0.1, _p(_a6), 0.35, voice: Voice.star, bright: 0.5, gain: 0.2),
], cutoff: 6000, peakDb: -18, room: 0.1, tail: 1.0),
};
SoundSpec _wood(SfxKind kind, int i) => switch (kind) {
SfxKind.ready => SoundSpec([
Note(0, _p(_d5), 0.9, gain: 0.75, bright: 0.55),
Note(0.085, _p(_woodLadder[i]), 1.1, bright: 0.65),
], peakDb: -17, room: 0.14, tail: 0.5),
// The same strings, muted and stepping down.
SfxKind.unready => SoundSpec([
Note(0, _p(_a5), 0.35, gain: 0.8, bright: 0.2),
Note(0.09, _p(_g5), 0.45, bright: 0.15),
], cutoff: 3800, peakDb: -20, room: 0.1, tail: 0.5),
// Just the bell: playback starting is the rest of the cue.
SfxKind.forceReady => SoundSpec([
Note(0, _p(_d6) * 2, 1.6, voice: Voice.chime),
], cutoff: 7000, peakDb: -18, room: 0.18, tail: 0.5),
// Wind through glass: three uneven tinkles.
SfxKind.join => SoundSpec([
Note(0, _p(_a6), 1.3, voice: Voice.chime, gain: 0.8),
Note(0.11, _p(_d6) * 2, 1.2, voice: Voice.chime, gain: 0.55),
Note(0.26, _p(_e6) * 2, 1.4, voice: Voice.chime, gain: 0.45),
], cutoff: 7000, peakDb: -19, room: 0.18, tail: 0.5),
// The wind settling: two softer, lower tinkles.
SfxKind.leave => SoundSpec([
Note(0, _p(_b6), 1.0, voice: Voice.chime, gain: 0.6),
Note(0.17, _p(_e6), 1.3, voice: Voice.chime, gain: 0.5),
], cutoff: 5000, peakDb: -22, room: 0.18, tail: 0.5),
// Two hardwood clacks, then a low muted string.
SfxKind.kick => SoundSpec([
Note(0, 1900, 0.09, voice: Voice.clack),
Note(0.16, 1750, 0.11, voice: Voice.clack, gain: 0.9),
Note(0.3, _p(_d4), 0.7, bright: 0.15, gain: 0.7),
], cutoff: 5000, peakDb: -19, room: 0.12, tail: 0.5),
// The door slides open on something new: air, then a fifth.
SfxKind.swap => SoundSpec([
Note(0, 500, 0.42, voice: Voice.slide, to: 1700, gain: 0.5),
Note(0.33, _p(_d5), 1.1, bright: 0.6, gain: 0.8),
Note(0.36, _p(_a5), 1.1, bright: 0.6, gain: 0.7),
], peakDb: -18, tail: 0.5),
// A hollow wooden tok-tok, the second higher.
SfxKind.chat => SoundSpec([
Note(0, _p(_g5), 0.13, voice: Voice.bamboo, gain: 0.85),
Note(0.075, _p(_d6), 0.16, voice: Voice.bamboo),
], cutoff: 5500, peakDb: -19, room: 0.1, tail: 0.5),
// A bowl: one soft strike that hums.
SfxKind.warn => SoundSpec([
Note(0, _p(_d5), 2.6, voice: Voice.rin),
], cutoff: 4500, peakDb: -20, room: 0.1, tail: 0.5),
// A string going slack: plucked, its pitch sagging away.
SfxKind.lost => SoundSpec([
Note(0, _p(_a5), 1.1, to: _p(_d5), bright: 0.4),
Note(0.05, _p(_d5), 1.0, to: _p(_a4), bright: 0.3, gain: 0.6),
], cutoff: 3500, peakDb: -19, tail: 0.5),
// Re-tuned: a quick bend up that lands, and a tinkle.
SfxKind.back => SoundSpec([
Note(0, _p(_b4), 0.9, to: _p(_d5), bright: 0.5),
Note(0.2, _p(_a5), 1.0, bright: 0.6, gain: 0.7),
Note(0.3, _p(_d6) * 2, 1.0, voice: Voice.chime, gain: 0.2),
], peakDb: -18, tail: 0.5),
// Two quick wooden taps, the second flicking up, with a glint.
SfxKind.nudge => SoundSpec([
Note(0, _p(_a5), 0.12, voice: Voice.bamboo, gain: 0.8),
Note(0.09, _p(_e6), 0.16, voice: Voice.bamboo),
Note(0.09, _p(_b6), 0.35, voice: Voice.chime, gain: 0.3),
], cutoff: 6500, peakDb: -18, room: 0.04, tail: 0.5),
};
/// Shown in Settings → Notifications & sounds: name and when it plays.
const soundInfo = {
SfxKind.ready: ('Ready', 'Someone presses Space; climbs with each ready person'),
SfxKind.unready: ('Not ready', 'Someone takes their ready back'),
SfxKind.forceReady: ('Everyone ready', 'Someone marks everyone ready'),
SfxKind.join: ('Joined', 'Someone joins the server'),
SfxKind.leave: ('Left', 'Someone leaves'),
SfxKind.kick: ('Sat out', 'Someone sits you out'),
SfxKind.swap: ('New video', 'A different video or episode is put up'),
SfxKind.chat: ('Message', 'A chat message from someone else'),
SfxKind.warn: ('Idle warning', 'Auto-unready soon unless you press Space'),
SfxKind.lost: ('Connection lost', 'The server stopped answering; reconnecting'),
SfxKind.back: ('Reconnected', 'Back in the server after a drop'),
SfxKind.nudge: ('Nudge', 'Someone nudges you; always plays (volume still applies)'),
};
Loaded 100 of 298 files, more files were not shown because too many files have changed in this diff. Show more