This document provides comprehensive guidance for AI assistants working on the Kiris/Phate Radio codebase.
Phate Radio (Kiris) is an internet radio streaming platform specializing in anime, games, and Japanese pop music. The application manages a music library primarily sourced from NicoNico Douga (Japanese video sharing platform) and integrates with utaitedb.net for track metadata.
Core Features:
- Live streaming radio with Icecast integration
- Track database with rich metadata (artist, album, tags, lyrics)
- User-submitted artwork/images from Pixiv
- Community features (comments, issues/feedback)
- Multi-language support (English, Japanese, Simplified/Traditional Chinese)
- Content moderation workflow (QUEUED → reviewed → OK/DELETED)
- Bridge API for streaming server integration
License: MIT
IMPORTANT: Before making any code changes, please refer to CONTRIBUTING.md for:
- Code standards and style guidelines (RuboCop, ESLint, etc.)
- Commit message conventions (Conventional Commits)
- Pre-commit workflow and linter requirements
- Language and documentation standards
The CONTRIBUTING.md file is the source of truth for all contribution standards and must be followed for all code changes.
- Ruby: 2.5.9 (defined in Gemfile, note: .ruby-version shows 2.5.8 but Gemfile uses 2.5.9)
- Rails: 4.2.5
- Database: PostgreSQL (with structure.sql format, not schema.rb)
- Cache: Redis (production only)
- Web Server: Puma (2 workers, 5 threads default)
- Templates: Slim (slim-rails)
- Forms: SimpleForm with Foundation integration
- Authentication: Custom session-based (no Devise)
- Search: Ransack
- Markdown: Redcarpet with CodeRay syntax highlighting
- HTTP Client: HTTParty
- Pagination: Kaminari
- Frontend: Foundation framework, Font Awesome, jQuery
- Asset Gems: jPlayer (HTML5 audio), NProgress, Intro.js
- Cloud Storage: fog-google (Google Cloud Platform)
- CORS: rack-cors
- NicoNico Douga: Video/music source
- utaitedb.net: Track metadata API
- Pixiv: Artwork sourcing
- Amazon Product API: ASIN-based track import
- Imgur: Image CDN
- Icecast: Streaming server
- Xiph YP Directory: Stream listing
kiris/
├── app/
│ ├── assets/
│ │ ├── javascripts/ # CoffeeScript files (player, timer, danmaku, pjax, chatroom)
│ │ ├── stylesheets/ # Sass/SCSS with Foundation framework
│ │ └── images/
│ ├── controllers/
│ │ ├── concerns/ # CacheLock, CodeRayify, Streammeta, YPDirectory
│ │ ├── admin/ # Admin namespace (tracks, images, playlist, migrations, notices)
│ │ ├── upload/ # Upload namespace (asin, niconico)
│ │ ├── bridge/ # Bridge API (playlist, tracks, caches)
│ │ ├── json/ # JSON API (playlist, status, request)
│ │ ├── tracks/ # Nested (comments, images, lyrics)
│ │ └── images/ # Nested (comments)
│ ├── models/
│ │ ├── concerns/ # SharedMethods (value normalization)
│ │ ├── track.rb # Central model
│ │ ├── image.rb # Artwork/covers
│ │ ├── playlist.rb # Current/queued tracks
│ │ ├── history.rb # Play history
│ │ ├── track_migration.rb # Staging for track changes
│ │ ├── issue.rb # Feedback system
│ │ ├── catalog.rb # Wiki with versioning
│ │ ├── member.rb # User authentication
│ │ └── [comments, lyric, category, notice].rb
│ ├── views/ # Slim templates organized by controller
│ ├── helpers/
│ └── mailers/
├── config/
│ ├── routes.rb # Routing with subdomain constraints
│ ├── application.rb # Main app config
│ ├── database.yml # PostgreSQL config
│ ├── puma.rb # Web server config
│ ├── initializers/ # Redis, session, environment variables, etc.
│ ├── locales/ # i18n files (en, ja, zh-Hans, zh-Hant)
│ └── environments/
├── db/
│ ├── migrate/ # Database migrations
│ ├── structure.sql # PostgreSQL schema (NOT schema.rb)
│ └── seeds.rb
├── lib/
│ └── tasks/ # Rake tasks (tracks.rake, images.rake)
├── test/ # Minitest suite
│ ├── controllers/
│ ├── models/
│ ├── fixtures/
│ ├── helpers/
│ ├── integration/
│ └── mailers/
├── public/
├── vendor/
├── .github/
│ └── workflows/ # Gemini CLI workflows
├── Gemfile
├── Procfile # Heroku deployment
├── .rubocop.yml # Code style rules
└── .travis.yml # Travis CI config
- Location:
app/models/track.rb - Key Fields: title, artist, album, tags, duration, szhash (file hash), niconico (video ID), asin (Amazon ID), status, uploader
- Relationships:
has_many :playlists(play history)has_many :histories(broadcast history)has_many :images(cover art)has_many :track_commentshas_one :lyric
- Important Scopes: requestable, utaitedb, niconico_tracks, utattemita (cover songs)
- Status Values: QUEUED, OK, DELETED
- Location:
app/models/image.rb - Key Fields: url, source, illustrator, status, verified, rate
- Validates: URLs from Imgur, sources from Pixiv/Piapro/NicoSeiga
- Relationships:
belongs_to :track,has_many :image_comments - Rating System: RANK_1 through RANK_5, RANK_BAKA
- Location:
app/models/playlist.rb - Purpose: Manages current and queued tracks
- Key Fields: playedtime, track_id, nickname, userip
- Note: Uses pessimistic locking for updates
- Location:
app/models/history.rb - Purpose: Play history (cleaned up after 30 days)
- Relationships:
belongs_to :track
- Location:
app/models/track_migration.rb - Purpose: Staging area for reviewing track changes before committing
- Workflow: Create → Review → Transfer to Track model
- Location:
app/models/member.rb - Authentication: Username, password hash, identity, access level
- Note: Custom authentication (no Devise)
- Catalog: Wiki-style content with parent/child revision tracking
- Issue/IssueReply: Community feedback system
- Lyric: Song lyrics (
belongs_to :track) - Comments: TrackComment, ImageComment, TrackMigrationComment (all support editability by IP/identity)
- Category, Notice: Simple categorization and announcements
- Uses structure.sql instead of schema.rb for PostgreSQL-specific features
- Counter caches for performance (images_count, track_comments_count, etc.)
- Status-based workflows throughout
- Comprehensive indexes and foreign keys
Subdomain Constraints:
api.phate.io→ JSON format endpointsgitio.phate.io→ Git.io proxy service
Custom Routing Pattern: Admin resources use non-standard REST:
- POST to 'new' action instead of create
- PATCH to 'edit' action instead of update
Example:
resources :tracks, except: [:create, :update] do
post 'new' => 'tracks#create', on: :collection
patch 'edit' => 'tracks#update', on: :member
end- DefaultController: Home, FAQ, chat, preferences, support pages
- ListenController: Redirects to streaming servers
- SearchController: Uses Ransack for searching (random, history, latest)
- TracksController: Individual track details
- ImagesController: Image galleries
- IssuesController: Community feedback
- CatalogsController: Wiki with diff viewing
- NoticesController: News/announcements
- MembersController: Login/logout
- StaticController: robots.txt, manifest, proxy services (imgur, gitio)
- ErrorsController: Custom error pages
- Requires: Authentication (access level >= 5)
- Admin::TracksController: CRUD, review/confirm workflow
- Admin::ImagesController: Management, import/export
- Admin::PlaylistController: Playlist management
- Admin::TrackMigrationsController: Migration workflow
- Admin::NoticesController: Notice management
- Upload::AsinController: Add tracks via Amazon ASIN
- Upload::NiconicoController: Add tracks from NicoNico Douga
- Purpose: Streaming server integration
- Authentication: Protected by BRIDGE_SECRET_KEY
- Bridge::PlaylistController: Updates now playing, generates playlists, YP Directory integration
- Bridge::TracksController: Track data API
- Bridge::CachesController: Cache management
- Json::PlaylistController: Public playlist JSON/XML with caching
- Json::StatusController: Server status
- Json::RequestController: Song requests
Location: app/controllers/concerns/
- CacheLock: Redis-based locking mechanism to prevent race conditions
- CodeRayify: Syntax highlighting for markdown code blocks
- Streammeta: Integer#abbrtime extension for duration formatting
- YPDirectory: Xiph directory listing integration
Location: app/controllers/application_controller.rb
Key Functionality:
- Locale detection (session or Accept-Language header)
- Timezone detection
- Client tracking (IP, user agent, forwarded IPs)
- Session-based authentication (
authenticate!method) - Access level checking (5 = admin)
- Markdown rendering with CodeRay
- HTTP request helpers
- CSRF protection
Important Methods:
set_locale- Automatic locale detectionauthenticate!- Check if logged inclient_ip,client_identity- Track usersmd_to_html- Markdown rendering
-
Install Dependencies:
gem install bundler --no-document gem install rails --no-document bundle install
-
Database Setup:
bundle exec rake db:create bundle exec rake db:migrate bundle exec rake db:seed
-
Start Server:
rails server # or for production-like: bundle exec puma -C config/puma.rb
-
Access Application:
- Development: http://localhost:3000
Framework: Minitest (Rails default)
Run Tests:
bundle exec rake db:test:prepare
bundle exec rake testTest Helper:
- Located at
test/test_helper.rb - Helper:
authenticate_member(sets session[:access] = 5 for admin access)
CI/CD:
- Travis CI configured (.travis.yml)
- GitHub Actions with Gemini CLI workflows
- Code Climate for quality and coverage
RuboCop Configuration (.rubocop.yml):
- Rails cops enabled
- Documentation disabled
- Max line length: 120 characters
- Class/module nesting style disabled
- Excludes: db/, config/, script/
Run Linter:
bundle exec rubocopbundle exec rake tracks:create_or_update_by_utaitedb- Fetches songs with >10,000 views
- Creates tracks with status QUEUED
- Updates existing tracks
bundle exec rake images:create_or_update_from_pixiv- Searches Pixiv for track artwork
- Tag matching algorithm
- Uploads to Google Cloud Storage
- Filters age-restricted content
# Reset database
bundle exec rake db:reset
# Load structure
bundle exec rake db:structure:load
# Dump structure (after migrations)
bundle exec rake db:structure:dump- Import/Create: Track created with status QUEUED
- Review: Admin reviews at
/admin/tracks/:id/review - Confirm: Admin confirms, status changes to OK
- Delete: Admin can mark as DELETED
- Create Migration: User proposes changes via TrackMigration
- Review: Community/admin reviews
- Migrate: Admin transfers changes to actual Track
- Cleanup: Migration record removed
- Upload: User submits image URL (Imgur) with source (Pixiv/etc)
- Verification: Admin verifies and rates
- Publish: Image marked as verified
- Frozen String Literals: Use
# frozen_string_literal: trueat top of files - Strong Parameters: Always use
.permit!or explicit whitelisting - Before Actions: Use for common operations (set_locale, authenticate!)
- Concerns: Extract shared functionality into concerns
- Scopes: Prefer scopes over class methods in models
- Counter Caches: Use for performance on associations with counts
- Models: Singular, CamelCase (Track, Image, Playlist)
- Controllers: Plural, CamelCase with Controller suffix
- Views: Organized by controller name, use Slim templates
- Helpers: Match controller names
- Database Tables: Plural, snake_case
# In controller
before_action :authenticate!
# Sets these variables:
@identity # Client identity (session or anonymous)
@access # Access level (5 = admin, nil = guest)# Available in ApplicationController
client_ip # User's IP address (handles proxies)
client_agent # User agent string
client_identity # Identity for tracking (uses session)Track Status:
nilorQUEUED→ Under reviewOK→ PublishedDELETED→ Removed
Image Status:
nil→ Pending verificationverified: true→ Published
# In views/controllers
md_to_html(text) # Converts markdown to HTML with syntax highlighting# Use CacheLock concern for preventing race conditions
include CacheLock
cache_lock('unique_key') do
# Critical section
end# Bridge controllers check BRIDGE_SECRET_KEY
params[:key] == ENV['BRIDGE_SECRET_KEY']Required in Production:
DATABASE_URL- PostgreSQL connection stringBRIDGE_SECRET_KEY- Bridge API authenticationDANMAKU_SECRET_KEY- Danmaku service authenticationREDIS_URL- Redis connection (for caching)
Optional:
ICECAST_SERVER- Primary Icecast server URLICECAST_RELAYS- Comma-separated relay serversSTATIC_SERVER_URL- CDN/static file serverPIXIV_AUTHORIZATION- Pixiv API tokenOFFLINE_TRACK_ID- Track to show when offlineWEB_CONCURRENCY- Puma worker count (default: 2, set to 0 in dev)MAX_THREADS- Puma thread count (default: 5, set to 3 in dev)
Development (.env file):
WEB_CONCURRENCY=0
MAX_THREADS=3
Platform: Primary deployment target is Heroku
Procfile:
web: bundle exec puma -C config/puma.rb
Production Gems:
- rails_12factor - Heroku integration (logging, static assets)
- lograge - Structured logging
- cloudflare-rails - CloudFlare IP filtering
Database:
- Uses DATABASE_URL environment variable
- PostgreSQL with structure.sql
Buildpacks:
- Ruby buildpack (detects Ruby version from Gemfile)
Configuration:
- Set all required environment variables in Heroku config
- Enable Redis add-on for caching
- Configure DATABASE_URL (automatic with Postgres add-on)
-
Precompile Assets:
RAILS_ENV=production bundle exec rake assets:precompile -
Database Migration:
RAILS_ENV=production bundle exec rake db:migrate -
Start Server:
bundle exec puma -C config/puma.rb
- Always use structure.sql: This project uses PostgreSQL-specific features; never convert to schema.rb
- Follow custom REST patterns: Admin routes use POST to 'new' and PATCH to 'edit'
- Use Slim templates: Views are in Slim, not ERB
- Respect status workflows: Tracks go through QUEUED → OK/DELETED states
- Use counter caches: Don't manually count associations; use counter_cache columns
- Include frozen_string_literal: Add
# frozen_string_literal: trueto new Ruby files - Follow RuboCop rules: Max 120 char lines, no documentation requirement
- Test authentication: Use
authenticate_memberhelper in tests - Use concerns: Extract shared logic into concerns, not modules
- Don't use Devise: This project has custom authentication
- Don't use schema.rb: Use structure.sql for database schema
- Don't bypass status checks: Always respect Track/Image status fields
- Don't skip authentication: Admin actions require access level >= 5
- Don't use ERB: Templates are Slim format
- Don't ignore IP tracking: Many features depend on client_ip/client_identity
- Don't remove frozen_string_literal: It's a project standard
- Don't create standard REST routes for admin: Follow the custom pattern
- Don't bypass CacheLock: Use it for playlist updates and critical sections
- Database Schema: Modifying structure.sql manually is tricky; prefer migrations
- Custom Routes: Don't expect standard REST routes in admin namespace
- Authentication: Session-based, not token-based; stored in session[:access]
- Slim Syntax: Different from ERB;
=outputs,-doesn't,==outputs without escaping - PostgreSQL Features: Don't use MySQL-specific features; this is PostgreSQL only
- Counter Caches: Must be manually updated if bypassing ActiveRecord
- NicoNico Douga: May require authentication; video IDs stored in
niconicofield - utaitedb.net: API may have rate limits; rake task handles this
- Pixiv: Requires PIXIV_AUTHORIZATION token; may change API
- Imgur: Used for image hosting; URLs validated in Image model
- Icecast: Streaming server; Bridge API integrates with it
- Supported Locales: en, ja, zh-Hans, zh-Hant
- Locale Detection: Automatic from Accept-Language header
- Session Override: User can set preferred locale
- Translation Service: Uses Locale app (localeapp.com)
- Chinese Variants: Distinguish between Simplified (Hans) and Traditional (Hant)
- Redis Caching: Only in production; development uses memory
- Counter Caches: Used extensively; don't count manually
- N+1 Queries: Use includes/joins for associations
- Playlist Locking: Uses pessimistic locking to prevent race conditions
- History Cleanup: Histories older than 30 days are purged
- Asset Pipeline: Precompile assets for production
- Routes:
config/routes.rb - Application Config:
config/application.rb - Database Config:
config/database.yml - Environment Variables:
config/initializers/environment_variables.rb - Puma Config:
config/puma.rb - Redis Config:
config/initializers/redis.rb
- Track:
app/models/track.rb - Image:
app/models/image.rb - Playlist:
app/models/playlist.rb - Member:
app/models/member.rb
- Application:
app/controllers/application_controller.rb - Bridge Playlist:
app/controllers/bridge/playlist_controller.rb - Admin Tracks:
app/controllers/admin/tracks_controller.rb
- Track Import:
lib/tasks/tracks.rake - Image Import:
lib/tasks/images.rake
- RuboCop:
.rubocop.yml - Travis CI:
.travis.yml - Gemfile:
Gemfile(dependencies) - Procfile:
Procfile(Heroku)
- Repository: https://github.com/phateio/kiris
- License: MIT (see LICENSE file)
- Issue Tracker: GitHub Issues
- Translation: https://www.localeapp.com/projects/6196
- CI Status: https://travis-ci.org/phateio/kiris
- Code Climate: https://codeclimate.com/github/phateio/kiris
Last Updated: 2025-11-15
This document should be updated whenever significant architectural changes are made to the codebase.