GuessTheSongYear ๐ต
A music quiz party game for Android โ watch a music video, guess its release year, compete with friends on the same device or over LAN.
No accounts. No ads. No tracking. Just music and guessing.
๐ก๏ธ Development environment security verified: 2026-07-28 โ build machine hardened (UFW, fail2ban, tirith, key-only auth pending final step)
๐ฎ How the Game Works
- Pick a difficulty โ Easy (1980โ2010, with hints), Medium (1970โ2024), or Hard (1960โ2025). Harder = more points.
- Watch the video โ A YouTube music video plays and you listen (and watch, unless you toggle listen-only mode).
- Guess the year โ Type a year or scroll through the year picker. The closer you are, the more points you get.
- See the results โ After everyone guesses, the host reveals the answer with a leaderboard.
Exact guess: 50 pts + streak bonus ยท Off by 1: 30 pts ยท Off by โค3: 20 pts ยท Off by โค5: 10 pts ยท Off by โค10: 5 pts ยท Way off: 0 pts, but the right answer stares you in the face.
๐ฅ Multiplayer
Play solo or host a LAN party โ everyone on the same WiFi can join:
| Mode | How it works |
|---|---|
| Host | Press "Vert" (Host). The app shows your IP + a QR code. Friends scan or type it in. |
| Join | Press "Bli med" (Join). It scans your network automatically for active hosts โ no manual typing needed. |
| QR scan | Press the QR button to scan the host's code directly with your camera. |
Discovery is automatic. As soon as someone opens the Join tab, the app probes 192.168.x.2โ254 looking for hosts with a HELLO/ACK handshake. Usually it finds the host before you notice.
Connection Types
- WiFi (TCP on port 8888) โ the default, requires same LAN
- TLS (TCP on port 8889) โ encrypted channel with SPKI certificate pinning, activated when the joiner scans the host's QR code
- Bluetooth RFCOMM โ close-range fallback, no WiFi needed
Messages exchanged between host and joiners are HMAC-SHA256 signed using a per-session random key distributed at join time. Older unsigned clients are still accepted (backward-compatible).
๐ฑ Features
- ๐ต YouTube video via official Iframe Player API โ zero ToS violations, no API keys, no user login
- ๐ต ~40 music videos spanning decades โ from 80s classics to modern hits
- ๐ฏ Three difficulty levels with different year ranges and score multipliers
- ๐ Streak system โ correct guesses build a streak for bonus points, wrong guesses reset it
- ๐น๏ธ Classic and Arcade local multiplayer โ either everyone guesses together or players rotate one turn at a time
- ๐ฅ Remembered party setup โ two to eight named players persist between games
- ๐๏ธ Configurable song distribution โ choose pure random, modern-prioritized, or custom decade weights
- ๐ Persistent player statistics โ review guesses, exact answers, and points from the toolbar
- ๐ Responsive game surfaces โ 16:9 playback and adaptive spacing scale from phones to tablets
- ๐ Listen-only mode โ the video overlay with the video opens and manages YouTube externally
- ๐ Dark theme with amber accent, Material3 design
- ๐ค Norwegian โโ English UI โ switch language from the toolbar
- ๐ซ Duplicate prevention โ songs you've already seen are avoided
- โญ๏ธ Auto-skip โ embed-disabled videos skip silently to the next one
- ๐ฑ Min SDK 24 (Android 7.0) through target SDK 37 (Android 15)
๐๏ธ Architecture
GuessTheSongYear
โโโ MainActivity.kt Entry point, toolbar, difficulty & language switching
โโโ VideoPlayerFragment.kt Core game: WebView YouTube Iframe API, guess engine, scoring
โโโ ScoreManager.kt Points calculation, streaks, accuracy
โโโ Difficulty.kt Easy/Medium/Hard config (year ranges + multipliers)
โโโ GameSessionManager.kt Session state for multiplayer
โโโ HostGameFragment.kt Host UI: IP/QGSR display, player list
โโโ JoinGameFragment.kt Join UI: LAN scan results, QR scanning, manual IP entry
โโโ Protocol.kt TCP message JSON protocol (10 message types)
โโโ HostGameService.kt TCP server on port 8888 + TLS on 8889, HMAC signing
โโโ JoinGameService.kt TCP client, LAN scanner, SPKI pinning, HMAC verify
โโโ SecureChannelManager.kt TLS infrastructure: ephemeral RSA keys, self-signed certs
โโโ ... (layouts, resources, tests)
Security Features
| Layer | Approach |
|---|---|
| Transport | Optional TLS (TLSv1.3) with ephemeral RSA 2048 keys + self-signed X.509 cert per session |
| Certificate pinning | SPKI hash shown on host screen โ clients verify it during TLS join |
| Message signing | HMAC-SHA256 on every message body, key shared in JOIN_ACK |
| Fallback | Plain TCP still available for backwards compatibility; unsigned messages still accepted from old clients |
| CI | contents: write permission scoped to release job only, not workflow level |
๐งฑ Tech Stack
| Layer | Technology |
|---|---|
| Language | Kotlin |
| Build | Gradle + Android Gradle Plugain 9.3.0 + Kotlin 2.4.10 |
| UI | Android ViewBinding + Material3 |
| Video player | YouTube Iframe Player API (WebView embeddings) |
| YouTube API library | com.pierfrancescosoffritti.androidyoutubeplayer:core 12.1.2 |
| QR code | ZXing Core 3.5.3 + ZXing Embedded 4.3.0 |
| Crypto (TLS) | BouncyCastle bcpkix-jdk18on 1.84 + standard javax.net.ssl |
| Crypto (signing) | HMAC-SHA256 via javax.crypto.Mac |
| CI/CD | GitHub Actions (build + test + release on push to master) |
๐ License & Privacy
GuessTheSongYear is open source. The name "GuessTheSongYear", the game loop concept, and all source code in this repository are open source. Contributions are welcome.
Privacy: No accounts. No ads. No tracking. The app uses YouTube Iframe Player API with zero API keys and no user login. Multiplayer traffic stays on your local LAN. Personal data is not collected.
For a detailed security audit, see SECURITY.md in the repository.
๐ฆ Releases
Every push to master auto-builds a new APK through CI, bumps version numbers, and creates a GitHub Release. Find the latest one and side-load it onto your Android device โ the project isn't on the Play Store (it was a weekend side-project).
Repository: https://github.com/turbolego/GuessTheSongYear