Compare commits
113
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ae07b93ec | ||
|
|
067395a362 | ||
|
|
30155e1c5f | ||
|
|
72cb3c376e | ||
|
|
f1d2c2f2eb | ||
|
|
6ba8728dce | ||
|
|
5107c4b0fc | ||
|
|
aa8f16f21b | ||
|
|
c809127996 | ||
|
|
d4e1cb2fbf | ||
|
|
63c3df11b2 | ||
|
|
89ebe21201 | ||
|
|
766cea2199 | ||
|
|
9a1f44da46 | ||
|
|
d2f7f84cbf | ||
|
|
7d77700c7b | ||
|
|
9858e44710 | ||
|
|
b3e3f62c54 | ||
|
|
f982d391b9 | ||
|
|
b131932a4e | ||
|
|
3ae8ffad64 | ||
|
|
5df4a1aaa3 | ||
|
|
4fd690a127 | ||
|
|
ac4cf32746 | ||
|
|
8b3d80a0a6 | ||
|
|
c85fab4459 | ||
|
|
32019f2e20 | ||
|
|
02247a6287 | ||
|
|
9c7dec32a8 | ||
|
|
57edfaa60b | ||
|
|
891d3dfedc | ||
|
|
4439277c86 | ||
|
|
4c48ef8b19 | ||
|
|
fb97980775 | ||
|
|
243b7c8b90 | ||
|
|
48043dbae2 | ||
|
|
aac8438764 | ||
|
|
af1ca4370c | ||
|
|
5467fba0eb | ||
|
|
f1164db2c5 | ||
|
|
549f9c5eed | ||
|
|
434371e9cf | ||
|
|
769fe01c84 | ||
|
|
0782545013 | ||
|
|
590e069366 | ||
|
|
2b43b9ef12 | ||
|
|
8c847c59b8 | ||
|
|
36d171e51b | ||
|
|
1da91e40a1 | ||
|
|
d5a8d3ad88 | ||
|
|
43ab18854c | ||
|
|
12b27c77c3 | ||
|
|
36cb1324fd | ||
|
|
93fb0fd728 | ||
|
|
845fa349e4 | ||
|
|
b9374baaf1 | ||
|
|
bd3732fba5 | ||
|
|
4bc4b075ec | ||
|
|
e528d04b43 | ||
|
|
d5d47473a2 | ||
|
|
a2ca7e8061 | ||
|
|
9775041c30 | ||
|
|
05d95f51cd | ||
|
|
ad0c74f880 | ||
|
|
06b6490ac9 | ||
|
|
bd5cd8a4b1 | ||
|
|
9a8ecbdceb | ||
|
|
48c2809041 | ||
|
|
19f08e6293 | ||
|
|
5e2ed3d12e | ||
|
|
abc392d30b | ||
|
|
a0b84dfc37 | ||
|
|
4fb68f4bbc | ||
|
|
613f203cf9 | ||
|
|
64f19aeef2 | ||
|
|
f2d052dbf6 | ||
|
|
9777eb0952 | ||
|
|
4b31946635 | ||
|
|
e30962245e | ||
|
|
5fed3360c2 | ||
|
|
0e183cf560 | ||
|
|
226136b4e7 | ||
|
|
5e5849f985 | ||
|
|
594159c0ae | ||
|
|
f46d6596c5 | ||
|
|
dd2dd497f2 | ||
|
|
4c47a5f8a0 | ||
|
|
56c1167382 | ||
|
|
63f0768507 | ||
|
|
12b519762b | ||
|
|
f41db66ac6 | ||
|
|
32c0054c44 | ||
|
|
e1bb687df4 | ||
|
|
78d26effbf | ||
|
|
d2f2eb62be | ||
|
|
598a4f5c75 | ||
|
|
52e167ea59 | ||
|
|
0dffd59440 | ||
|
|
574826dc03 | ||
|
|
add8aa831c | ||
|
|
0c623bbcc0 | ||
|
|
fdd9e6cab2 | ||
|
|
b1128bcce2 | ||
|
|
80c304d86b | ||
|
|
4a4df7a73c | ||
|
|
2675e6d82b | ||
|
|
666da9c5d3 | ||
|
|
b6b2a9c25a | ||
|
|
1ed180c739 | ||
|
|
f12042ad24 | ||
|
|
a265636139 | ||
|
|
70914400b4 | ||
|
|
8d6cf2f5a1 |
+167
-10
@@ -5,28 +5,185 @@ MEMBY_EMBY_URL=https://molise.bounceme.net
|
||||
|
||||
# What TVs are told to stream from. Only set this when it differs from the address
|
||||
# above — video goes device -> Emby directly, never through the gateway.
|
||||
#MEMBY_EMBY_PUBLIC_URL=https://molise.bounceme.net
|
||||
MEMBY_EMBY_PUBLIC_URL=https://molise.bounceme.net
|
||||
|
||||
# Where the gateway reads media *bytes* from, which is a different question from the two
|
||||
# addresses above and matters for exactly one subsystem.
|
||||
#
|
||||
# Credits detection is the only server-side thing that opens a media file, and it does so as
|
||||
# ranged reads over the last few minutes of it. The gateway runs on the NAS (10.0.0.213) and
|
||||
# the media and Emby live on the HTPC (10.0.0.2), so with this unset every one of those
|
||||
# ranged reads takes MEMBY_EMBY_URL: out to the DDNS name, through TLS and the reverse proxy,
|
||||
# and back in — and a proxy that buffers turns the ranged read into a whole-file read, which
|
||||
# is the one thing the scan design exists to avoid.
|
||||
#
|
||||
# Unset, it falls back to MEMBY_EMBY_URL and behaves exactly as before. Confirm the port your
|
||||
# Emby listens on over plain HTTP on the LAN before uncommenting.
|
||||
MEMBY_EMBY_MEDIA_URL=http://10.0.0.2:8096
|
||||
|
||||
# Postgres password for the memby role. Generate one, e.g.
|
||||
# openssl rand -base64 24
|
||||
POSTGRES_PASSWORD=change-me
|
||||
POSTGRES_PASSWORD=7dfc3eb07108013c7bea9787457397551b5eb9059adac51aea4741f4ec649be2
|
||||
|
||||
# Host port the gateway listens on.
|
||||
MEMBY_PORT=8080
|
||||
# Fixed NAS host port used by the mserver.sublogue.com reverse proxy.
|
||||
MEMBY_PORT=32768
|
||||
|
||||
# INFO is recommended. DEBUG adds diagnostic request and playback detail; TRACE adds the
|
||||
# deepest request and playback-negotiation trace. Sensitive structured values are redacted.
|
||||
MEMBY_LOG_LEVEL=INFO
|
||||
MEMBY_LOG_BUFFER_CAPACITY=5000
|
||||
# The Compose default is a named-volume-backed JSONL archive restored by the admin log.
|
||||
MEMBY_LOG_HISTORY_PATH=/data/logs/events.jsonl
|
||||
# console (default) is one aligned line per event, timestamp first and no `time=` key.
|
||||
# logfmt restores slog's own key=value line; json is for a log collector.
|
||||
MEMBY_LOG_FORMAT=console
|
||||
MEMBY_GOMEMLIMIT=384MiB
|
||||
MEMBY_SERVER_MEMORY_LIMIT=512m
|
||||
|
||||
# Used for local Sonarr air times and Radarr digital-release day boundaries.
|
||||
MEMBY_TIMEZONE=Pacific/Auckland
|
||||
|
||||
# How long a cached home payload stays warm.
|
||||
MEMBY_HOME_TTL=60s
|
||||
MEMBY_RECOMMEND_TTL=24h
|
||||
# Optional JSON overlay on the explainable weighted-ranking defaults.
|
||||
# MEMBY_RECOMMENDATION_WEIGHTS={"ExplorationRate":0.08,"MinimumEvidence":2,"ImpressionPenalty":0.12}
|
||||
MEMBY_RECOMMENDATION_WEIGHTS=
|
||||
|
||||
# Admin interface at http://<host>:8080/admin/ — library imports, the maintenance
|
||||
# switch, and row analytics. Leave blank to disable /admin entirely. Generate with
|
||||
# Optional complete Remote Config document. Leave blank for bundled-equivalent defaults.
|
||||
# Every change must increase configVersion; see server/REMOTE_CONFIG.md.
|
||||
MEMBY_REMOTE_CONFIG_JSON=
|
||||
|
||||
# Maximum number of distinct Memby TVs one Emby user may keep signed in.
|
||||
|
||||
# Admin interface at https://mserver.sublogue.com/admin/ — library imports, the
|
||||
# maintenance switch, and row analytics. Leave blank to disable /admin entirely. Generate with
|
||||
# openssl rand -hex 32
|
||||
MEMBY_ADMIN_TOKEN=
|
||||
MEMBY_ADMIN_TOKEN=4fad67d508558efee5cc5ae05694105421d4c79d35ee2333a817b3791235cd8c
|
||||
|
||||
# Library import. Hourly incremental keeps up with episodes added through the day.
|
||||
# Public gateway address. Release publishing uses the file-backed secret in
|
||||
# MEMBY_SECRETS_DIR, never a value in this environment file.
|
||||
MEMBY_PUBLIC_URL=https://mserver.sublogue.com
|
||||
MEMBY_SECRETS_DIR=/share/Docker/Memby-secrets
|
||||
|
||||
# Catalogue sweep. With both *arr webhooks below configured this is reconciliation for
|
||||
# media Sonarr and Radarr do not manage — a file dropped in by hand, a title edited in
|
||||
# Emby, a notification that never arrived — so it can be lengthened to 6h. Without them
|
||||
# it is the only way a new title is ever found and must stay frequent. The console can
|
||||
# override it at Settings > Catalogue sweep without a redeployment.
|
||||
MEMBY_SYNC_INTERVAL=1h
|
||||
MEMBY_SYNC_ON_START=false
|
||||
|
||||
# Optional Emby service account for imports. Without it the gateway borrows the most
|
||||
# recently active TV session, which works but stops if that user is removed.
|
||||
#MEMBY_SYNC_USER_ID=
|
||||
#MEMBY_SYNC_API_KEY=
|
||||
MEMBY_SYNC_USER_ID=23bf90fc1fe049ce898db7adda1ed7bf
|
||||
MEMBY_SYNC_API_KEY=56775917938841e7ac1b6a233d4d5075
|
||||
|
||||
# Optional Sonarr calendar integration. Use the URL reachable from this container,
|
||||
# not necessarily the address entered in a browser. The API key is in Sonarr under
|
||||
# Settings > General > Security.
|
||||
MEMBY_SONARR_URL=http://10.0.0.2:8989
|
||||
MEMBY_SONARR_API_KEY=6b608b051ee24582925773bd5dfbe37a
|
||||
MEMBY_SONARR_TTL=5m
|
||||
# How long an episode banner keeps being offered — both the "aired, coming soon" one and
|
||||
# the "new episode added" one a finished scan produces. 0 turns both off and leaves the
|
||||
# airing-today row alone.
|
||||
MEMBY_SONARR_ALERT_WINDOW=3h
|
||||
# Shared secret for Sonarr's webhook, which is how the catalogue learns that an episode
|
||||
# landed rather than waiting for the next sweep. In Sonarr: Settings > Connect > + >
|
||||
# Webhook, with On Import, On Upgrade, On Rename, On Episode File Delete and On Series
|
||||
# Delete ticked, URL https://<public gateway>/hooks/sonarr?token=<this value>. Empty
|
||||
# makes the hook 404.
|
||||
MEMBY_SONARR_WEBHOOK_TOKEN=09f091f0f2ee8af3cba1aac29557e344
|
||||
# How long after a webhook the gateway first looks for the file in Emby. Sonarr and
|
||||
# Radarr fire the moment they have moved the file into place and Emby has not scanned it
|
||||
# yet, so asking immediately spends a request to learn nothing.
|
||||
MEMBY_ARR_INGEST_SETTLE=1m
|
||||
|
||||
# Optional Radarr calendar integration. Upcoming movies cover the coming month, ordered by
|
||||
# Radarr's digital release date. A film with no digital date yet is estimated at its cinema
|
||||
# date plus 30 days and says so on the card; physical dates are ignored.
|
||||
MEMBY_RADARR_URL=http://10.0.0.2:7878
|
||||
MEMBY_RADARR_API_KEY=d393acb157a44dc2b0e2aede96278ad5
|
||||
MEMBY_RADARR_TTL=5m
|
||||
# Shared secret for Radarr's webhook, which is how the catalogue learns that a film landed
|
||||
# rather than waiting for the next sweep. In Radarr: Settings > Connect > + > Webhook, with
|
||||
# On Import, On Upgrade, On Rename, On Movie File Delete and On Movie Delete ticked, URL
|
||||
# https://<public gateway>/hooks/radarr?token=<this value>. Empty makes the hook 404.
|
||||
# The catalogue reads all five; the banner is announced once the film has actually scanned
|
||||
# in, and only for an import — an upgrade replaced a film that was already there.
|
||||
MEMBY_RADARR_WEBHOOK_TOKEN=bfa059594adeadf9105c27481a5fd758
|
||||
# How long a newly scanned film keeps being announced, so a TV switched on shortly after
|
||||
# the import still hears about it. 0 turns the banners off.
|
||||
MEMBY_RADARR_ALERT_WINDOW=3h
|
||||
|
||||
# Optional Bazarr integration, one of the two providers a viewer can fetch a missing
|
||||
# subtitle from. Bazarr writes the file beside the media file, so Emby serves the result
|
||||
# and Memby stores nothing — leaving these unset simply means Bazarr is never offered. The
|
||||
# API key is in Bazarr under Settings > General.
|
||||
#
|
||||
# Whether viewers may actually use it is a switch on the admin console's Subtitles page,
|
||||
# not an environment variable: the address is deployment configuration and belongs here,
|
||||
# but turning the provider on and off is an operator's decision that should not need a
|
||||
# redeployment. The second provider, OpenSubtitles, is configured entirely on that page —
|
||||
# it needs only an API key, and the credentials live in the database rather than in this
|
||||
# file.
|
||||
#MEMBY_BAZARR_URL=http://10.0.0.2:6767
|
||||
#MEMBY_BAZARR_API_KEY=e079ab79c1e32b5cf648d079f4421e11
|
||||
# How long Bazarr's movie/series/episode listings are cached. They exist only to turn an
|
||||
# Emby item into the Radarr/Sonarr id Bazarr keys on.
|
||||
#MEMBY_BAZARR_TTL=5m
|
||||
# A manual search queries live subtitle providers and is legitimately slow, so it gets a
|
||||
# longer timeout than the rest of the upstreams. Too short reads to a viewer as "no
|
||||
# subtitles found" rather than as a timeout.
|
||||
MEMBY_BAZARR_TIMEOUT=45s
|
||||
|
||||
# How often the gateway checks that Emby is answering. A run of failures raises the
|
||||
# "server not responding" banner on every TV, including mid-playback, and recovery
|
||||
# raises the matching "back online" one. 0 turns the probe and both banners off.
|
||||
MEMBY_EMBY_HEALTH_INTERVAL=60s
|
||||
|
||||
# How slow a request has to be before its log line carries a breakdown of where the time
|
||||
# went: Emby, Postgres, Redis, each *arr, and the gateway's own row assembly and encoding.
|
||||
# Fast requests carry none deliberately — a breakdown on every /v1/status poll would bury
|
||||
# the one it exists to reveal. Lower it while chasing one slow screen. The gateway's
|
||||
# settings page overrides it without a redeployment.
|
||||
MEMBY_SLOW_REQUEST_THRESHOLD=500ms
|
||||
|
||||
# Optional Tracearr-powered For You signals. Create a read-only public API key in
|
||||
# Tracearr Settings. Server ID is optional unless Tracearr monitors multiple servers.
|
||||
MEMBY_TRACEARR_URL=https://tracearr.sublogue.com/
|
||||
MEMBY_TRACEARR_API_KEY=trr_pub_WKSdiZFGZ_10d-zgQ2Wx0H4Ym4NAiIeLBSvf8a6rvZ0
|
||||
MEMBY_TRACEARR_SERVER_ID=6964b9ed-3a21-4f51-b94b-cd344ec42c1b
|
||||
MEMBY_TRACEARR_SYNC_INTERVAL=5m
|
||||
MEMBY_TRACEARR_FULL_INTERVAL=24h
|
||||
|
||||
# Credits detection: where an episode's closing credits begin, discovered for the few
|
||||
# episodes the household is actually about to watch. Tracearr is what says which those are,
|
||||
# so this never scans the library — it prepares a sliding window a few episodes ahead of
|
||||
# each viewer and stops. An accepted marker is scanned once per media version; inconclusive
|
||||
# attempts observe the retry delay configured in the admin console.
|
||||
#
|
||||
# It is the only thing in the gateway that reads media bytes, which is why it is off by
|
||||
# default. With ffmpeg absent it still runs, writing markers from where viewers actually
|
||||
# stopped — which on a well-watched show is the better signal anyway.
|
||||
#
|
||||
# Where those bytes are read FROM is MEMBY_EMBY_MEDIA_URL, near the top of this file beside
|
||||
# the other Emby addresses. It is the setting that decides whether a scan crosses the LAN to
|
||||
# the HTPC or goes out to the DDNS name and back through the reverse proxy, so it is worth
|
||||
# setting before turning this on.
|
||||
MEMBY_CREDITS_ENABLED=true
|
||||
# Leave blank to find ffmpeg on the path, which is where the image puts it.
|
||||
MEMBY_CREDITS_FFMPEG=
|
||||
# Look-ahead for an ordinary viewer. A binge expands it, a slow viewer contracts it, and
|
||||
# MAX_PREFETCH is the ceiling nothing exceeds however fast somebody watches.
|
||||
MEMBY_CREDITS_PREFETCH_EPISODES=3
|
||||
MEMBY_CREDITS_MAX_PREFETCH=5
|
||||
# Pending candidates. Past this, low-priority speculation is discarded rather than queued:
|
||||
# a backlog of episodes nobody reached has stopped describing demand. These three values
|
||||
# are first-run defaults; Admin Console → Credits detection can override them durably.
|
||||
MEMBY_CREDITS_QUEUE_LIMIT=20
|
||||
|
||||
MEMBY_FOR_YOU_MIN_REBUILD_AGE=24h
|
||||
MEMBY_FOR_YOU_REFRESH_INTERVAL=24h
|
||||
MEMBY_FOR_YOU_REBUILD_HOUR=4
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
name: Publish Memby release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out source
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Java 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: "17"
|
||||
|
||||
- name: Set up Android SDK
|
||||
uses: android-actions/setup-android@v3
|
||||
|
||||
- name: Install Android 35 build tools
|
||||
shell: bash
|
||||
run: sdkmanager "platforms;android-35" "build-tools;35.0.0"
|
||||
|
||||
- name: Read version from tag
|
||||
shell: bash
|
||||
run: |
|
||||
VERSION="${GITEA_REF_NAME#v}"
|
||||
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "Tag must look like v0.1.54" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "VERSION=$VERSION" >> "$GITEA_ENV"
|
||||
|
||||
- name: Restore release keystore
|
||||
shell: bash
|
||||
env:
|
||||
KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
|
||||
run: |
|
||||
printf '%s' "$KEYSTORE_BASE64" | base64 --decode > "$GITEA_WORKSPACE/memby-release.jks"
|
||||
|
||||
- name: Test and build signed APK
|
||||
shell: bash
|
||||
env:
|
||||
MEMBY_KEYSTORE: ${{ gitea.workspace }}/memby-release.jks
|
||||
MEMBY_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
|
||||
MEMBY_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
|
||||
MEMBY_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
||||
run: |
|
||||
chmod +x ./gradlew
|
||||
./gradlew --console=plain testDebugUnitTest assembleRelease \
|
||||
-Pmemby.versionName="$VERSION"
|
||||
|
||||
- name: Verify signed APK
|
||||
shell: bash
|
||||
run: |
|
||||
"$ANDROID_HOME/build-tools/35.0.0/apksigner" verify --verbose --print-certs \
|
||||
app/build/outputs/apk/release/app-release.apk
|
||||
echo "APK_SHA256=$(sha256sum app/build/outputs/apk/release/app-release.apk | awk '{print $1}')" \
|
||||
>> "$GITEA_ENV"
|
||||
|
||||
- name: Publish to Memby gateway
|
||||
shell: bash
|
||||
env:
|
||||
RELEASE_TOKEN: ${{ secrets.MEMBY_RELEASE_PUBLISH_TOKEN }}
|
||||
run: |
|
||||
NOTES="$(git log -1 --pretty=%B)"
|
||||
curl --fail-with-body --show-error --silent \
|
||||
-X POST \
|
||||
-H "Authorization: Bearer $RELEASE_TOKEN" \
|
||||
-F "version=$VERSION" \
|
||||
-F "sha256=$APK_SHA256" \
|
||||
-F "notes=$NOTES" \
|
||||
-F "apk=@app/build/outputs/apk/release/app-release.apk;type=application/vnd.android.package-archive" \
|
||||
https://mserver.sublogue.com/admin/api/release
|
||||
+16
@@ -18,9 +18,25 @@ local.properties
|
||||
# Holds the Postgres password, admin token and Emby address.
|
||||
.env
|
||||
/server/bin/
|
||||
/server/.tmp-go-cache/
|
||||
|
||||
# Release artefacts and signing material. The keystore must never be committed:
|
||||
# whoever holds it can publish an update that installs over Memby.
|
||||
/dist/out/
|
||||
*.apk
|
||||
*.jks
|
||||
*.keystore
|
||||
*.p12
|
||||
*.pfx
|
||||
*.password
|
||||
*.secret
|
||||
/secrets/
|
||||
|
||||
# Local environment variants may contain host paths or release settings. The
|
||||
# checked-in example contains names/placeholders only.
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# The installed APK a deploy script pulls off a television to compare against.
|
||||
/.tmp-memby-installed-base.apk
|
||||
.tmp-go-cache
|
||||
@@ -0,0 +1 @@
|
||||
e76e2be01f9477bf3dae4d4f5582546f
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,15 @@
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import org.gradle.wrapper.PathAssembler;
|
||||
import org.gradle.wrapper.WrapperConfiguration;
|
||||
|
||||
public class WrapperProbe {
|
||||
public static void main(String[] args) throws Exception {
|
||||
WrapperConfiguration config = new WrapperConfiguration();
|
||||
config.setDistribution(URI.create(args[0]));
|
||||
PathAssembler assembler = new PathAssembler(new File(args[1]), new File(args[1]));
|
||||
PathAssembler.LocalDistribution local = assembler.getDistribution(config);
|
||||
System.out.println(local.getDistributionDir().getAbsolutePath());
|
||||
System.out.println(local.getZipFile().getAbsolutePath());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -0,0 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Implementation-Title: Gradle Wrapper
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,59 @@
|
||||
# AGENTS.md
|
||||
|
||||
Guidance for coding agents working in this repository. **`CLAUDE.md` is the full brief** —
|
||||
what Memby is, how it is built and deployed, and why the architecture is the shape it is.
|
||||
Read it first. This file carries the rules that are easiest to break without noticing.
|
||||
|
||||
## Language: New Zealand English
|
||||
|
||||
**Everything a person reads is New Zealand English. No American spellings.**
|
||||
|
||||
- `-ise` / `-isation`, never `-ize` / `-ization` — personalise, synchronisation, organise,
|
||||
recognise, prioritise.
|
||||
- `-our`, never `-or` — colour, favourite, behaviour, honour, neighbour.
|
||||
- `-re`, never `-er` — centre, theatre, metre.
|
||||
- **licence** the noun, *license* the verb. **programme** for a broadcast, *program* for
|
||||
software.
|
||||
- grey, catalogue, cancelled, labelled, travelling, dialogue, defence, mould.
|
||||
|
||||
This covers on-screen copy in Kotlin and `app/src/main/res/values/strings.xml`, the admin
|
||||
console at `server/internal/api/admin.html`, the release landing page in `dist/template/`,
|
||||
every string the gateway sends the client to display — row titles,
|
||||
alert `label`s, the preference and feature catalogues, error messages — and this
|
||||
repository's own prose and comments.
|
||||
|
||||
### What is exempt, and why
|
||||
|
||||
**Identifiers and anything on a wire keep the spelling they already have.** Renaming them
|
||||
breaks the wire or the build:
|
||||
|
||||
- Emby's API — `favorites`, `IsFavorite`, `SearchTerm`.
|
||||
- Android and Compose — `Color`, `fontSize`, `TheaterComedy`, `RecognizerIntent`.
|
||||
- Go and Kotlin — `synchronized`, and the literal `"request canceled"` in `images.go`,
|
||||
which is matched against `net/http`'s own error text.
|
||||
- SPDX and licence names — "GNU General Public License", `GPL-2.0-only`.
|
||||
- CSS properties — `color`, `text-transform`, `box-sizing`.
|
||||
|
||||
The rule is about words a person reads, never tokens a machine matches.
|
||||
|
||||
### Where the two meet
|
||||
|
||||
The boundary sits at the render. The favourites row is the worked example: the id and the
|
||||
row `kind` stay `favorites` on both sides of the wire, and `personalisedFavouritesTitle`
|
||||
puts **"Favourites"** on the screen. When a new string is both a key and a label, spell the
|
||||
display half and leave the key alone.
|
||||
|
||||
A quick sweep before finishing a change that touches copy:
|
||||
|
||||
```bash
|
||||
grep -rnEio '\b([a-z]{3,}iz(e|es|ed|ing|ation)|behaviors?|colors?|favorites?|honor|theater|canceled|labeled|catalog|gray)\b' \
|
||||
app/src/main server/internal dist
|
||||
```
|
||||
|
||||
Expect hits — most are the exemptions above. Read each one and ask whether a viewer or an
|
||||
operator ever sees it.
|
||||
|
||||
## Version bumps
|
||||
|
||||
Every version bump is a release change: commit and push the complete release to GitHub. Do
|
||||
not leave a bumped version only in the local working tree.
|
||||
+424
@@ -0,0 +1,424 @@
|
||||
## 0.3.17 - 2026-08-24
|
||||
- Fixed: Crash on every cold start of a signed-in television. `MembyIcons`' icon-pack state and `DesignTokens`' palette state are process-wide `mutableStateOf`, and the first thing to touch either was `ThemeSync` applying the cached pack from an IO thread while the launcher was composing — so the state object was created in a snapshot newer than the composition reading it, and the navigation rail's first `MembyIcon.mark` read threw "Reading a state that was created after the snapshot was taken". `MembyApp.onCreate` now creates both on the main thread before anything else in the process can. Invisible on a fresh install because first-run and sign-in draw no marks, which is why clearing app data appeared to fix it.
|
||||
- Fixed: A television that had just installed the version the gateway demanded opened on the retired-build screen it was updating to escape. `AppRoot` now filters the persisted `requiredUpdateVersion` through `requiredUpdateSatisfied`, the same rule the in-memory signal and `markRequiredUpdate` already apply.
|
||||
|
||||
## 0.2.98 - 2026-08-22
|
||||
- v298 closes the following issues: 116 (Feature: Integrate Memby with Android TV Home Screen), 117 (Improve Row Navigation & Add “View All” Actions) and 115 (Add ratings strip to movie continue watching).
|
||||
- Added: Continue Watching support on the Google TV launcher homescreen (116)
|
||||
- Added: Ratings strip to continue watching cards (115)
|
||||
- Added: Buttons to rows which can take you back to main genre (117).
|
||||
- Improved: Cleaned up genres chips row on search page.
|
||||
- Improved: Switched cards to list cards for Search.
|
||||
- Fixed: RequeststCards.kt shares its component with Search.
|
||||
- Improved: Bug fixes & improvements to ResumeableMediaCard.kt, ContinueWatchingCard.kt
|
||||
- Updated: Server updated to v0.1.64
|
||||
- Publishes partially watched movies/episodes and Next Up episodes through Android TV’s native Watch Next API.
|
||||
- Updates launcher progress from existing playback reports.
|
||||
- Removes completed, stale, deleted, duplicate, signed-out, or previous-user entries.
|
||||
- Launcher cards deep-link through a validated activity directly into PlayerActivity, resuming from current Emby/Memby state.
|
||||
- Uses appropriate episode, series, season, runtime, description, and backdrop/poster metadata.
|
||||
- Keeps provider failures and offline reconciliation completely supplementary to playback.
|
||||
- Added an isolated tvhome architecture:
|
||||
- app/src/main/java/com/ponzischeme89/memby/tvhome/TvHomeIntegrationService.kt
|
||||
- app/src/main/java/com/ponzischeme89/memby/tvhome/WatchNextPublisher.kt
|
||||
- app/src/main/java/com/ponzischeme89/memby/tvhome/TvDeepLinkActivity.kt
|
||||
- app/src/main/java/com/ponzischeme89/memby/tvhome/TvDeepLinks.kt
|
||||
- app/src/main/java/com/ponzischeme89/memby/tvhome/HomeChannelPublisher.kt
|
||||
|
||||
## 0.2.97 - 2026-08-21
|
||||
- Improved: Modularised MembyButtons, ProfileChooser, SettingsSheet, ViewerControls
|
||||
- Added: Traliers no longer show controls that are outside of our control (e.g. Youtube)
|
||||
- Improved: Simplified the Emby exit screen prompt. Moved to EmbyMembyScreen.kt
|
||||
|
||||
## 0.2.96 - 2026-08-21
|
||||
- Improved: Refactor EmbyRepo
|
||||
- Improved: HomeNavigation.kt
|
||||
- Added: ServiceLocator.kt
|
||||
|
||||
## 0.2.95 - 2026-08-21
|
||||
- Improved: MetadataHero.kt
|
||||
- Added: MetadataHero.kt opts to Preferences
|
||||
|
||||
## 0.2.94 - 2026-08-21
|
||||
- Improved: MetadataHero.kt
|
||||
|
||||
## 0.2.93 - 2026-08-21
|
||||
- Fixed: Bugs with HomeOverlay
|
||||
|
||||
## 0.2.92 - 2026-08-21
|
||||
- Improved: App design / layout tweaks.
|
||||
- Improved: Many behind the scenes tweaks, bug fixes to make the app run smoother.
|
||||
- Improved: Gateway updated to version 0.1.63.
|
||||
|
||||
## 0.2.91 - 2026-08-21
|
||||
- Fixed: Bugs with HomeComponents/HomeScreen
|
||||
- Fixed: Bugs with FocusScaleContainer
|
||||
- Improved: LandscapeCard, ContinueWatchingCard, MediaBadges, MediaCardMetadata, MediaCardSizing, MediaLoading, PosterCard
|
||||
|
||||
## 0.2.90 - 2026-08-21
|
||||
- Fixed: Padding fixes with HomeComponents.kt
|
||||
|
||||
## 0.2.89 - 2026-08-21
|
||||
- Fixed: Responsive row sizing fixes
|
||||
|
||||
## 0.2.88 - 2026-08-21
|
||||
- Fixed: More bug fixes to HomeComponents.kt, HomeScreen.kt.
|
||||
- Tested: Tests passed with HomeMovieHeroTest.
|
||||
|
||||
## 0.2.87 - 2026-08-21
|
||||
- Fixed: Bugs with HomeComponents.kt
|
||||
- Fixed: Bugs with HomeOverlays.kt
|
||||
- Fixed: Bugs with HomeScreen.kt
|
||||
|
||||
## 0.2.86 - 2026-08-21
|
||||
- Fixed: Bugs with HomeComponents
|
||||
|
||||
## 0.2.85 - 2026-08-21
|
||||
- Fixed: Bug with LaunchPreroll
|
||||
- Fixed: Bug fixes to PrerollPreLoader
|
||||
- Fixed: Bug which prevented ResumableMediaCardTest from firing
|
||||
## 0.2.84 - 2026-08-21
|
||||
- Improved: Loading and performance of the continue watching row.
|
||||
|
||||
## 0.2.83 - 2026-08-20
|
||||
- Fixed: Turning off "Play the next episode" now turns off the next-episode recap or preview with it. With auto-play off but previews on, an episode still cut away two minutes before the end to play a preview and then rolled into the next episode anyway.
|
||||
- Fixed: "Who's watching?" now appears as soon as your server has viewers switched on, so the first person can actually be added. It was hidden until a second viewer existed, which nothing on the TV could create.
|
||||
- Improved: The user menu now lists the people on this account, so switching to somebody else is one press instead of a row leading to a separate screen. Your Emby sign-ins moved to "Switch account" just below them, and adding a viewer is a row in the same list.
|
||||
- Improved: The account chooser says plainly that it adds a separate Emby sign-in, so it is no longer mistaken for the way to add somebody to the account you are already using.
|
||||
- Improved: "Who’s watching?" is centred, evenly spaced, and the profile under focus is obvious from across the room. Names now line up along one baseline whichever one is selected.
|
||||
- Improved: Removing a user is a small delete mark that appears on the profile you are on — press Down to reach it — rather than a "Remove user" button parked under every profile. It still asks before it removes anything, and cancelling puts you back where you were.
|
||||
|
||||
## 0.2.82 - 2026-08-20
|
||||
- New: Each Memby account can have multiple "profiles" per TV.
|
||||
|
||||
## 0.2.81 - 2026-08-19
|
||||
- Improved: Moved poster media type icon to subtitle on Genres page.
|
||||
- Fixed: Homepage heroes now update as soon as an administrator changes them.
|
||||
- Fixed: Show/movie logos were not cached correctly.
|
||||
- Fixed: Genres list in Genres page sorted by user activity.
|
||||
|
||||
## 0.2.80 - 2026-08-19
|
||||
- New: Hold down on the user switcher to get a "clear all" option for notifications.
|
||||
- Fixed: Improved navigation behaviour on the TV Shows page.
|
||||
- Fixed: Design tweaks/fixes with Genres page.
|
||||
- Removed: Recent Searches feature.
|
||||
|
||||
## 0.2.79 - 2026-08-19
|
||||
- Bug fixes
|
||||
|
||||
## 0.2.78 - 2026-08-19
|
||||
- Removed: Genres row from the TV Series and Movies pages. Replaced by Genres page.
|
||||
- Improved: Added Mark as Watched button to Series detail pages.
|
||||
- Improved: In the backend, Intergrations has it's own tab.
|
||||
|
||||
## 0.2.77 - 2026-08-19
|
||||
- New: Films on the "Upcoming Movie releases" shelf now open a page of their own.
|
||||
- New: A trailer can be played from an upcoming film's page.
|
||||
- Improved: Backend of notifications now go through one service.
|
||||
- Improved: Design of Notifications.
|
||||
- Fixed: A bug where the short indent overlapping at the start of the show.
|
||||
- Fixed: Bug with Continue Watching not remembering playstate, in some cases.
|
||||
- Fixed: Backend server fixes.
|
||||
|
||||
## 0.2.76 - 2026-08-18
|
||||
- Fixed: Backend server fixes.
|
||||
|
||||
## 0.2.75 - 2026-08-18
|
||||
- Improved: Upgrade player version.
|
||||
- Fixed: Backend server fixes.
|
||||
|
||||
## 0.2.74 — 2026-08-17
|
||||
- Fixed: Genre shelves were showing films only. A programme whose details came from TMDb carries its genre as one label — "Sci-Fi & Fantasy", "Action & Adventure", "War & Politics" — which the shelves did not recognise, so every show was missing from Sci-Fi & Fantasy, Action & Adventure and War & History. Shows now appear on them beside the films.
|
||||
- Improved: Genre shelves recognise more ways of spelling the same genre, including the hyphenated names IMDb uses, so fewer titles are missed. Sport finds more than it did, though a film your server has not tagged with a sport genre at all still cannot appear there.
|
||||
- Improved: The genre grid is now five titles across.
|
||||
- New: While browsing genres, each poster carries a small mark in its top corner saying whether it is a film or a TV show, so you can tell them apart where a shelf holds both.
|
||||
- Improved: The genre you are looking at is picked out in dark green in the genre list, so it stays clear which shelf is on screen once you move down into the posters.
|
||||
|
||||
## 0.2.73 — 2026-08-17
|
||||
- Fixed: Reintroduced Genres.
|
||||
|
||||
## 0.2.72 — 2026-08-17
|
||||
- New: The player now has a Next episode button, so you can move on to the next episode at any point without going back to the programme's page.
|
||||
- New: A "Play something else" button on the player picks another film from your library and starts it, based on what you have been watching.
|
||||
- Fixed: The next episode is now found and offered whether or not you have automatic play-next switched on. With it off, the closing-credits panel and the Next episode banner appear as they should and wait for you to press Play, instead of never appearing at all.
|
||||
- Fixed: Starting the next episode no longer occasionally fails to connect. The stream is now re-established before it is needed rather than reusing one negotiated when the episode began.
|
||||
- Fixed: Reaching the closing credits, a progress update and a button press arriving together can no longer start the next episode more than once.
|
||||
- Fixed: Removed the obsolete Back item from the Settings rail now that it no longer exits Settings.
|
||||
|
||||
## 0.2.71 — 2026-08-17
|
||||
- Fixed: Leaving playback now saves the final position immediately and prevents an older progress update from moving the saved playhead backwards.
|
||||
- Fixed: Playback loading, pre-roll and first-frame screens now keep the selected title's artwork, series logo and episode details correct through previews, auto-advance and screen recreation.
|
||||
- Fixed: D-pad navigation now moves predictably through the navigation rail and profile menu, with Profiles kept at the top and Settings available from the profile menu.
|
||||
- Fixed: The TV calendar now displays New Zealand's Monday-first week order.
|
||||
- Technical fix: Playback reports are serialised, successful stop reports cancel their exact fallback job, and stale stop retries expire before they can overwrite progress made on another device.
|
||||
- Technical fix: Added configurable daily gateway quiet time, pausing television requests and background work while leaving the admin console and health controls available.
|
||||
|
||||
## 0.2.70 - 2026-08-16
|
||||
- Improvements: Playback "cold start" improvements.
|
||||
|
||||
## 0.2.69 - 2026-08-16
|
||||
- Fixed: Cached recommendation shelves remain visible while fresh recommendations rebuild, avoiding row flicker and recomposition.
|
||||
- Fixed: “For You” content now loads only when opened, rather than competing with homepage requests.
|
||||
- Fixed: Detail, related-title, episode and trailer prefetching waits until focus settles, preventing request storms during D-pad navigation.
|
||||
- Fixed: Homepage artwork preloading is smaller and lifecycle-aware.
|
||||
- Fixed: My Shows and notifications refresh concurrently.
|
||||
- Fixed: Diagnostic logging avoids unnecessary production-path work.
|
||||
- Fixed: Added time-to-interactive and improved frame/jank measurements, including a warm-start benchmark.
|
||||
|
||||
## 0.2.67 — 2026-08-16
|
||||
- Improved: Movie and show pages.
|
||||
|
||||
## 0.2.66 - 2026-08-15
|
||||
- Improved: Skip end credits.
|
||||
|
||||
## 0.2.65 - 2026-08-15
|
||||
- Added: Server now detects end credits more accurately.
|
||||
|
||||
## 0.2.64 — 2026-08-14
|
||||
- Improved: General app bug fixes & polish.
|
||||
- Fixed: Backend improvements.
|
||||
|
||||
## 0.2.63 — 2026-08-14
|
||||
- Improved: General app bug fixes & polish.
|
||||
- Improved: Backend.
|
||||
|
||||
## 0.2.62 — 2026-08-13
|
||||
- Fixed: Rolled back version 0.2.60.
|
||||
|
||||
## 0.2.60 — 2026-08-12
|
||||
- Added: Voice search on the Requests page, beside the query, the same as Search.
|
||||
- Fixed: Remote navigation on the Requests search page. Up through the keyboard now moves a row at a time instead of jumping to the tabs, coming back from the results lands on the key you were last on, and Back steps results → keyboard → tabs → out, one press at a time.
|
||||
- Added: A USB or phone keyboard types into the Requests search the same way it does into Search.
|
||||
- Fixed: The navigation rail is now on the Requests page, so Left goes somewhere and you can move straight to Home, Search or Settings without leaving it first.
|
||||
- Fixed: "Good morning" now greets you while the featured card is up, instead of appearing only once you had pressed Down past it.
|
||||
- Improved: Notifications shows a bell that quietly pulses while it is listening when you have nothing waiting, and a still grey one when notifications are switched off.
|
||||
- Changed: Requests no longer searches as you type. Type a name and press Search, so one title costs one look-up instead of one per letter — and two characters is now enough, so short titles work again. Dictating a title searches straight away.
|
||||
|
||||
## 0.2.59 — 2026-08-12
|
||||
- Fixed: Moving up through the on-screen keyboard on the Requests page no longer jumps straight to the tabs — only the top row of letters leaves for them.
|
||||
- Improved: Requests now waits for five letters, and for a short pause in typing, before looking anything up.
|
||||
- Fixed: The featured cards at the top of Home no longer lead with a film you have already watched. Series are unchanged, so a new season of something you are up to date with still features.
|
||||
- Fixed: Saving a setting could fail repeatedly with a server error.
|
||||
|
||||
## 0.2.58 — 2026-08-12
|
||||
- Added: Improved Requests option to the user selector menu.
|
||||
|
||||
## 0.2.57 — 2026-08-12
|
||||
- Fixed: Traliers show the logo from the show in the on screen player vs text.
|
||||
- Added: Smart search for next episode recap/preview episode from YouTube that plays 2 mins before show end
|
||||
- Added: Optional setting for next episode recap/preview.
|
||||
- Fixed: The source IP address for each tralier play should come from the Client's real IP address, not from the server.
|
||||
|
||||
## 0.2.56 — 2026-08-12
|
||||
- Added: Films, series and supported seasons can now play trailers with one press.
|
||||
- Improved: Memby prefers official Apple and YouTube trailers, plays them natively and automatically tries another source when one is unavailable, blocked or fails during playback.
|
||||
- Fixed: Trailer playback no longer needs a second Play press, opens a broken web player or leaves the viewer on a generic playback error.
|
||||
- Fixed: Back now returns cleanly to the detail page after a trailer, and Memby returns there automatically when no playable source remains.
|
||||
- Improved: Successful trailer sources are remembered for faster repeat playback without unnecessary provider requests.
|
||||
- Added: Selected labels, feature switches and safe presentation settings can now be managed by the server, while complete bundled defaults keep the app working normally when the server is slow or unavailable.
|
||||
- Improved: Search now has a dedicated Request action for finding the exact film or series to add, with clearer in-library and already-requested states.
|
||||
- Fixed: Film and series requests now use the intended monitored request workflow and provide clearer results when a title cannot be requested.
|
||||
|
||||
## 0.2.53 — 2026-08-11
|
||||
- Fixed: Removed the home-screen message asking viewers to open the For You page.
|
||||
- Improved: The play button used by the mini hero cards now appears over every focused poster.
|
||||
- Improved: Reduced the primary homepage hero logo so its supporting details remain visible.
|
||||
- Improved: The profile rail now shows the name of the user signed in on the television.
|
||||
|
||||
## 0.2.52 — 2026-08-11
|
||||
- Fixed: The last refreshed date in the Devices list used the incorrect date format.
|
||||
- Fixed: The show or film logo was missing from the primary home-page hero.
|
||||
- Fixed: My Alerts did not include an alert when Memby had been updated.
|
||||
- Fixed: The release date in version history used the incorrect date format.
|
||||
- Improved: Added soundbar and passthrough settings to the on-screen display.
|
||||
- Improved: Slimmed down the subtitles menu and made the Get subtitles button easier to see.
|
||||
- Improved: Added country flags to the subtitles search and menu.
|
||||
|
||||
## 0.2.51 — 2026-08-11
|
||||
- Improved: For You now begins loading after Home arrives, so its personalised rows are usually ready before you open it.
|
||||
- Fixed: Frequent screen updates can no longer repeat network requests, navigation, analytics, timers or other background work.
|
||||
- Fixed: Cancelled Home, For You, Search, detail and screensaver requests can no longer replace newer results or show a stale error.
|
||||
- Fixed: App updates now keep one download and installer attempt across screen and activity recreation instead of starting again.
|
||||
- Fixed: Settings choices are saved even when you leave Settings immediately after making them.
|
||||
- Fixed: Delayed D-pad focus, long-press, pagination, retry and permission actions now use the current screen state rather than an older callback.
|
||||
- Improved: Screen data is now collected with the active lifecycle, avoiding unnecessary work while a screen is stopped.
|
||||
|
||||
## 0.2.50 — 2026-08-11
|
||||
- Fixed: Playback reports are now serialised, and final stop positions are protected from out-of-order requests.
|
||||
- Fixed: Home, preferences, themes and detail screens now recover automatically with bounded retries.
|
||||
- Fixed: Retries can no longer cause request storms, duplicate media requests or repeated alert actions.
|
||||
- Fixed: Useful cached content remains visible when refreshes or later result pages fail.
|
||||
- Fixed: Network requests now have explicit loading, success, empty and failure states, with retry actions where appropriate.
|
||||
- Fixed: HTTP 429 responses now respect the server's Retry-After delay.
|
||||
- Fixed: Malformed and partially populated objects are safely filtered before reaching the interface.
|
||||
- Fixed: Rate limits, partial payloads and durable playback stops now have regression coverage.
|
||||
- Fixed: Resuming playback now cancels delayed background stop work before reporting the session as active again.
|
||||
- Fixed: Refreshed streams and subtitle-driven stream changes now close superseded playback sessions and start their replacements in order.
|
||||
- Fixed: Cancelled or stale stream, retry and subtitle requests can no longer overwrite a newer playback attempt.
|
||||
- Fixed: Enabling burned-in subtitles no longer lets the hidden video continue and then jumps back to an earlier position when the stream is ready.
|
||||
- Fixed: Playback now waits for a slow next-episode lookup, and auto-advance resets subtitle, preview, intro, credits, artwork and session state for the new episode.
|
||||
- Fixed: Player construction and synchronous media preparation failures now show recoverable Retry and Exit actions instead of closing the player.
|
||||
- Fixed: The pre-roll player remains idle while the main programme is playing, avoiding a second prepared decoder after hand-off.
|
||||
- Fixed: Repeated screensaver Play presses are gated, and a new single-task player intent now deliberately replaces the current playback.
|
||||
- Fixed: Playback position, stream identity, selected tracks and episode details now survive activity and process recreation.
|
||||
- Fixed: Natural playback completion now returns to the screen that launched the player instead of clearing the task back to Home.
|
||||
|
||||
## 0.2.49 — 2026-08-11
|
||||
- Fixed: D-pad focus now stays visible and predictable across My Shows, Settings, profile management, the TV calendar, detail pages and changing home rows.
|
||||
|
||||
## 0.2.48 — 2026-08-11
|
||||
- Improved: The TV calendar is now a modern weekly guide with artwork, title logos and clear season-premiere and finale labels.
|
||||
- Added: Home-screen metadata now shows the sound profile, including mono, stereo, 5.1 and 7.1.
|
||||
|
||||
## 0.2.47 — 2026-08-11
|
||||
- Fixed: A television signed out because its version has been retired now stays on the update screen — across a restart, and while the server is slow to answer — instead of falling back to a welcome and sign-in it could never get past.
|
||||
|
||||
## 0.2.46 — 2026-08-11
|
||||
- Added: A TV calendar on the navigation rail — the month ahead as a grid, with the focused day's episodes beside it, so you can see when a show comes back.
|
||||
- Added: A television signed out because its version has been retired now goes straight to the update screen, instead of showing a sign-in that could never succeed.
|
||||
- Improved: In Settings, pressing Up at the top of a page returns to the page list rather than doing nothing.
|
||||
- Fixed: The home screen could close itself when a row or a card arrived twice from the server. The same fault could close the cast list, search results, similar titles and episode lists.
|
||||
- Fixed: Pressing down through the rows on Home and TV Shows could stop part way and refuse to go any further until you left the page.
|
||||
- Fixed: Play could stop responding for the rest of the session if starting a title failed early, with nothing on screen to say why.
|
||||
- Fixed: A title the server could not supply a stream for now says so, instead of flickering and returning to where you were.
|
||||
- Fixed: Back now leaves the "starting playback" screen, rather than waiting out a server that has stopped answering.
|
||||
- Fixed: Dismissing one of your alerts takes effect immediately, so a slow connection no longer invites a second press.
|
||||
- Improved: Genre and search shelves no longer stop loading further pages when the server repeats a title across a page boundary.
|
||||
- Improved: Memby uses less memory on a television left on for weeks at a time.
|
||||
|
||||
## 0.2.45 - 2026-08-10
|
||||
- Bug fixes
|
||||
|
||||
## 0.2.43 — 2026-08-10
|
||||
- Fixed: App no longer crashes.
|
||||
- Fixed: Manual surround-sound choices now reliably override the automatically detected audio output.
|
||||
|
||||
## 0.2.42 — 2026-08-10
|
||||
- Added: Memby's own opening clip now plays while the app is starting up.
|
||||
- Added: Surround sound passthrough for Dolby Digital, Dolby Digital Plus, Dolby Atmos, DTS, DTS-HD and TrueHD, with automatic detection and manual receiver overrides.
|
||||
- Improved: Unsupported surround tracks are decoded to PCM on the TV, and audio conversion no longer makes Emby re-encode otherwise compatible video.
|
||||
- Fixed: Colour schemes now repaint the whole app. The home screen, Settings, Search and the opening screen only followed a theme in part.
|
||||
- Improved: A far wider range of welcome lines and opening messages, so the same one comes round much less often.
|
||||
- Improved: The opening screen is no longer rebuilt as Memby moves between start-up steps.
|
||||
|
||||
## 0.2.40 — 2026-08-10
|
||||
- Improved: Video playback recovers more reliably from decoder failures and prolonged buffering.
|
||||
- Improved: Memby's short pre-roll is prepared in advance for faster playback starts.
|
||||
- Added: A libmpv compatibility fallback for videos Media3 cannot decode or read.
|
||||
- Fixed: Shows opened from the upcoming schedule now resume from the viewer's latest episode progress.
|
||||
|
||||
## 0.2.39 — 2026-08-09
|
||||
- Improved: HEVC playback performance
|
||||
- Added: Cast panels now mark deceased performers and open biographies and filmographies.
|
||||
- Added: An optional confirmation before the Back button closes Memby.
|
||||
- Improved: Reviewed D-pad navigation across the Android TV experience and documented follow-up work.
|
||||
- Fixed: Continue Watching now keeps its saved position and shows Resume instead of Play.
|
||||
|
||||
## 0.2.38 — 2026-08-09
|
||||
- Improved: Ratings are larger and easier to read.
|
||||
- Improved: Genre cards now use the same poster format as home-screen rows.
|
||||
- Improved: The Memby rail logo now animates when opening every section.
|
||||
- Improved: The user switcher now shows each profile's initials.
|
||||
- Added: The About screen now shows the connected Emby gateway version.
|
||||
- Changed: The genre browser is now controlled by the server and is off by default while its layout is refined.
|
||||
- Fixed: The automatic home-screen artwork setting no longer reverts to posters.
|
||||
- Fixed: Watched changes now appear on posters immediately, just like favourite changes.
|
||||
|
||||
## 0.2.37 — 2026-08-09
|
||||
- Improved: Browse films and TV shows from colourful genre cards with clearer, consistent categories.
|
||||
- Improved: Genre pages now keep their place and show Favourite changes immediately after returning from a title.
|
||||
- Improved: App updates now show a brief confirmation instead of a full-screen release-notes panel.
|
||||
- Fixed: Episodes without their own cast information now use the series cast in the player.
|
||||
|
||||
## 0.2.36 — 2026-08-09
|
||||
- Improved: Browse all movies or TV shows alongside individual genres, with clearer navigation and paging.
|
||||
|
||||
## 0.2.35 — 2026-08-09
|
||||
- General bug fixes and improvements.
|
||||
|
||||
## 0.2.34 — 2026-08-09
|
||||
- General bug fixes and improvements.
|
||||
|
||||
## 0.2.33 — 2026-08-08
|
||||
- General bug fixes and improvements.
|
||||
|
||||
## 0.2.32 — 2026-08-08
|
||||
- General bug fixes and improvements.
|
||||
- Backend improvements.
|
||||
|
||||
## 0.2.31 — 2026-08-08
|
||||
- General bug fixes and improvements.
|
||||
|
||||
## 0.2.30 — 2026-08-08
|
||||
- General bug fixes and improvements.
|
||||
|
||||
## 0.2.29 — 2026-08-07
|
||||
- Fixed: A small bug with new seek previews.
|
||||
- Fixed: Skip Intro and Outro Credits were not appearing in some cases.
|
||||
|
||||
## 0.2.28 — 2026-08-07
|
||||
- Added: Skip outro credits
|
||||
- Added: Themes for Halloween, Christmas and Easter.
|
||||
|
||||
## 0.2.27 — 2026-08-06
|
||||
- Added: Skip Intro button
|
||||
- Fixed: Player improvements & bug fixes
|
||||
- Fixed: Gateway bug fixes & improvements
|
||||
- Fixed: Homepage hero relevancy
|
||||
- Fixed: Related titles showing up empty often
|
||||
- Fixed: You can add another user to the television again, from Manage users
|
||||
|
||||
## 0.2.26 — 2026-08-05
|
||||
- Improved: Ratings are more accurate and easier to read.
|
||||
- Fixed: Continue Watching now keeps the right order as you watch.
|
||||
- Fixed: Film and show pages no longer display incorrectly.
|
||||
- Fixed: The loading screen no longer appears when you skip forward or back.
|
||||
|
||||
## 0.2.25 — 2026-08-05
|
||||
- Added: Left and right on the remote skip back and forward while you watch.
|
||||
- Added: You can choose whether one press skips 10, 20 or 30 seconds.
|
||||
- Added: You can search for and add subtitles without leaving what you are watching.
|
||||
- Added: A cast button in the player shows who is in the scene.
|
||||
- Added: Episode lists now show when each episode aired.
|
||||
- Improved: Ratings now show the IMDb, Rotten Tomatoes, Metacritic, Letterboxd and TMDb logos.
|
||||
|
||||
## 0.2.24 — 2026-08-05
|
||||
- Fixed: The centre button on the remote pauses straight away instead of taking two presses.
|
||||
- Fixed: Reinstalling Memby on a television replaces it in your devices list instead of adding another one.
|
||||
- Fixed: Removing a television from Devices now clears it from Emby as well.
|
||||
- Improved: The subtitle menu is quicker to read and easier to move around.
|
||||
|
||||
## 0.2.23 — 2026-08-04
|
||||
- Added: Turning subtitles off, and the language you pick, are saved to your account.
|
||||
- Added: Tags showing whether a show is continuing, has ended, or is still in cinemas.
|
||||
- Improved: Settings is simpler, darker and easier to read.
|
||||
- Fixed: The Play button on the Home screen is no longer squashed by long titles.
|
||||
|
||||
## 0.2.22 — 2026-08-04
|
||||
- Fixed: Updates no longer fail with a warning about an untrusted release key.
|
||||
- Fixed: Updates now install on televisions where they previously did nothing.
|
||||
- Added: Memby asks for permission to install updates while you are setting up.
|
||||
- Added: Instructions for televisions that have no install permission screen.
|
||||
- Improved: You are now told clearly when an update fails.
|
||||
|
||||
## 0.2.20 — 2026-08-04
|
||||
- Added: A warning appears when Memby cannot reach your Emby server.
|
||||
- Added: Your settings are saved to your account and restored on any television you sign into.
|
||||
- Added: Title logos, auto-play and the ten-minute reminder are saved for each person.
|
||||
|
||||
## 0.2.17 — 2026-08-03
|
||||
- Added: Episodes in Continue Watching now open their own page.
|
||||
- Improved: Resume buttons show which episode you are up to.
|
||||
|
||||
## 0.2.16 — 2026-08-03
|
||||
- Added: You can rename or sign out televisions from Settings → Devices.
|
||||
- Added: Optional ratings on film pages.
|
||||
- Added: A version history in About, so you can see what changed.
|
||||
- Improved: The navigation rail stays visible while you are in Settings.
|
||||
|
||||
## 0.2.0 — 2026-05-01
|
||||
- Added: Instant search with an on-screen keyboard.
|
||||
- Added: Alerts for newly added titles, library updates and server outages.
|
||||
- Added: Personalised "Why you might enjoy it" suggestions on film and show pages.
|
||||
- Improved: Film and series pages now share one layout.
|
||||
- Improved: Home screen rows can change without updating the app.
|
||||
@@ -0,0 +1,340 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
Memby
|
||||
Copyright (C) 2026 Memby contributors
|
||||
|
||||
Memby is free software licensed under the GNU General Public License,
|
||||
version 2. A copy of that licence is included in the LICENSE file.
|
||||
|
||||
Source code:
|
||||
https://g.sublogue.com/admin/memby
|
||||
|
||||
Wholphin
|
||||
--------
|
||||
|
||||
Portions of Memby's Android playback capability probing, server-side
|
||||
device-profile generation, and ExoPlayer construction (constant-bitrate
|
||||
seeking and sourcing media over the application's own OkHttp client, in
|
||||
app/src/main/java/com/ponzischeme89/memby/ui/player/PlayerEngine.kt), and the
|
||||
design of its seek preview thumbnails
|
||||
(app/src/main/java/com/ponzischeme89/memby/ui/player/TrickplayPreview.kt), are
|
||||
adapted from Wholphin, an Android TV client licensed under GNU GPL version 2:
|
||||
|
||||
https://github.com/damontecres/Wholphin
|
||||
|
||||
Wholphin's profiling implementation is itself derived from Jellyfin Android TV:
|
||||
|
||||
https://github.com/jellyfin/jellyfin-androidtv
|
||||
|
||||
The adapted Memby files identify both upstream projects, identify Memby's 2026
|
||||
modifications, and remain under GPLv2. Copyright in the upstream work remains
|
||||
with its respective contributors.
|
||||
|
||||
Memby and Wholphin are both licensed under GPL version 2, so adapting source
|
||||
from Wholphin into Memby is permitted provided the combined work stays under
|
||||
that licence and attribution is preserved — which is what this file does. No
|
||||
Wholphin source is copied verbatim into Memby; the files listed above reproduce
|
||||
architectural techniques, and where an approach was reproduced independently
|
||||
rather than adapted it is recorded as such in PERFORMANCE.md.
|
||||
|
||||
Moonfin
|
||||
-------
|
||||
|
||||
Memby's surround-audio capability probe, Auto/manual per-codec passthrough
|
||||
rules, Media3 audio-sink configuration, and software-decode fallback are adapted
|
||||
from Moonfin's Android TV playback backend, licensed under GNU GPL version 2:
|
||||
|
||||
https://github.com/Moonfin-Client/Moonfin-Core
|
||||
|
||||
The adapted files identify Moonfin, identify Memby's 2026 modifications, and
|
||||
remain under GPLv2. Copyright in the upstream work remains with the Moonfin
|
||||
contributors.
|
||||
|
||||
Third-party software
|
||||
--------------------
|
||||
|
||||
Inter
|
||||
-----
|
||||
|
||||
Memby bundles the Inter font family, copyright 2016 The Inter Project Authors
|
||||
(https://github.com/rsms/inter), under the SIL Open Font License, Version 1.1.
|
||||
The complete font licence is packaged at assets/licenses/inter_ofl.txt.
|
||||
|
||||
The Android application uses AndroidX, Jetpack Compose, Media3, Coil,
|
||||
Retrofit, OkHttp, kotlinx.serialization, and other open-source libraries.
|
||||
Their copyright notices and licence metadata remain in their respective
|
||||
source distributions and packaged dependency metadata. Those components
|
||||
remain governed by their own licences; GPLv2 applies to Memby's original
|
||||
and combined application code as required by the licence.
|
||||
|
||||
The Media3 software audio fallback uses Jellyfin's FFmpeg decoder extension:
|
||||
|
||||
https://github.com/jellyfin/jellyfin-androidx-media
|
||||
|
||||
Memby is an independent project and is not affiliated with or endorsed by
|
||||
Emby LLC. Emby is a trademark of Emby LLC.
|
||||
|
||||
The marks shown beside each score in the ratings strip
|
||||
(app/src/main/res/drawable-nodpi/ic_rating_*.png) identify the rating provider
|
||||
a score came from. IMDb, Rotten Tomatoes, Metacritic, Letterboxd and TMDb are
|
||||
trademarks of their respective owners, used here only to attribute those
|
||||
scores. Memby is not affiliated with, endorsed by, or certified by any of them.
|
||||
@@ -1,53 +1,129 @@
|
||||
# Memby (Android TV)
|
||||
<div align="center">
|
||||
|
||||
An independent Android TV client for Emby, by **ponzischeme89**. Sign in to browse your
|
||||
personalised home screen, resume titles, play recent movies, and choose audio or subtitle
|
||||
tracks during playback. The screensaver (Dream) code remains isolated from the client work.
|
||||
<img src="memby-icon.png" alt="Memby" width="128" height="128">
|
||||
|
||||
# Memby
|
||||
|
||||
**An independent Android TV client for Emby.**
|
||||
|
||||
A personalised television launcher, a full media player, a system screensaver and an
|
||||
optional self-hosted gateway that shapes the experience for every viewer in the house.
|
||||
|
||||
[](LICENSE)
|
||||
[](#build--install)
|
||||
[](#build--install)
|
||||
[](#tech-stack)
|
||||
[](server/README.md)
|
||||
|
||||
Source: [g.sublogue.com/admin/memby](https://g.sublogue.com/admin/memby)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## Contents
|
||||
|
||||
- [How it works](#how-it-works)
|
||||
- [Features](#features)
|
||||
- [Browse and discover](#browse-and-discover)
|
||||
- [Playback](#playback)
|
||||
- [Profiles, televisions and administration](#profiles-televisions-and-administration)
|
||||
- [Screensaver and updates](#screensaver-and-updates)
|
||||
- [Tech stack](#tech-stack)
|
||||
- [Build & install](#build--install)
|
||||
- [Server address](#server-address)
|
||||
- [First run](#first-run)
|
||||
- [Running the gateway](#running-the-gateway)
|
||||
- [Project layout](#project-layout)
|
||||
- [Distributing builds](#distributing-builds)
|
||||
- [One-time: a signing key](#one-time-a-signing-key)
|
||||
- [NAS deployment release (recommended)](#nas-deployment-release-recommended)
|
||||
- [Local PowerShell release (compatibility path)](#local-powershell-release-compatibility-path)
|
||||
- [How TVs update themselves](#how-tvs-update-themselves)
|
||||
- [Upgrading to v0.1.53](#upgrading-to-v0153)
|
||||
- [Notes & limitations](#notes--limitations)
|
||||
- [Licence](#licence)
|
||||
|
||||
---
|
||||
|
||||
## How it works
|
||||
|
||||
The client can talk to Emby directly, or through the **Memby gateway** — a Go service in
|
||||
`server/` that runs in Docker alongside Postgres and Redis and owns auth, caching, search
|
||||
and the shaping of TV screens. With a gateway the launcher is one request instead of four,
|
||||
and the TV holds a revocable gateway token rather than a live Emby token.
|
||||
|
||||
```
|
||||
direct: TV ──────────────────────────────► Emby
|
||||
gateway: TV ──► Memby gateway ──► Emby (metadata + artwork)
|
||||
TV ─────────────────────► Emby (video stream; never proxied by the gateway)
|
||||
```
|
||||
|
||||
Which one a build uses is decided by `memby.gatewayUrl` in `gradle.properties`: set it and
|
||||
the app is a thin client; leave it blank and the app talks to Emby directly. Both paths are
|
||||
maintained — the direct path is the fallback when the container is down.
|
||||
|
||||
## Features
|
||||
|
||||
- **System screensaver** via `DreamService` — auto-starts on idle once selected in the
|
||||
TV's Screensaver settings. Also previewable from the app's home screen.
|
||||
- Random, cross-fading **backdrops** of Movies and Series that have backdrop images.
|
||||
- **Interactive**: OK reveals Play / Favorite actions; ◄ ► change the backdrop; Back exits.
|
||||
- **In-app playback** with Media3/ExoPlayer (movies play directly; a series plays its
|
||||
next-up / first episode).
|
||||
- **Favorites** managed through the Emby API and shown on the home screen.
|
||||
### Browse and discover
|
||||
|
||||
- Personalised, server-composed Home rows with a cached instant-start launcher.
|
||||
- A cinematic featured hero that rotates through ranked films and series premieres.
|
||||
- Continue Watching merged with Next Up, ordered by the viewer's recent activity.
|
||||
- Favourites, latest films, recommendations, curated shelves and schedule rows.
|
||||
- Dedicated Movies, TV Shows, Favourites and For You destinations.
|
||||
- Instant search with an on-screen keyboard, voice input and session result caching.
|
||||
- Optional, server-controlled genre browsing with paged film and series catalogues.
|
||||
- A dedicated For You page combining viewing history, browsing signals, available time
|
||||
and direct-play evidence; every recommendation explains why it was selected.
|
||||
- More Like This, cast and technical details on film and series pages.
|
||||
- Per-title IMDb, Rotten Tomatoes, Metacritic, Letterboxd and TMDb ratings where available.
|
||||
- My Shows tracking, upcoming episodes, lifecycle badges and personal return alerts.
|
||||
- Themes, seasonal colour schemes and optional launcher decorations.
|
||||
|
||||
### Playback
|
||||
|
||||
- Media3/ExoPlayer playback with direct play, compatible-stream remuxing and HLS fallback.
|
||||
- Resume playback, progress reporting, watched state and immediate poster badge updates.
|
||||
- Audio and subtitle track selection with per-viewer language preferences.
|
||||
- Missing-subtitle search and download through Bazarr or OpenSubtitles when configured.
|
||||
- Configurable Left/Right seeking with trickplay thumbnail previews.
|
||||
- Skip-intro prompts or automatic skipping from Emby's chapter markers.
|
||||
- Automatic next-episode playback with an on-screen countdown.
|
||||
- Closing-credits presentation with the next episode alongside and adaptive 2× playback.
|
||||
- Episode pages with season navigation, watched markers and estimated completion dates.
|
||||
- In-player cast lookup, pause artwork, time-remaining reminders and service-status banners.
|
||||
|
||||
### Profiles, televisions and administration
|
||||
|
||||
- Multiple saved viewer profiles with quick switching from the navigation rail.
|
||||
- Per-viewer settings synchronised through the gateway across every signed-in television.
|
||||
- Device management, session revocation and stable television identity.
|
||||
- Gateway-controlled feature flags, maintenance mode, forced app updates and live themes.
|
||||
- Service alerts for new library items, aired episodes, deployments and Emby outages.
|
||||
- A dependency-free web console for accounts, devices, settings history, features,
|
||||
updates, library imports, recommendations, searches, row engagement and logs.
|
||||
- Postgres catalogue import, Redis caching, full-text search and background recommendation
|
||||
and ratings warming.
|
||||
|
||||
### Screensaver and updates
|
||||
|
||||
- A system `DreamService` screensaver with cross-fading library backdrops.
|
||||
- Interactive screensaver controls for playback, favourites and artwork navigation.
|
||||
- An in-app screensaver preview using the same rendering surface as the system Dream.
|
||||
- Self-hosted signed APK updates with SHA-256, package and signing-key verification.
|
||||
- Mandatory and optional update policy controlled by the gateway.
|
||||
|
||||
## Tech stack
|
||||
|
||||
- Kotlin + Jetpack **Compose for TV** (`androidx.tv:tv-material`)
|
||||
- **Media3 / ExoPlayer** for playback
|
||||
- Retrofit + OkHttp + kotlinx.serialization for the Emby REST API
|
||||
- DataStore for persisted connection/session
|
||||
- Coil for backdrop image loading
|
||||
|
||||
## Project layout
|
||||
|
||||
```
|
||||
app/src/main/java/com/ponzischeme89/memby/
|
||||
MembyApp.kt Application; initialises the ServiceLocator
|
||||
ServiceLocator.kt Manual DI (settings + repository)
|
||||
data/
|
||||
SettingsStore.kt DataStore-backed connection/session state
|
||||
ServerConfig.kt Which backend this build talks to
|
||||
EmbyRepository.kt Content, favorites, playback; dual gateway/direct paths
|
||||
analytics/RowAnalytics.kt Row engagement buffering
|
||||
model/ Emby + gateway DTOs (kotlinx.serialization)
|
||||
remote/EmbyApi.kt Retrofit interface for Emby
|
||||
remote/GatewayApi.kt Retrofit interface for the Memby gateway
|
||||
ui/
|
||||
MainActivity.kt Setup, profiles, home
|
||||
HomeViewModel.kt Home state, row analytics, refresh
|
||||
HomeComponents.kt Navigation rail, rows, cards
|
||||
MaintenanceScreen.kt Full-screen offline state
|
||||
settings/SettingsSheet.kt Settings panel
|
||||
screensaver/ Shared slideshow + in-app preview host
|
||||
player/PlayerActivity.kt Media3 playback
|
||||
screensaver/MembyDreamService.kt System screensaver (hosts ScreensaverContent)
|
||||
|
||||
server/ The Memby gateway (Go) — see server/README.md
|
||||
```
|
||||
| Layer | Built with |
|
||||
| --- | --- |
|
||||
| UI | Kotlin + Jetpack **Compose for TV** (`androidx.tv:tv-material3`) |
|
||||
| Playback | **Media3 / ExoPlayer** |
|
||||
| Networking | Retrofit + OkHttp + kotlinx.serialization, over one shared HTTP stack |
|
||||
| Storage | DataStore for profiles, settings and cached launcher state |
|
||||
| Images | Coil |
|
||||
| Gateway | Go, with Postgres and Redis |
|
||||
|
||||
## Build & install
|
||||
|
||||
@@ -62,7 +138,9 @@ terminal:
|
||||
"sdk.dir=C:\\Users\\<you>\\AppData\\Local\\Android\\Sdk" | Out-File -Encoding ascii local.properties
|
||||
|
||||
.\gradlew.bat assembleDebug # build the APK
|
||||
.\gradlew.bat test # JVM unit tests
|
||||
.\gradlew.bat installDebug # install to a connected Android TV / emulator
|
||||
|
||||
# Preferred for a TV already showing the Dream: clears Memby, installs, then reopens
|
||||
# it so the old render surface cannot remain black. Pass -Serial when more than one
|
||||
# device is attached.
|
||||
@@ -71,28 +149,6 @@ terminal:
|
||||
|
||||
The APK lands in `app/build/outputs/apk/debug/app-debug.apk`.
|
||||
|
||||
## Two ways to run
|
||||
|
||||
The client can talk to Emby directly, or through the **Memby gateway** — a Go service in
|
||||
`server/` that runs in Docker alongside Postgres and Redis and owns auth, caching, search
|
||||
and the shaping of TV screens. With a gateway the launcher is one request instead of four,
|
||||
and the TV holds a revocable gateway token rather than a live Emby token.
|
||||
|
||||
```
|
||||
direct: TV ──────────────────────────────► Emby
|
||||
gateway: TV ──► Memby gateway ──► Emby (metadata + artwork)
|
||||
TV ─────────────────────► Emby (video, always direct-play)
|
||||
```
|
||||
|
||||
Which one a build uses is decided by `memby.gatewayUrl` in `gradle.properties`: set it and
|
||||
the app is a thin client; leave it blank and nothing changes from the direct path below.
|
||||
See [`server/README.md`](server/README.md) to run the container.
|
||||
|
||||
The gateway also imports Emby's catalogue into Postgres (once manually, then hourly for
|
||||
new episodes), composes the home rows — including "Recommended from your watching
|
||||
history" — and has an admin page at `/admin/` for imports, an offline switch, and
|
||||
per-row engagement.
|
||||
|
||||
## Server address
|
||||
|
||||
Memby is built for one Emby server, so the address is baked into the APK instead of being
|
||||
@@ -105,11 +161,11 @@ memby.serverUrl=http://192.168.1.10:8096
|
||||
It can also come from `~/.gradle/gradle.properties` (keeps it out of the repo) or a single
|
||||
build: `.\gradlew.bat assembleDebug -Pmemby.serverUrl=http://192.168.1.10:8096`.
|
||||
|
||||
The value becomes `BuildConfig.EMBY_SERVER_URL`, read through
|
||||
`data/ServerConfig.kt`. When it is set, the setup screen only asks for a username and
|
||||
password, and the address wins over whatever a saved session recorded — so moving the
|
||||
server is a property change plus a reinstall, with no user action. Leaving the property
|
||||
**blank** restores the original behaviour: users type the address themselves.
|
||||
The value becomes `BuildConfig.EMBY_SERVER_URL`, read through `data/ServerConfig.kt`. When
|
||||
it is set, the setup screen only asks for a username and password, and the address wins
|
||||
over whatever a saved session recorded — so moving the server is a property change plus a
|
||||
reinstall, with no user action. Leaving the property **blank** restores the original
|
||||
behaviour: users type the address themselves.
|
||||
|
||||
## First run
|
||||
|
||||
@@ -119,16 +175,60 @@ server is a property change plus a reinstall, with no user action. Leaving the p
|
||||
3. **Preview screensaver** to test it, or **Set as system screensaver** to open the TV's
|
||||
screensaver settings and choose "Memby Screensaver".
|
||||
|
||||
## Running the gateway
|
||||
|
||||
See [`server/README.md`](server/README.md) to run the container. In short, the gateway
|
||||
imports Emby's catalogue into Postgres (once manually, then hourly for new episodes),
|
||||
composes the home rows — including "Recommended from your watching history" — and serves an
|
||||
admin console at `/admin/` for imports, an offline switch, and per-row engagement.
|
||||
|
||||
For Tracearr-powered recommendations, create a read-only public API key in Tracearr and set
|
||||
`MEMBY_TRACEARR_URL` plus `MEMBY_TRACEARR_API_KEY` on the gateway (and optionally
|
||||
`MEMBY_TRACEARR_SERVER_ID` when Tracearr monitors several servers). Tracearr credentials
|
||||
remain server-side; the Android app only receives ranked Emby items and short reasons. The
|
||||
gateway imports compact session signals into Postgres every five minutes and prepares an
|
||||
over-provisioned per-user pool, so opening For You normally performs one indexed database
|
||||
read. The live on-demand path remains available for cold starts and failed rebuilds.
|
||||
|
||||
## Project layout
|
||||
|
||||
```
|
||||
app/src/main/java/com/ponzischeme89/memby/
|
||||
MembyApp.kt Application; initialises the ServiceLocator
|
||||
ServiceLocator.kt Manual DI (settings + repository)
|
||||
data/
|
||||
SettingsStore.kt DataStore-backed connection/session state
|
||||
ServerConfig.kt Which backend this build talks to
|
||||
EmbyRepository.kt Content, favourites, playback; dual gateway/direct paths
|
||||
analytics/RowAnalytics.kt Row engagement buffering
|
||||
model/ Emby + gateway DTOs (kotlinx.serialization)
|
||||
remote/EmbyApi.kt Retrofit interface for Emby
|
||||
remote/GatewayApi.kt Retrofit interface for the Memby gateway
|
||||
ui/
|
||||
MainActivity.kt Setup, profiles, home
|
||||
HomeViewModel.kt Home state, row analytics, refresh
|
||||
HomeComponents.kt Navigation rail, rows, cards
|
||||
MaintenanceScreen.kt Full-screen offline state
|
||||
settings/SettingsSheet.kt Settings panel
|
||||
screensaver/ Shared slideshow + in-app preview host
|
||||
player/PlayerActivity.kt Media3 playback
|
||||
screensaver/MembyDreamService.kt System screensaver (hosts ScreensaverContent)
|
||||
|
||||
admin-ui/ The operations console (React, served by nginx)
|
||||
server/ The Memby gateway (Go) — see server/README.md
|
||||
benchmark/ Macrobenchmarks and baseline-profile generation
|
||||
```
|
||||
|
||||
## Distributing builds
|
||||
|
||||
Memby is handed out as an APK from your own web server or NAS, and updates itself from
|
||||
the same folder.
|
||||
Memby is handed out as an APK from your own web server or NAS, and updates itself from the
|
||||
same folder.
|
||||
|
||||
### One-time: a signing key
|
||||
|
||||
Android identifies an app by `applicationId` **plus signing key**. Every update must be
|
||||
signed with the *same* key, or the TV rejects it as a different app. Lose the key and
|
||||
every user has to uninstall and reinstall.
|
||||
signed with the *same* key, or the TV rejects it as a different app. Lose the key and every
|
||||
user has to uninstall and reinstall.
|
||||
|
||||
```powershell
|
||||
keytool -genkeypair -v -keystore memby-release.jks -alias memby `
|
||||
@@ -145,22 +245,42 @@ memby.keyAlias=memby
|
||||
memby.keyPassword=…
|
||||
```
|
||||
|
||||
Without these, `assembleRelease` still builds but the APK is unsigned and will not
|
||||
install. The build prints a warning saying so.
|
||||
Without these, `assembleRelease` still builds but the APK is unsigned and will not install.
|
||||
The build prints a warning saying so.
|
||||
|
||||
### Each release
|
||||
### NAS deployment release (recommended)
|
||||
|
||||
`deploy-server.ps1` builds and publishes the TV app in the same operation that replaces the
|
||||
gateway: it signs with the existing keystore, verifies the APK and publishes it through
|
||||
`POST /admin/api/release` once the new gateway is healthy. A pushed semantic tag does the
|
||||
same thing through `.gitea/workflows/release.yml`. The gateway builds nothing itself.
|
||||
|
||||
The signed APK and its checksum are stored in the `memby-releases` volume at
|
||||
`/data/releases/memby-<version>.apk` and `/data/releases/memby-<version>.apk.sha256`. The
|
||||
release-publish credential is a read-only Compose secret under `/run/secrets` and is never
|
||||
copied into the image or repository.
|
||||
|
||||
Admin Console → App updates sets the update policy — latest version, APK URL, release
|
||||
notes, and the required/destructive toggles.
|
||||
|
||||
### Local PowerShell release (compatibility path)
|
||||
|
||||
```powershell
|
||||
.\release.ps1 -Version 0.1.54 -Notes "Faster home screen" -BaseUrl https://nas.example.com/memby
|
||||
.\release.ps1 -Version 0.1.54 -Notes "Faster home screen" `
|
||||
-BaseUrl https://nas.example.com/memby `
|
||||
-SourceUrl https://g.sublogue.com/admin/memby
|
||||
```
|
||||
|
||||
That bumps `versionName`/`versionCode`, runs the tests, builds a signed APK, and fills
|
||||
`dist/out/` with:
|
||||
That workflow bumps `versionName`/`versionCode`, runs the tests, builds a signed APK, and
|
||||
fills `dist/out/` with:
|
||||
|
||||
```
|
||||
index.html the page people are sent to
|
||||
latest.json the update manifest the app polls
|
||||
memby-0.1.54.apk the build
|
||||
index.html the page people are sent to
|
||||
latest.json the update manifest the app polls
|
||||
memby-0.1.54.apk the build
|
||||
memby-0.1.54.apk.sha256 the SHA-256 checksum
|
||||
LICENSE GNU GPL v2 terms
|
||||
NOTICE copyright and third-party acknowledgements
|
||||
```
|
||||
|
||||
Copy those to the folder the NAS serves. Old APKs can stay alongside — only `latest.json`
|
||||
@@ -168,13 +288,11 @@ decides what the app offers, so rolling back is editing one file.
|
||||
|
||||
### How TVs update themselves
|
||||
|
||||
In Memby's Settings, set the update URL to `https://nas.example.com/memby/latest.json`.
|
||||
**Check for updates** then downloads and installs on the TV, no computer involved. A URL
|
||||
ending in `.json` is read as a static manifest; anything else is treated as a Gitea host
|
||||
(`/api/v1/repos/{owner}/{repo}/releases/latest`), so either source works.
|
||||
|
||||
The manifest's `apkUrl` may be relative (`memby-0.1.54.apk`) and is resolved against the
|
||||
manifest's own URL, so the folder keeps working if the NAS is reached by another name.
|
||||
The gateway checks the running app version on launch and returns an optional or mandatory
|
||||
update with its download address. Memby downloads the APK, verifies its checksum, package
|
||||
and release signature, then commits it through Android's package installer. Update policy
|
||||
and the download address are managed in the gateway's admin console; viewers do not need to
|
||||
configure an update source on the television.
|
||||
|
||||
First install on each TV still has to be manual — the **Downloader** app pointed at the
|
||||
landing page is the usual route, and the page explains it.
|
||||
@@ -188,17 +306,35 @@ different app, so on every TV:
|
||||
1. Install v0.1.53 — it appears as a **second** Memby entry in the launcher.
|
||||
2. Sign in again; the previous session does not carry over.
|
||||
3. Uninstall the old app: `adb uninstall com.mattcohen.embyclientsname`.
|
||||
4. Re-select Memby in the TV's Screensaver settings — the Dream's component name changed
|
||||
as well, so the old selection no longer resolves.
|
||||
4. Re-select Memby in the TV's Screensaver settings — the Dream's component name changed as
|
||||
well, so the old selection no longer resolves.
|
||||
|
||||
## Notes & limitations
|
||||
|
||||
- Playback uses Emby's direct stream (`/Videos/{id}/stream?static=true`). This direct-plays
|
||||
containers/codecs ExoPlayer supports (most MP4/H.264, many MKV). Server-side transcoding
|
||||
is not requested; unusual codecs may need it — a future enhancement is to call Emby's
|
||||
`PlaybackInfo` endpoint and use the returned HLS transcode URL (the `media3-exoplayer-hls`
|
||||
dependency is already included).
|
||||
- Cleartext HTTP is enabled so local `http://` servers work out of the box. For an HTTPS-only
|
||||
server this is unnecessary but harmless.
|
||||
- Playback negotiates through Emby's `PlaybackInfo` endpoint, then streams directly from
|
||||
Emby. The Wholphin/Jellyfin-derived capability engine reports Android's H.264 and HEVC
|
||||
profiles, maximum levels and resolutions rather than assuming every decoder handles every
|
||||
file. The Moonfin-derived audio path reports formats the TV can decode or bitstream,
|
||||
offers automatic receiver detection and manual per-codec passthrough overrides, and
|
||||
decodes the rest to PCM through Media3's FFmpeg renderer. Emby can preserve a supported
|
||||
video stream while converting only incompatible audio or subtitles. If a vendor decoder
|
||||
still fails, Media3 tries another decoder and Memby ultimately requests an H.264 HLS
|
||||
transcode instead of abandoning playback.
|
||||
- Cleartext HTTP is enabled so local `http://` servers work out of the box. For an
|
||||
HTTPS-only server this is unnecessary but harmless.
|
||||
- The device is remembered across sign-outs (stable `DeviceId`); credentials are cleared.
|
||||
```
|
||||
|
||||
## Licence
|
||||
|
||||
Memby is free software licensed under the [GNU General Public License v2](LICENSE).
|
||||
Copyright and third-party acknowledgements are recorded in [NOTICE](NOTICE). Distributed
|
||||
APKs and server binaries must be accompanied by the corresponding source in accordance with
|
||||
GPLv2. The Android TV app also exposes the source link, notices, and complete licence under
|
||||
**Settings → About / Licences**.
|
||||
|
||||
Playback capability probing and device-profile generation contain GPLv2 adaptations from
|
||||
[Wholphin](https://github.com/damontecres/Wholphin), itself derived in part from
|
||||
[Jellyfin Android TV](https://github.com/jellyfin/jellyfin-androidtv). Surround-audio
|
||||
capability detection, passthrough controls and software fallback are adapted from
|
||||
[Moonfin](https://github.com/Moonfin-Client/Moonfin-Core) under GPLv2. Attribution and
|
||||
modification notices are preserved in the adapted source files.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# The Dockerfile copies only the manifest and source files it needs. Keeping these
|
||||
# out of the build context avoids sending a local development install or stale bundle
|
||||
# to the daemon before Node creates its clean, reproducible build-stage install.
|
||||
node_modules
|
||||
dist
|
||||
@@ -0,0 +1,33 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# The Memby operations console.
|
||||
#
|
||||
# Two stages: Node builds the static bundle, nginx serves it. Nothing from Node survives
|
||||
# into the running image — the console is a directory of files, and the container's only
|
||||
# job is to hand them over on the compose network. It is never published: the gateway
|
||||
# reverse-proxies /admin to it, so the console shares the gateway's origin, cookie and
|
||||
# single ingress.
|
||||
|
||||
FROM node:22-alpine AS build
|
||||
WORKDIR /ui
|
||||
|
||||
# Dependencies first so an edit to the source does not re-resolve the whole tree.
|
||||
COPY package.json package-lock.json* ./
|
||||
# `npm ci` when there is a lockfile — reproducible, and the only reason a lockfile is
|
||||
# checked in — falling back to `npm install` so a first build works without one.
|
||||
RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
|
||||
|
||||
COPY tsconfig.json vite.config.ts index.html ./
|
||||
COPY src ./src
|
||||
# The type check is part of the build rather than a separate step: a console that compiles
|
||||
# to JavaScript but reads a field the gateway does not send is exactly the failure the
|
||||
# hand-written wire types exist to catch, and it must not reach a deployment.
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:1.27-alpine
|
||||
# Read-only, non-root and no default site: the image serves one directory and does nothing
|
||||
# else. nginx's own temp paths are the only writable thing it needs, and they are declared
|
||||
# as tmpfs in docker-compose.yml.
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=build /ui/dist /usr/share/nginx/html
|
||||
EXPOSE 80
|
||||
+1
File diff suppressed because one or more lines are too long
+11
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+59
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
||||
import"./router-D9WH5XEU.js";
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
<!doctype html>
|
||||
<html lang="en-NZ">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<!-- The console is behind an admin token and is nobody's search result. -->
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<meta name="color-scheme" content="dark" />
|
||||
<meta name="theme-color" content="#010409" />
|
||||
<title>Memby admin</title>
|
||||
<!-- An SVG favicon, inline, because the console must fetch nothing from anywhere. -->
|
||||
<link
|
||||
rel="icon"
|
||||
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Ccircle cx='16' cy='16' r='16' fill='%2352b54b'/%3E%3Ctext x='16' y='23' font-family='system-ui,sans-serif' font-size='19' font-weight='800' text-anchor='middle' fill='%2306240a'%3EM%3C/text%3E%3C/svg%3E"
|
||||
/>
|
||||
<script type="module" crossorigin src="/admin/assets/index-D2yWw-VA.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/admin/assets/router-D9WH5XEU.js">
|
||||
<link rel="stylesheet" crossorigin href="/admin/assets/index-BIMcejkS.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,21 @@
|
||||
<!doctype html>
|
||||
<html lang="en-NZ">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<!-- The console is behind an admin token and is nobody's search result. -->
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<meta name="color-scheme" content="dark" />
|
||||
<meta name="theme-color" content="#010409" />
|
||||
<title>Memby admin</title>
|
||||
<!-- An SVG favicon, inline, because the console must fetch nothing from anywhere. -->
|
||||
<link
|
||||
rel="icon"
|
||||
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Ccircle cx='16' cy='16' r='16' fill='%2352b54b'/%3E%3Ctext x='16' y='23' font-family='system-ui,sans-serif' font-size='19' font-weight='800' text-anchor='middle' fill='%2306240a'%3EM%3C/text%3E%3C/svg%3E"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,75 @@
|
||||
# The console's own server. It sees only requests the gateway has already authenticated
|
||||
# and forwarded, so there is no auth here — and there must not be, or there would be two
|
||||
# places deciding who may read the console.
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
|
||||
# The gateway proxies /admin/* through untouched, so the paths arriving here carry that
|
||||
# prefix. Vite is built with base=/admin/, so the asset URLs in index.html match.
|
||||
location /admin/assets/ {
|
||||
alias /usr/share/nginx/html/assets/;
|
||||
# Hashed filenames: a changed file is a changed name, so this can be cached hard
|
||||
# and a deployment is picked up the moment the shell is re-fetched.
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Canonicalise the console root even though the gateway normally performs this
|
||||
# redirect before proxying. It keeps the static container correct if it is ever
|
||||
# reached directly during diagnosis or through a future internal proxy change.
|
||||
location = /admin {
|
||||
return 308 /admin/;
|
||||
}
|
||||
|
||||
# Every other /admin path is a console route — /admin/logins, /admin/devices/tv-1, a
|
||||
# deep link, a refresh, the Back button — and none of them exist as files. They all
|
||||
# answer with the shell, which is what makes client-side routing work on a reload.
|
||||
location /admin {
|
||||
try_files $uri @console_shell;
|
||||
# The shell must never be cached: it carries the asset hashes, so a stale copy
|
||||
# points at JavaScript a deployment has already replaced. The gateway sets this too
|
||||
# on the way past; it is stated in both places because either alone would be enough
|
||||
# to get wrong.
|
||||
add_header Cache-Control "no-store" always;
|
||||
}
|
||||
|
||||
# `try_files ... /index.html` would internally re-match the fallback against the
|
||||
# catch-all `location /` below, which deliberately returns 404. A named location
|
||||
# retains this server's static root and serves the shell without exposing /index.html
|
||||
# as a second public route.
|
||||
location @console_shell {
|
||||
try_files /index.html =404;
|
||||
add_header Cache-Control "no-store" always;
|
||||
}
|
||||
|
||||
# A liveness probe for docker-compose. Deliberately outside /admin so it cannot be
|
||||
# confused with a console route, and it answers without touching the filesystem.
|
||||
location = /healthz {
|
||||
access_log off;
|
||||
default_type text/plain;
|
||||
return 200 "ok\n";
|
||||
}
|
||||
|
||||
# Nothing else is served. The console is the only thing in this image.
|
||||
location / {
|
||||
return 404;
|
||||
}
|
||||
|
||||
# The console fetches nothing from anywhere: no CDN, no fonts, no analytics. Saying so
|
||||
# in a policy means a dependency that tried to would fail loudly during development
|
||||
# rather than quietly phone home from an operator's browser.
|
||||
#
|
||||
# 'unsafe-inline' for styles is Vite's injected critical CSS; connect-src 'self' covers
|
||||
# the API and the notification stream, both of which are same-origin by construction.
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "no-referrer" always;
|
||||
|
||||
gzip on;
|
||||
gzip_types text/css application/javascript application/json image/svg+xml;
|
||||
gzip_min_length 1024;
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../baseline-browser-mapping/dist/cli.cjs" "$@"
|
||||
else
|
||||
exec node "$basedir/../baseline-browser-mapping/dist/cli.cjs" "$@"
|
||||
fi
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\baseline-browser-mapping\dist\cli.cjs" %*
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../baseline-browser-mapping/dist/cli.cjs" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../baseline-browser-mapping/dist/cli.cjs" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../baseline-browser-mapping/dist/cli.cjs" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../baseline-browser-mapping/dist/cli.cjs" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../browserslist/cli.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../browserslist/cli.js" "$@"
|
||||
fi
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\browserslist\cli.js" %*
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../browserslist/cli.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../browserslist/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../browserslist/cli.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../browserslist/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../esbuild/bin/esbuild" "$@"
|
||||
else
|
||||
exec node "$basedir/../esbuild/bin/esbuild" "$@"
|
||||
fi
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\esbuild\bin\esbuild" %*
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../esbuild/bin/esbuild" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../esbuild/bin/esbuild" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../esbuild/bin/esbuild" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../esbuild/bin/esbuild" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../jsesc/bin/jsesc" "$@"
|
||||
else
|
||||
exec node "$basedir/../jsesc/bin/jsesc" "$@"
|
||||
fi
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\jsesc\bin\jsesc" %*
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../jsesc/bin/jsesc" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../jsesc/bin/jsesc" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../jsesc/bin/jsesc" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../jsesc/bin/jsesc" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../json5/lib/cli.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../json5/lib/cli.js" "$@"
|
||||
fi
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\json5\lib\cli.js" %*
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../json5/lib/cli.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../json5/lib/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../json5/lib/cli.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../json5/lib/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../loose-envify/cli.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../loose-envify/cli.js" "$@"
|
||||
fi
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\loose-envify\cli.js" %*
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../loose-envify/cli.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../loose-envify/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../loose-envify/cli.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../loose-envify/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../nanoid/bin/nanoid.cjs" "$@"
|
||||
else
|
||||
exec node "$basedir/../nanoid/bin/nanoid.cjs" "$@"
|
||||
fi
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\nanoid\bin\nanoid.cjs" %*
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../@babel/parser/bin/babel-parser.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../@babel/parser/bin/babel-parser.js" "$@"
|
||||
fi
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\@babel\parser\bin\babel-parser.js" %*
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../@babel/parser/bin/babel-parser.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../@babel/parser/bin/babel-parser.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../@babel/parser/bin/babel-parser.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../@babel/parser/bin/babel-parser.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../rollup/dist/bin/rollup" "$@"
|
||||
else
|
||||
exec node "$basedir/../rollup/dist/bin/rollup" "$@"
|
||||
fi
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\rollup\dist\bin\rollup" %*
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../rollup/dist/bin/rollup" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../rollup/dist/bin/rollup" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../rollup/dist/bin/rollup" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../rollup/dist/bin/rollup" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../semver/bin/semver.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../semver/bin/semver.js" "$@"
|
||||
fi
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\semver\bin\semver.js" %*
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../semver/bin/semver.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../semver/bin/semver.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../semver/bin/semver.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../semver/bin/semver.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../typescript/bin/tsc" "$@"
|
||||
else
|
||||
exec node "$basedir/../typescript/bin/tsc" "$@"
|
||||
fi
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\typescript\bin\tsc" %*
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user