diff --git a/.env.example b/.env.example index 9f2eb13..c864d8a 100644 --- a/.env.example +++ b/.env.example @@ -51,6 +51,7 @@ YOUTUBE_REMOTE_LOGIN_ENABLED=false YOUTUBE_OUTBOUND_PROXY_URL= YOUTUBE_REMOTE_LOGIN_INTERNAL_TOKEN=SET_ME_YOUTUBE_REMOTE_LOGIN_INTERNAL_TOKEN YOUTUBE_SESSION_ENCRYPTION_KEY=SET_ME_YOUTUBE_SESSION_ENCRYPTION_KEY +BILIBILI_SESSION_ENCRYPTION_KEY=SET_ME_BILIBILI_SESSION_ENCRYPTION_KEY YOUTUBE_REMOTE_LOGIN_CALLBACK_ORIGIN=http://typetype-server:8080 YOUTUBE_REMOTE_LOGIN_TTL_MS=480000 YOUTUBE_REMOTE_LOGIN_MAX_SESSIONS=2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee8bfea..e8e2317 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ env: jobs: validate-pr: if: github.event_name == 'pull_request' - runs-on: ubuntu-24.04 + runs-on: [self-hosted, Linux, X64, ci-stack] steps: - uses: actions/checkout@v7.0.1 with: diff --git a/AI_TRANSPARENCY.md b/AI_TRANSPARENCY.md new file mode 100644 index 0000000..8efb29f --- /dev/null +++ b/AI_TRANSPARENCY.md @@ -0,0 +1,62 @@ +# AI Transparency + +TypeType is built by humans. AI is a development tool, not an author, maintainer or decision-maker. + +I use AI regularly. Depending on the task, I may use open-weight models in zero-data-retention setups, such as GLM, Qwen or DeepSeek, or hosted models such as GPT-5.6 Luna. + +AI can help with debugging, refactoring, tests, docs, translations, infrastructure and even complete implementations. That is fine. But AI does not own the result. + +## Human responsibility + +For every merged change, a human must be able to answer: + +- what does it do? +- why is it correct? +- how was it tested? +- what could break? +- can it be maintained? + +AI can write 10%, 50% or 100% of the code. That is not the issue. The issue is whether a human reviewed it, validated it and takes responsibility for it. + +## Testing still matters + +AI can produce bugs. Humans can too. + +Every meaningful change needs review, tests and real validation. The amount of AI used does not lower the quality bar. + +Security changes, breaking changes, migration work and user-facing behavior require extra care. + +## Contributor rules + +You may use AI freely, including for complete implementations. + +Your pull request should still explain: + +- what the change does; +- why it is needed; +- how it was tested; +- known risks or limitations. + +Low-effort AI spam is rejected. Mass-generated PRs, untested fixes, fake reports and unreviewed translations are not welcome. + +## Commits and attribution + +I do not want AI systems in commit history. + +Do not add AI tools as author, committer or co-author. Do not add trailers like `Co-authored-by: SomeAI` or `Generated-with: SomeAI`. + +The human who validates the change signs the work. + +If you think an exception makes sense, explain it in the PR or contact me. I am open to discussion, but hidden AI attribution in commit history is not accepted by default. + +## Private data + +Do not send credentials, cookies, tokens, API keys, private keys, account data, third-party personal data or private logs to AI tools. + +If real data is needed to reproduce an issue, reduce it, anonymize it and never publish it. + +## Criticism is welcome + +This document is my current position, not dogma. + +If you think something here is wrong, open an issue, start a discussion or contact me. Explain your case. I prefer honest criticism over silent compliance. diff --git a/Docs-TypeType b/Docs-TypeType index e8183d0..0396e3e 160000 --- a/Docs-TypeType +++ b/Docs-TypeType @@ -1 +1 @@ -Subproject commit e8183d0dc1227737b9c191167f9d4a3195543cf9 +Subproject commit 0396e3e65a966381442d3c0f4cc4b96ec7cbded3 diff --git a/README.md b/README.md index 7530633..5c04a08 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ TypeType

+You want to know the current position of TypeType about AI ? Go check [this](https://github.com/TypeType-Video/TypeType/blob/dev/AI_TRANSPARENCY.md). + +[Roadmap](https://roadmap.typetype.video) · [Git mirror](https://git.typetype.video/TypeType-Video) + # TypeType TypeType is a self-hosted video platform for YouTube, NicoNico, and BiliBili. diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 848aee0..71677e7 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,62 @@ +# TypeType 1.9.0 + +This is a big one. More than two weeks of work went into making playback more reliable, bringing subscription groups to desktop, improving Takeout and self-hosting, and making TypeType’s approach to AI easier to find. Many of these improvements started with community reports and PRs. We’re grateful to everyone who helped make this release happen. + +## AI Transparency + +We've added a public [AI Transparency document](https://github.com/TypeType-Video/TypeType/blob/main/AI_TRANSPARENCY.md) explaining our approach to AI in TypeType development. + +## Playback and Providers + +- Make YouTube web playback SABR-only, while keeping BiliBili and NicoNico playback unchanged. [#168](https://github.com/TypeType-Video/TypeType/issues/168) +- Requeue recoverable live requests in the active playback session instead of leaving playback stuck. +- Improve quality selection and stop showing the unavailable-audio warning on single-language streams. [Frontend #34](https://github.com/TypeType-Video/TypeType-Frontend/pull/34), [Frontend #33](https://github.com/TypeType-Video/TypeType-Frontend/pull/33) +- Decode extractor responses using their declared charset. [Server #87](https://github.com/TypeType-Video/TypeType-Server/pull/87) +- Fix YouTube comments displaying raw HTML. [#266](https://github.com/TypeType-Video/TypeType/issues/266) + +Live playback passed three 30-minute beta runs with no session loss; 16 recoverable requests were requeued in place. + +## Subscriptions and Watch Experience + +- Add desktop management for subscription groups and paginate group members. [Frontend #32](https://github.com/TypeType-Video/TypeType-Frontend/pull/32), [Server #86](https://github.com/TypeType-Video/TypeType-Server/pull/86) +- Show a skeleton while channel avatars load and retry failed image requests without requiring a page refresh. +- Remove the duplicate NicoNico entry from the service picker. +- Fix Shorts not appearing on the initial home screen. [#275](https://github.com/TypeType-Video/TypeType/issues/275) + +## Takeout and Self-Hosting + +- Improve Takeout imports for localized fields, including Spanish, and larger archives. +- Add `BILIBILI_SESSION_ENCRYPTION_KEY` to the Compose and environment templates. [#291](https://github.com/TypeType-Video/TypeType/pull/291) +- Improve self-hosting documentation, including Dockge and Traefik guidance. +- Split the Server into focused Gradle modules and keep tests close to the code they cover. + +## Issues Fixed in This Dev Line + +- [#267](https://github.com/TypeType-Video/TypeType/issues/267): Account registration and login could fail. +- [#271](https://github.com/TypeType-Video/TypeType/issues/271), [#273](https://github.com/TypeType-Video/TypeType/issues/273), [#274](https://github.com/TypeType-Video/TypeType/issues/274): Fix YouTube Remote Login persistence and configuration issues, plus a readiness check. +- [#272](https://github.com/TypeType-Video/TypeType/issues/272): Document reverse-proxy WebSocket headers. +- [#275](https://github.com/TypeType-Video/TypeType/issues/275): Shorts could be missing from the initial home screen. +- [#266](https://github.com/TypeType-Video/TypeType/issues/266): YouTube comments could display raw HTML. + +## Thx + +More than two weeks of work went into this release, and many improvements began with someone taking the time to report a problem, send a PR, test a fix, or help with translations. We truly appreciate the time and care you put into TypeType. + +- @Aquarius-Situla for the playback fixes in [Frontend #33](https://github.com/TypeType-Video/TypeType-Frontend/pull/33) and [#34](https://github.com/TypeType-Video/TypeType-Frontend/pull/34), and the BiliBili session configuration in [#291](https://github.com/TypeType-Video/TypeType/pull/291). +- @kapdon for desktop subscription-group management in [Frontend #32](https://github.com/TypeType-Video/TypeType-Frontend/pull/32) and pagination in [Server #86](https://github.com/TypeType-Video/TypeType-Server/pull/86). +- @tam1m for the charset fix in [Server #87](https://github.com/TypeType-Video/TypeType-Server/pull/87). +- @drJeckyll for reporting the YouTube comments issue in [#266](https://github.com/TypeType-Video/TypeType/issues/266). +- @hsjlyj for the detailed reports in [#271](https://github.com/TypeType-Video/TypeType/issues/271), [#272](https://github.com/TypeType-Video/TypeType/issues/272), [#273](https://github.com/TypeType-Video/TypeType/issues/273), and [#274](https://github.com/TypeType-Video/TypeType/issues/274). +- @therealresonix for reporting the login issue in [#267](https://github.com/TypeType-Video/TypeType/issues/267). +- @uniextra for the Shorts report and Takeout import feedback in [#275](https://github.com/TypeType-Video/TypeType/issues/275) and [#276](https://github.com/TypeType-Video/TypeType/issues/276). +- @Toastienergy for the German translation and beta testing. + +## Updating + +Follow the [update guide](https://typetype-video.github.io/Docs-TypeType/self-hosting/maintenance). + +If necessary, follow the [rollback guide](https://typetype-video.github.io/Docs-TypeType/self-hosting/rollback). + # TypeType 1.8.1 TypeType 1.8.1 focuses on making playback more dependable across YouTube, BiliBili and NicoNico, improving mobile controls, strengthening profiles and notifications, making recommendations service-aware, and simplifying self-hosting. diff --git a/TypeType-Downloader b/TypeType-Downloader index 321a487..d66b414 160000 --- a/TypeType-Downloader +++ b/TypeType-Downloader @@ -1 +1 @@ -Subproject commit 321a4870673313643d531eee4faf44e1820d5d3e +Subproject commit d66b41466eff2cf3ad44c5d723bcee3372b83d3a diff --git a/TypeType-Frontend b/TypeType-Frontend index f19386c..91e8708 160000 --- a/TypeType-Frontend +++ b/TypeType-Frontend @@ -1 +1 @@ -Subproject commit f19386cb44f2cda189e66659afcd9428e76587e0 +Subproject commit 91e8708f927e8b4355bbaeb9b0833872c6210930 diff --git a/TypeType-Player b/TypeType-Player index 8eb0027..4b53351 160000 --- a/TypeType-Player +++ b/TypeType-Player @@ -1 +1 @@ -Subproject commit 8eb0027f5270b78d9cb17657662868e66b71738d +Subproject commit 4b533516ee3692bffe2005113bc2d904be4e5441 diff --git a/TypeType-Server b/TypeType-Server index 945d59a..49e33da 160000 --- a/TypeType-Server +++ b/TypeType-Server @@ -1 +1 @@ -Subproject commit 945d59a25bb8c4ee9a979cd435c21cecb23af40c +Subproject commit 49e33daab748cd8aaaf944c8006d6413d1fbd40a diff --git a/TypeType-Token b/TypeType-Token index b91a2a7..d7e34da 160000 --- a/TypeType-Token +++ b/TypeType-Token @@ -1 +1 @@ -Subproject commit b91a2a7683a8d3839abf4f4aa4f4031189d5cb29 +Subproject commit d7e34da66afdbefa8560b21542d8f2fcca59ebfc diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 7d9bf4f..339c237 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -24,6 +24,15 @@ services: image: ${TYPETYPE_SERVER_BETA_IMAGE:-ghcr.io/typetype-video/typetype-server-beta:latest} ports: - "${HOST_BIND_SERVER_BETA:-127.0.0.1}:${HOST_PORT_SERVER_BETA:-18080}:8080" + mem_limit: 6g + mem_reservation: 4g + entrypoint: + - java + - -XX:+UseContainerSupport + - -XX:MaxRAMPercentage=50.0 + - -XX:+ExitOnOutOfMemoryError + - -jar + - app.jar env_file: .env environment: AUTH_SESSION_TTL_DAYS: ${AUTH_SESSION_TTL_DAYS:-30} @@ -42,6 +51,7 @@ services: YOUTUBE_REMOTE_LOGIN_INTERNAL_TOKEN_FILE: /run/typetype-secrets/youtube_remote_login_internal_token YOUTUBE_SESSION_ENCRYPTION_KEY: "" YOUTUBE_SESSION_ENCRYPTION_KEY_FILE: /run/typetype-secrets/youtube_session_encryption_key + BILIBILI_SESSION_ENCRYPTION_KEY: ${BILIBILI_SESSION_ENCRYPTION_KEY:-} YOUTUBE_REMOTE_LOGIN_TTL_MS: ${YOUTUBE_REMOTE_LOGIN_TTL_MS:-480000} YOUTUBE_REMOTE_LOGIN_MAX_SESSIONS: ${YOUTUBE_REMOTE_LOGIN_MAX_SESSIONS:-2} YOUTUBE_REMOTE_LOGIN_MAX_FRAME_BYTES: ${YOUTUBE_REMOTE_LOGIN_MAX_FRAME_BYTES:-524288} diff --git a/docker-compose.yml b/docker-compose.yml index 7f745e6..1544506 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -41,6 +41,7 @@ services: YOUTUBE_REMOTE_LOGIN_CALLBACK_BASE_URL: ${YOUTUBE_REMOTE_LOGIN_CALLBACK_BASE_URL:-http://typetype-server:8080} YOUTUBE_REMOTE_LOGIN_INTERNAL_TOKEN_FILE: /run/typetype-secrets/youtube_remote_login_internal_token YOUTUBE_SESSION_ENCRYPTION_KEY_FILE: /run/typetype-secrets/youtube_session_encryption_key + BILIBILI_SESSION_ENCRYPTION_KEY: ${BILIBILI_SESSION_ENCRYPTION_KEY:-} YOUTUBE_REMOTE_LOGIN_TTL_MS: ${YOUTUBE_REMOTE_LOGIN_TTL_MS:-480000} YOUTUBE_REMOTE_LOGIN_MAX_SESSIONS: ${YOUTUBE_REMOTE_LOGIN_MAX_SESSIONS:-2} YOUTUBE_REMOTE_LOGIN_MAX_FRAME_BYTES: ${YOUTUBE_REMOTE_LOGIN_MAX_FRAME_BYTES:-524288} diff --git a/scripts/bootstrap-env.sh b/scripts/bootstrap-env.sh index c9fbcd0..460a642 100755 --- a/scripts/bootstrap-env.sh +++ b/scripts/bootstrap-env.sh @@ -7,6 +7,7 @@ ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" REMOTE_LOGIN_PLACEHOLDER="SET_ME_YOUTUBE_REMOTE_LOGIN_INTERNAL_TOKEN" REMOTE_LOGIN_LEGACY_PLACEHOLDER="SET_ME_SHARED_SECRET" SESSION_KEY_PLACEHOLDER="SET_ME_YOUTUBE_SESSION_ENCRYPTION_KEY" +BILIBILI_SESSION_KEY_PLACEHOLDER="SET_ME_BILIBILI_SESSION_ENCRYPTION_KEY" GARAGE_RPC_PLACEHOLDER="SET_ME_GARAGE_RPC_SECRET" GARAGE_RPC_STATIC_DEFAULT="f4db2c1d5aef1dce278d4315b80425e98831714a48c059e22c2a39001b15ca89" @@ -86,4 +87,5 @@ ensure_hex_secret() { ensure_env_file ensure_secret "YOUTUBE_REMOTE_LOGIN_INTERNAL_TOKEN" "${REMOTE_LOGIN_PLACEHOLDER}" ensure_secret "YOUTUBE_SESSION_ENCRYPTION_KEY" "${SESSION_KEY_PLACEHOLDER}" +ensure_secret "BILIBILI_SESSION_ENCRYPTION_KEY" "${BILIBILI_SESSION_KEY_PLACEHOLDER}" ensure_hex_secret "GARAGE_RPC_SECRET" "${GARAGE_RPC_PLACEHOLDER}" diff --git a/scripts/deploy-beta.sh b/scripts/deploy-beta.sh index 72ce7ca..6e047d7 100755 --- a/scripts/deploy-beta.sh +++ b/scripts/deploy-beta.sh @@ -9,7 +9,7 @@ stage=locate-stack trap 'printf "beta deployment failed during %s\n" "$stage" >&2' ERR project= anchor= -for candidate in typetype-beta typetype-beta-stack; do +for candidate in typetype-beta-stack typetype-beta; do anchor=$(docker ps -a -q \ --filter "label=com.docker.compose.project=${candidate}" | head -n 1) if [[ -n "$anchor" ]]; then diff --git a/scripts/deploy-stable.sh b/scripts/deploy-stable.sh index 958c28d..bf79e4e 100755 --- a/scripts/deploy-stable.sh +++ b/scripts/deploy-stable.sh @@ -98,8 +98,6 @@ finish() { if [[ "$succeeded" == true ]]; then printf 'succeeded\n' > "$backup/status" ln -sfn "$(basename "$backup")" "$rollback_root/last-successful" - find "$rollback_root" -mindepth 1 -maxdepth 1 -type d -printf '%T@ %p\n' \ - | sort -nr | tail -n +6 | cut -d' ' -f2- | xargs -r rm -rf exit "$status" fi diff --git a/scripts/deploy-stable.test.sh b/scripts/deploy-stable.test.sh new file mode 100755 index 0000000..3ed2b2b --- /dev/null +++ b/scripts/deploy-stable.test.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +set -euo pipefail + +repository="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +temporary="$(mktemp -d)" +trap 'rm -rf "$temporary"' EXIT + +source_root="$temporary/source" +stack="$temporary/stack" +fake_bin="$temporary/bin" +mkdir -p "$source_root/scripts" "$stack/scripts" "$fake_bin" + +for file in \ + .env.example \ + docker-compose.yml \ + docker-compose.arm64.yml \ + scripts/bootstrap-env.sh \ + scripts/bootstrap-garage.sh \ + scripts/initialize-stack.sh \ + scripts/run-stack-init.sh \ + scripts/deploy-stable.sh; do + mkdir -p "$source_root/$(dirname "$file")" "$stack/$(dirname "$file")" + cp "$repository/$file" "$source_root/$file" + cp "$repository/$file" "$stack/$file" +done +printf 'POSTGRES_PASSWORD=test\n' > "$stack/.env" +for script in bootstrap-env.sh bootstrap-garage.sh initialize-stack.sh run-stack-init.sh; do + printf '#!/usr/bin/env bash\nexit 0\n' > "$source_root/scripts/$script" +done + +rollback_root="$stack/.deploy-rollbacks" +mkdir -p "$rollback_root" +for index in {1..6}; do + mkdir -p "$rollback_root/old-$index" + printf 'existing snapshot\n' > "$rollback_root/old-$index/status" +done + +cat > "$fake_bin/docker" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +case "$1" in + ps) + if [[ " $* " == *" -q "* ]]; then + echo server-container + fi + ;; + inspect) + if [[ "$2" == server-container ]]; then + echo "$FAKE_STACK_ROOT" + else + echo sha256:old + fi + ;; + compose) + case " $* " in + *" config --services "*) + printf '%s\n' typetype typetype-server postgres dragonfly garage + ;; + *" ps -a -q "*) + printf 'container-%s\n' "${*: -1}" + ;; + *" exec -T postgres sh -ec "*) + command="${*: -1}" + if [[ "$command" == *"SELECT 1 FROM pg_database"* ]]; then + printf '0\n' + elif [[ "$command" == *"pg_dump "* ]]; then + printf 'fake database dump\n' + fi + ;; + esac + ;; +esac +EOF +chmod +x "$fake_bin/docker" + +PATH="$fake_bin:$PATH" \ +FAKE_STACK_ROOT="$stack" \ + "$repository/scripts/deploy-stable.sh" "$source_root" + +for index in {1..6}; do + if [[ ! -d "$rollback_root/old-$index" ]]; then + echo "successful deployment removed existing rollback snapshot old-$index" >&2 + exit 1 + fi +done +snapshot_count="$(find "$rollback_root" -mindepth 1 -maxdepth 1 -type d | wc -l)" +if [[ "$snapshot_count" -ne 7 ]]; then + echo "expected six existing snapshots and one new snapshot, got $snapshot_count" >&2 + exit 1 +fi diff --git a/scripts/install-stack.test.sh b/scripts/install-stack.test.sh index 38243b5..2fbcab5 100755 --- a/scripts/install-stack.test.sh +++ b/scripts/install-stack.test.sh @@ -58,9 +58,11 @@ test ! -e "$python_log" downloader_access_key="$(grep '^DOWNLOADER_S3_ACCESS_KEY=' "${install_dir}/.env" | cut -d= -f2-)" downloader_secret_key="$(grep '^DOWNLOADER_S3_SECRET_KEY=' "${install_dir}/.env" | cut -d= -f2-)" remote_login_token="$(grep '^YOUTUBE_REMOTE_LOGIN_INTERNAL_TOKEN=' "${install_dir}/.env" | cut -d= -f2-)" +bilibili_session_key="$(grep '^BILIBILI_SESSION_ENCRYPTION_KEY=' "${install_dir}/.env" | cut -d= -f2-)" [[ "$downloader_access_key" =~ ^GK[0-9a-f]{24}$ ]] [[ "$downloader_secret_key" =~ ^[0-9a-f]{64}$ ]] [[ "$remote_login_token" =~ ^[A-Za-z0-9_-]{64}$ ]] +[[ "$bilibili_session_key" =~ ^[A-Za-z0-9_-]{64}$ ]] beta_install_dir="${temporary}/beta-stack" PATH="${fake_bin}:${PATH}" \ diff --git a/scripts/validate-stack.sh b/scripts/validate-stack.sh index df50760..09eb9f9 100755 --- a/scripts/validate-stack.sh +++ b/scripts/validate-stack.sh @@ -21,6 +21,7 @@ for script in scripts/*.sh; do done ./scripts/install-stack.test.sh ./scripts/deploy-beta.test.sh +./scripts/deploy-stable.test.sh ./scripts/run-stack-init.test.sh docker compose --env-file .env.example -f docker-compose.yml config -q