diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..bf1c9e7 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,434 @@ +# AGENTS.md + + +## AGENTS.md Specific Instructions + +This file provides guidance to AI Agents when working with code in this repository. + +## Development Guidelines + +1. **Testing**: + - Always add unit tests for backend changes. + - Frontend changes should have tests if possible. + +2. **Code Style & Architecture**: + - **DRY (Don't Repeat Yourself)**: Codebase must follow DRY principle. + - **OOP (Object-Oriented Programming)**: Required for all backend code. + +3. **Refactoring**: + - You are authorized to refactor code to align with DRY/OOP principles. + - **Permission Required**: You MUST ask for user permission before significant refactoring. + +4. **Localization (i18n)**: + - Update translation files if there are changes to UI text or console messages. + +5. **Documentation**: + - Always update `README.md` and all agent instruction files when making changes. + - The contents of all agent instruction files should be identical except for the `Specific Instructions` section. Any agent-specific instructions must be added to that section. + +## Project Overview + +Twitch Drops Miner is a Python application that automatically mines timed Twitch drops without downloading stream data. It uses Twitch's GraphQL API and websocket connections to simulate watching streams while tracking drop progress. + +**Key Characteristics:** + +- Python 3.12+ required +- Web-based GUI using FastAPI and Socket.IO +- Async/await architecture with asyncio +- Session persistence via cookies +- No stream video/audio download (bandwidth-efficient) +- Docker-ready for easy deployment + +## Architecture + + +The application now uses a clean `src/` package structure with clear separation of concerns. + +### Project Structure + +```text +src/ +├── models/ # Domain models (Game, Channel, Campaign, Drop, Benefit) +├── config/ # Configuration (constants, paths, operations, settings, client_info) +├── utils/ # Pure utilities (string, JSON, async helpers, rate_limiter, backoff) +├── i18n/ # Translation system (Translator class, TypedDict schemas) +├── auth/ # Authentication (auth_state for OAuth and token management) +├── api/ # External API (HTTP client, GraphQL client) +├── websocket/ # Real-time updates (websocket connection, pool) +├── web/ # Web GUI (app, gui_manager, api/) +│ └── managers/ # Individual UI managers (status, console, channels, campaigns, inventory, login, settings, cache, broadcaster) +├── services/ # Business logic services (channel, inventory, watch, maintenance, message_handlers) +├── core/ # Core client (Twitch client) +├── exceptions.py # Custom exceptions +├── version.py # Version string +└── __main__.py # Entry point + +lang/ # Translation JSON files (19 languages) +├── English.json # Default/fallback translations +├── Español.json +├── Français.json +├── Deutsch.json +└── ... # 15 more languages +``` + +### Core Components + +**main.py** - Simple launcher: + +- Runs the `src` package as a module using `runpy.run_module("src")` +- All application logic is now in `src/__main__.py` + +**src/__main__.py** - Entry point: + +- Parses command-line arguments +- Initializes Settings, Twitch client, and WebGUIManager +- Starts the FastAPI web server (uvicorn on port 8080) +- Runs the main asyncio event loop +- Handles signals (SIGINT, SIGTERM on Linux) and exit codes + +**src/core/client.py** - Central client (`Twitch` class): + +- State machine: IDLE, INVENTORY_FETCH, GAMES_UPDATE, CHANNELS_CLEANUP, CHANNELS_FETCH, CHANNEL_SWITCH, EXIT +- Composes `_AuthState`, `HTTPClient`, and `GQLClient` +- Delegates to service layer for business logic +- Drop progress monitoring via periodic "watch" payloads +- Manages WebsocketPool and maintenance tasks + +**src/services/** - Business logic layer (fully implemented): + +- `ChannelService`: Channel management and selection logic +- `InventoryService`: Campaign and drop inventory operations +- `WatchService`: Drop mining watch payload logic +- `MaintenanceService`: Periodic maintenance tasks +- `MessageHandlerService`: Websocket message routing and handling + + +**src/models/channel.py** - Channel and Stream: + +- `Channel` class: Twitch channel with online/offline status +- `Stream` class: Active stream with game, viewers, drop status +- Stream URL fetching and validation +- ACL-based vs directory channels + +**src/models/campaign.py** - Drop campaigns: + +- `DropsCampaign`: Campaign with game, timeframe, allowed channels +- Time-based eligibility and progress tracking + +**src/models/drop.py** - Drop types: + +- `TimedDrop`: Drops with minute requirements and progress +- `BaseDrop`: Base class with claim logic +- Precondition chains for sequential drops + +**src/web/gui_manager.py** - Web GUI: + +- `WebGUIManager`: Main GUI coordinator +- Composes individual managers for different UI concerns (status, console, channels, campaigns, inventory, login, settings, cache) +- Uses `WebSocketBroadcaster` for real-time Socket.IO updates +- Pure asyncio, no tkinter dependency + +**src/web/app.py** - FastAPI application: + +- REST API endpoints: `/api/status`, `/api/channels`, `/api/campaigns`, `/api/settings`, `/api/login`, `/api/oauth/confirm`, `/api/reload`, `/api/close`, `/api/version` +- Socket.IO server for real-time bi-directional communication +- Serves static web frontend from `web/` directory +- Integrates with WebGUIManager via `set_managers()` + +**src/websocket/pool.py** - WebSocket management: + +- Sharded connections (up to 50 topics per socket, max 199 channels) +- Topics: User.Drops, User.Notifications, Channel.StreamState, Channel.StreamUpdate +- Automatic reconnection with exponential backoff +- Message routing to registered callbacks + +**src/config/settings.py** - Application settings: + +- Games to watch list (auto-populated from available campaigns if empty) +- Connection quality multiplier +- Language selection +- Proxy support (including verification) +- Logging and dump flags from command-line arguments +- Persistence to JSON file (`settings.json`) in DATA_DIR +- Inventory filters (Status, Benefit Type, Game Search) + +### State Machine Flow + +1. **IDLE** - Waiting for campaigns or user action +2. **INVENTORY_FETCH** - Fetch campaigns from GraphQL, claim completed drops +3. **GAMES_UPDATE** - Determine wanted games based on priority/exclude lists +4. **CHANNELS_CLEANUP** - Remove channels not streaming wanted games +5. **CHANNELS_FETCH** - Discover channels via ACL lists or game directories +6. **CHANNEL_SWITCH** - Select best channel to watch based on priority/ACL +7. Loop between CHANNEL_SWITCH and periodic INVENTORY_FETCH (hourly) + +### Authentication + +- Uses OAuth device code flow (user enters code at twitch.tv/activate) +- Managed by `src/auth/auth_state.py` (`_AuthState` class) +- Access tokens stored in `cookies.jar` in DATA_DIR +- Device ID from Twitch's `unique_id` cookie +- Session ID generated per run +- Client info defined in `src/config/client_info.py` (presents as Android app with Client-Id and User-Agent spoofing) + +### Drop Mining Mechanism + +The application sends periodic "watch" payloads to a spade URL every ~20 seconds: + +- Payload contains minute-watched events with channel/broadcast IDs +- Twitch reports progress via websocket (User.Drops topic) +- If websocket updates stop, fallback to GQL CurrentDrop query +- Extrapolation via "bump minutes" when no updates received + +### GraphQL Operations + +Defined in `src/config/operations.py` as `GQL_OPERATIONS`: + +- **Inventory** - Fetch in-progress campaigns and claimed benefits +- **Campaigns** - List available active/upcoming campaigns +- **CampaignDetails** - Detailed drop info for a campaign +- **GameDirectory** - Find live streams for a game with drops enabled +- **GetStreamInfo** - Check if channel is online and get stream details +- **CurrentDrop** - Query currently mined drop progress +- **ClaimDrop** - Claim a completed drop +- **AvailableDrops** - Check which campaigns a channel qualifies for (badge validation) +- **NotificationsDelete** - Delete Twitch notifications + +### Channel Selection Priority + +1. Selected channel (if user clicked one) +2. ACL-based channels over directory channels +3. Game priority order (from settings) +4. Viewer count (descending) +5. Maximum 199 channels tracked simultaneously + +### Maintenance Task + +Runs in background to trigger: + +- Channel cleanup when drops start/end (based on time_triggers) +- Inventory reload every ~60 minutes + +### Translation System + +**Architecture:** + +- All translations stored as JSON files in `lang/` directory (19 languages supported) +- English (`lang/English.json`) is the single source of truth and fallback language +- Strongly typed with TypedDict schema defined in `src/i18n/translator.py` +- Translator class (`src/i18n/translator.py`) handles language loading and fallback +- Singleton instance `_` available via `from src.i18n import _` + +**Supported Languages:** + +- English, Dansk (Danish), Deutsch (German), Español (Spanish), Français (French) +- Indonesian, Italiano (Italian), Nederlandse (Dutch), Polski (Polish), Português (Portuguese) +- Română (Romanian), Türkçe (Turkish), Čeština (Czech) +- Русский (Russian), Українська (Ukrainian), العربية (Arabic) +- 日本語 (Japanese), 简体中文 (Simplified Chinese), 繁體中文 (Traditional Chinese) + +**Translation Structure:** + +```python +Translation = { + "language_name": str, # Display name of language + "english_name": str, # English name of language + "status": StatusMessages, # Console status messages + "login": LoginMessages, # Login-related messages + "error": ErrorMessages, # Error messages + "gui": GUIMessages # All web GUI text (tabs, settings, help, etc.) +} +``` + +**Usage:** + +```python +from src.i18n import _ + +# Access translations +status_text = _.t["gui"]["status"]["idle"] # Returns "Idle" +login_text = _.t["login"]["status"]["logged_in"] # Returns "Logged in" +``` + +**Language Persistence:** + +- Language selection persisted in `settings.json` (DATA_DIR) +- Dynamic language switching supported in web GUI +- Changes take effect immediately without restart + +## Key Files + +- **src/config/constants.py** - Core enums (State, WebsocketTopic), logging config, type aliases +- **src/config/operations.py** - GraphQL operation definitions (GQL_OPERATIONS) +- **src/config/paths.py** - Path management and Docker environment detection +- **src/config/client_info.py** - Twitch client info (Client-Id, User-Agent) +- **src/config/settings.py** - Application settings with JSON persistence +- **src/exceptions.py** - Custom exceptions (MinerException, ExitRequest, RequestException, RequestInvalid, WebsocketClosed, LoginException, CaptchaRequired, GQLException) +- **src/utils/** - Helper utilities (string_utils, json_utils, async_helpers, rate_limiter, backoff) +- **src/i18n/** - Internationalization package with TypedDict schema and Translator class + - **translator.py** - Translator class with typed translation schema (Translation TypedDict) + - **__init__.py** - Exports translation types and `_` (Translator instance) +- **lang/** - Translation JSON files for 19 languages (English.json is the single source of truth) +- **src/version.py** - Version string +- **src/web/app.py** - FastAPI application with REST API and Socket.IO +- **src/web/managers/cache.py** - ImageCache for campaign artwork caching +- **web/** - Frontend assets (index.html, static/app.js, static/styles.css) + +## Development Commands + +**IMPORTANT: Always activate the virtual environment first!** + +The project uses a virtual environment located at `env/`. All Python commands must be run within this environment: + +```bash +# Activate the virtual environment (required before any Python commands) +source env/bin/activate +``` + +### Running the Application + +```bash +# Run from source (remember to activate venv first!) +source env/bin/activate && python main.py + +# With verbose logging (stackable: -vv, -vvv) +source env/bin/activate && python main.py -v + +# Create data dump for debugging +source env/bin/activate && python main.py --dump + +# Access the web interface at http://localhost:8080 +``` + +### Development Setup + +The application requires: + +- Python 3.12+ +- Virtual environment at `env/` (must be activated before running commands) +- Dependencies from `pyproject.toml` (includes FastAPI, uvicorn, Socket.IO) + +Docker deployment: + +```bash +# Build and run with docker-compose +docker-compose up -d + +# Access at http://localhost:8080 +``` + +## Testing + +### Automated Tests + +The project includes a test suite in the `tests/` directory: + +```bash +# Activate virtual environment and run tests +source env/bin/activate && python -m pytest tests/ +``` + +**Test Files:** + +- `tests/test_proxy_settings.py` - Tests for proxy settings configuration +- `tests/test_verify_proxy.py` - Tests for proxy verification functionality + + +### Manual Testing + +1. Run with `-vvv` for maximum verbosity (levels: -v, -vv, -vvv, -vvvv) +2. Use `--dump` to generate debug data dumps +3. Check log files in `./logs/` directory +4. Use `--debug-ws` for websocket debug logging +5. Use `--debug-gql` for GraphQL debug logging +6. Monitor web GUI console output and browser developer tools + +## Web GUI Architecture + +The application uses a web-based interface accessible via browser: + +### Web GUI Components + +**src/web/gui_manager.py** - WebGUIManager class: + +- Managers: StatusManager, ConsoleOutputManager, ChannelListManager, CampaignProgressManager, InventoryManager, LoginFormManager, SettingsManager, CacheManager +- Uses WebSocketBroadcaster to push real-time updates to connected clients via Socket.IO +- Pure async/await implementation + +**src/web/app.py** - FastAPI application: + +- REST API endpoints: `/api/status`, `/api/channels`, `/api/campaigns`, `/api/settings`, `/api/login`, `/api/oauth/confirm`, `/api/reload`, `/api/close`, `/api/version` +- Socket.IO server for real-time bi-directional communication +- Serves static web frontend from `web/` directory +- Integrates with WebGUIManager via `set_managers()` + +**web/** - Frontend assets: + +- `index.html` - Single-page application layout with tabs +- `static/app.js` - Socket.IO client, real-time UI updates, API calls, Inventory Filtering logic +- `static/styles.css` - Responsive design with dark mode support + +### Communication Protocol + +**Server → Client (Socket.IO events):** + +- `initial_state` - Full state on connect +- `status_update` - Status bar changes +- `console_output` - New log lines +- `channel_add/update/remove` - Channel list changes +- `drop_progress` - Drop mining progress +- `campaign_add` - New campaign added +- `login_required` - Prompt for credentials +- `settings_updated` - Settings changed + +**Client → Server:** + +- REST API for actions (login, settings, channel selection) +- Socket.IO for connection management + +### Docker Integration + +**src/config/paths.py:** + +- Detects Docker environment via `DOCKER_ENV` env var or `/.dockerenv` file +- Docker: Uses `/app` for code, `/app/data` for persistent storage +- Development: Uses `/data` for persistent storage +- All user data (cookies, settings, cache, logs) stored in DATA_DIR +- Provides `_resource_path()` helper for locating bundled resources + +**Dockerfile:** + +- Based on `python:3` +- Installs dependencies from `pyproject.toml` +- Exposes port 8080 +- Health check on `/api/status` + +**docker-compose.yml:** + +- Volume mounts `./data:/app/data` for persistence +- Port mapping `8080:8080` +- Auto-restart policy +- Timezone configuration + +### Key Design Decisions + +- **WebSocket for real-time** - Socket.IO chosen for reliability (fallback to polling) +- **Single-page app** - Simpler than full framework (React/Vue), fast load times +- **Direct Docker support** - Environment detection, proper path handling +- **OAuth device code flow** - Works great for web-based deployment + +## Project Scope + +**Supported:** + +- ✅ Web GUI - browser-based interface with advanced filtering +- ✅ Docker deployment - containerized for any platform +- ✅ Remote access - access from any device on network +- ✅ Headless operation - no display server required + +**NOT supported:** + +- Multi-account support +- Channel points mining +- Mining for unlinked campaigns +- Desktop GUI diff --git a/CLAUDE.md b/CLAUDE.md index fb2e7e2..235bb2c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,63 +1,43 @@ # CLAUDE.md +## CLAUDE.md Specific Instructions + This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +## Development Guidelines + +1. **Testing**: + - Always add unit tests for backend changes. + - Frontend changes should have tests if possible. + +2. **Code Style & Architecture**: + - **DRY (Don't Repeat Yourself)**: Codebase must follow DRY principle. + - **OOP (Object-Oriented Programming)**: Required for all backend code. + +3. **Refactoring**: + - You are authorized to refactor code to align with DRY/OOP principles. + - **Permission Required**: You MUST ask for user permission before significant refactoring. + +4. **Localization (i18n)**: + - Update translation files if there are changes to UI text or console messages. + +5. **Documentation**: + - Always update `README.md` and all agent instruction files when making changes. + - The contents of all agent instruction files should be identical except for the `Specific Instructions` section. Any agent-specific instructions must be added to that section. + ## Project Overview Twitch Drops Miner is a Python application that automatically mines timed Twitch drops without downloading stream data. It uses Twitch's GraphQL API and websocket connections to simulate watching streams while tracking drop progress. **Key Characteristics:** -- Python 3.10+ required +- Python 3.12+ required - Web-based GUI using FastAPI and Socket.IO - Async/await architecture with asyncio - Session persistence via cookies - No stream video/audio download (bandwidth-efficient) - Docker-ready for easy deployment -## Development Commands - -**IMPORTANT: Always activate the virtual environment first!** - -The project uses a virtual environment located at `env/`. All Python commands must be run within this environment: - -```bash -# Activate the virtual environment (required before any Python commands) -source env/bin/activate -``` - -### Running the Application - -```bash -# Run from source (remember to activate venv first!) -source env/bin/activate && python main.py - -# With verbose logging (stackable: -vv, -vvv) -source env/bin/activate && python main.py -v - -# Create data dump for debugging -source env/bin/activate && python main.py --dump - -# Access the web interface at http://localhost:8080 -``` - -### Development Setup - -The application requires: - -- Python 3.10+ -- Virtual environment at `env/` (must be activated before running commands) -- Dependencies from `pyproject.toml` (includes FastAPI, uvicorn, Socket.IO) - -Docker deployment: - -```bash -# Build and run with docker-compose -docker-compose up -d - -# Access at http://localhost:8080 -``` - ## Architecture @@ -97,7 +77,7 @@ lang/ # Translation JSON files (19 languages) - Runs the `src` package as a module using `runpy.run_module("src")` - All application logic is now in `src/__main__.py` -**src/**main**.py** - Entry point: +**src/__main__.py** - Entry point: - Parses command-line arguments - Initializes Settings, Twitch client, and WebGUIManager @@ -149,7 +129,7 @@ lang/ # Translation JSON files (19 languages) **src/web/app.py** - FastAPI application: -- REST API endpoints: `/api/status`, `/api/channels`, `/api/campaigns`, `/api/settings`, `/api/login`, `/api/oauth/confirm`, `/api/reload`, `/api/close` +- REST API endpoints: `/api/status`, `/api/channels`, `/api/campaigns`, `/api/settings`, `/api/login`, `/api/oauth/confirm`, `/api/reload`, `/api/close`, `/api/version` - Socket.IO server for real-time bi-directional communication - Serves static web frontend from `web/` directory - Integrates with WebGUIManager via `set_managers()` @@ -286,13 +266,56 @@ login_text = _.t["login"]["status"]["logged_in"] # Returns "Logged in" - **src/utils/** - Helper utilities (string_utils, json_utils, async_helpers, rate_limiter, backoff) - **src/i18n/** - Internationalization package with TypedDict schema and Translator class - **translator.py** - Translator class with typed translation schema (Translation TypedDict) - - ****init**.py** - Exports translation types and `_` (Translator instance) + - **__init__.py** - Exports translation types and `_` (Translator instance) - **lang/** - Translation JSON files for 19 languages (English.json is the single source of truth) - **src/version.py** - Version string - **src/web/app.py** - FastAPI application with REST API and Socket.IO - **src/web/managers/cache.py** - ImageCache for campaign artwork caching - **web/** - Frontend assets (index.html, static/app.js, static/styles.css) +## Development Commands + +**IMPORTANT: Always activate the virtual environment first!** + +The project uses a virtual environment located at `env/`. All Python commands must be run within this environment: + +```bash +# Activate the virtual environment (required before any Python commands) +source env/bin/activate +``` + +### Running the Application + +```bash +# Run from source (remember to activate venv first!) +source env/bin/activate && python main.py + +# With verbose logging (stackable: -vv, -vvv) +source env/bin/activate && python main.py -v + +# Create data dump for debugging +source env/bin/activate && python main.py --dump + +# Access the web interface at http://localhost:8080 +``` + +### Development Setup + +The application requires: + +- Python 3.12+ +- Virtual environment at `env/` (must be activated before running commands) +- Dependencies from `pyproject.toml` (includes FastAPI, uvicorn, Socket.IO) + +Docker deployment: + +```bash +# Build and run with docker-compose +docker-compose up -d + +# Access at http://localhost:8080 +``` + ## Testing ### Automated Tests @@ -333,10 +356,10 @@ The application uses a web-based interface accessible via browser: **src/web/app.py** - FastAPI application: -- REST API endpoints: `/api/status`, `/api/channels`, `/api/campaigns`, `/api/console`, `/api/settings`, `/api/login`, `/api/oauth/confirm`, `/api/reload`, `/api/close` -- Socket.IO events for real-time bi-directional communication +- REST API endpoints: `/api/status`, `/api/channels`, `/api/campaigns`, `/api/settings`, `/api/login`, `/api/oauth/confirm`, `/api/reload`, `/api/close`, `/api/version` +- Socket.IO server for real-time bi-directional communication - Serves static web frontend from `web/` directory -- Integrates with WebGUIManager via `set_managers(gui, twitch)` +- Integrates with WebGUIManager via `set_managers()` **web/** - Frontend assets: diff --git a/GEMINI.md b/GEMINI.md index 9df9dfa..b6efaa2 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -1,63 +1,43 @@ # GEMINI.md +## GEMINI.md Specific Instructions + This file provides guidance to Gemini when working with code in this repository. +## Development Guidelines + +1. **Testing**: + - Always add unit tests for backend changes. + - Frontend changes should have tests if possible. + +2. **Code Style & Architecture**: + - **DRY (Don't Repeat Yourself)**: Codebase must follow DRY principle. + - **OOP (Object-Oriented Programming)**: Required for all backend code. + +3. **Refactoring**: + - You are authorized to refactor code to align with DRY/OOP principles. + - **Permission Required**: You MUST ask for user permission before significant refactoring. + +4. **Localization (i18n)**: + - Update translation files if there are changes to UI text or console messages. + +5. **Documentation**: + - Always update `README.md` and all agent instruction files when making changes. + - The contents of all agent instruction files should be identical except for the `Specific Instructions` section. Any agent-specific instructions must be added to that section. + ## Project Overview Twitch Drops Miner is a Python application that automatically mines timed Twitch drops without downloading stream data. It uses Twitch's GraphQL API and websocket connections to simulate watching streams while tracking drop progress. **Key Characteristics:** -- Python 3.10+ required +- Python 3.12+ required - Web-based GUI using FastAPI and Socket.IO - Async/await architecture with asyncio - Session persistence via cookies - No stream video/audio download (bandwidth-efficient) - Docker-ready for easy deployment -## Development Commands - -**IMPORTANT: Always activate the virtual environment first!** - -The project uses a virtual environment located at `env/`. All Python commands must be run within this environment: - -```bash -# Activate the virtual environment (required before any Python commands) -source env/bin/activate -``` - -### Running the Application - -```bash -# Run from source (remember to activate venv first!) -source env/bin/activate && python main.py - -# With verbose logging (stackable: -vv, -vvv) -source env/bin/activate && python main.py -v - -# Create data dump for debugging -source env/bin/activate && python main.py --dump - -# Access the web interface at http://localhost:8080 -``` - -### Development Setup - -The application requires: - -- Python 3.10+ -- Virtual environment at `env/` (must be activated before running commands) -- Dependencies from `pyproject.toml` (includes FastAPI, uvicorn, Socket.IO) - -Docker deployment: - -```bash -# Build and run with docker-compose -docker-compose up -d - -# Access at http://localhost:8080 -``` - ## Architecture @@ -149,7 +129,7 @@ lang/ # Translation JSON files (19 languages) **src/web/app.py** - FastAPI application: -- REST API endpoints: `/api/status`, `/api/channels`, `/api/campaigns`, `/api/settings`, `/api/login`, `/api/oauth/confirm`, `/api/reload`, `/api/close` +- REST API endpoints: `/api/status`, `/api/channels`, `/api/campaigns`, `/api/settings`, `/api/login`, `/api/oauth/confirm`, `/api/reload`, `/api/close`, `/api/version` - Socket.IO server for real-time bi-directional communication - Serves static web frontend from `web/` directory - Integrates with WebGUIManager via `set_managers()` @@ -293,6 +273,49 @@ login_text = _.t["login"]["status"]["logged_in"] # Returns "Logged in" - **src/web/managers/cache.py** - ImageCache for campaign artwork caching - **web/** - Frontend assets (index.html, static/app.js, static/styles.css) +## Development Commands + +**IMPORTANT: Always activate the virtual environment first!** + +The project uses a virtual environment located at `env/`. All Python commands must be run within this environment: + +```bash +# Activate the virtual environment (required before any Python commands) +source env/bin/activate +``` + +### Running the Application + +```bash +# Run from source (remember to activate venv first!) +source env/bin/activate && python main.py + +# With verbose logging (stackable: -vv, -vvv) +source env/bin/activate && python main.py -v + +# Create data dump for debugging +source env/bin/activate && python main.py --dump + +# Access the web interface at http://localhost:8080 +``` + +### Development Setup + +The application requires: + +- Python 3.12+ +- Virtual environment at `env/` (must be activated before running commands) +- Dependencies from `pyproject.toml` (includes FastAPI, uvicorn, Socket.IO) + +Docker deployment: + +```bash +# Build and run with docker-compose +docker-compose up -d + +# Access at http://localhost:8080 +``` + ## Testing ### Automated Tests @@ -332,8 +355,8 @@ The application uses a web-based interface accessible via browser: **src/web/app.py** - FastAPI application: -- REST API endpoints: `/api/status`, `/api/channels`, `/api/campaigns`, `/api/console`, `/api/settings`, `/api/login`, `/api/oauth/confirm`, `/api/reload`, `/api/close` -- Socket.IO events for real-time bi-directional communication +- REST API endpoints: `/api/status`, `/api/channels`, `/api/campaigns`, `/api/settings`, `/api/login`, `/api/oauth/confirm`, `/api/reload`, `/api/close`, `/api/version` +- Socket.IO server for real-time bi-directional communication - Serves static web frontend from `web/` directory - Integrates with WebGUIManager via `set_managers(gui, twitch)` diff --git a/lang/Dansk.json b/lang/Dansk.json index 695a5a3..323d711 100644 --- a/lang/Dansk.json +++ b/lang/Dansk.json @@ -106,7 +106,20 @@ "starts": "Starter: {time}", "ends": "Slutter: {time}", "no_campaigns": "Ingen kampagner indlæst endnu...", - "claimed_drops": "hentet" + "claimed_drops": "hentet", + "filters": { + "active": "Aktiv", + "not_linked": "Ikke forbundet", + "upcoming": "Kommende", + "expired": "Udløbet", + "finished": "Afsluttet", + "item": "Genstand", + "badge": "Badge", + "emote": "Emote", + "other": "Andet", + "clear": "Ryd filtre", + "search_placeholder": "Søg efter spil..." + } }, "settings": { "general": { @@ -127,7 +140,9 @@ "actions": "Handlinger", "connection_quality": "Forbindelseskvalitet:", "minimum_refresh": "Minimum opdateringsinterval (minutter):", - "reload_campaigns": "Genindlæs kampagner" + "reload_campaigns": "Genindlæs kampagner", + "mining_benefits": "Mining Fordele", + "mining_benefits_help": "Vælg hvilke typer drops du vil mine." }, "help": { "about": "Om Twitch Drops Miner", @@ -164,6 +179,26 @@ "manual_mode": "MANUEL", "connected": "Tilsluttet", "disconnected": "Afbrudt" + }, + "footer": { + "version": "Version:", + "loading": "Indlæser...", + "update_available": "Opdatering tilgængelig:" + }, + "badges": { + "manual": { + "title": "Manuel tilstand aktiv - ser specifikt spil" + }, + "auto": { + "title": "Automatisk tilstand - følger spilprioritet" + }, + "proxy": { + "title": "Proxy-forbindelse aktiv" + } + }, + "wanted": { + "name": "Ønskede Drops Kø", + "none": "Ingen ønskede drops i køen..." } } } \ No newline at end of file diff --git a/lang/Deutsch.json b/lang/Deutsch.json index bc58cf5..c248f53 100644 --- a/lang/Deutsch.json +++ b/lang/Deutsch.json @@ -106,7 +106,20 @@ "starts": "Beginnt: {time}", "ends": "Endet: {time}", "no_campaigns": "Noch keine Kampagnen geladen...", - "claimed_drops": "abgeholt" + "claimed_drops": "abgeholt", + "filters": { + "active": "Aktiv", + "not_linked": "Nicht Verbunden", + "upcoming": "Kommend", + "expired": "Abgelaufen", + "finished": "Beendet", + "item": "Gegenstand", + "badge": "Abzeichen", + "emote": "Emote", + "other": "Andere", + "clear": "Filter löschen", + "search_placeholder": "Spiele suchen..." + } }, "settings": { "general": { @@ -127,7 +140,9 @@ "all_games_selected": "Alle Spiele sind ausgewählt oder keine Spiele verfügbar.", "actions": "Aktionen", "connection_quality": "Verbindungsqualität:", - "minimum_refresh": "Minimales Aktualisierungsintervall (Minuten):" + "minimum_refresh": "Minimales Aktualisierungsintervall (Minuten):", + "mining_benefits": "Mining-Vorteile", + "mining_benefits_help": "Wählen Sie, welche Arten von Drops Sie minen möchten." }, "help": { "about": "Über Twitch Drops Miner", @@ -164,6 +179,26 @@ "manual_mode": "MANUELL", "connected": "Verbunden", "disconnected": "Getrennt" + }, + "footer": { + "version": "Version:", + "loading": "Laden...", + "update_available": "Update verfügbar:" + }, + "badges": { + "manual": { + "title": "Manueller Modus aktiv - schaue bestimmtes Spiel" + }, + "auto": { + "title": "Automatischer Modus - folge Spielpriorität" + }, + "proxy": { + "title": "Proxy-Verbindung aktiv" + } + }, + "wanted": { + "name": "Wunsch-Drops Warteschlange", + "none": "Keine Wunsch-Drops in der Warteschlange..." } } -} +} \ No newline at end of file diff --git a/lang/English.json b/lang/English.json index 2dffcbf..21621de 100644 --- a/lang/English.json +++ b/lang/English.json @@ -97,24 +97,14 @@ "channel_count_plural": "channels", "viewers": "viewers" }, - "inventory": { - "no_campaigns": "No campaigns loaded yet...", - "status": { - "active": "Active ✔", - "upcoming": "Upcoming ⏳", - "expired": "Expired ❌", - "claimed": "Claimed ✔" - }, - "starts": "Starts: {time}", - "ends": "Ends: {time}", - "claimed_drops": "claimed" - }, "settings": { "general": { - "name": "General Settings", + "name": "General", "dark_mode": "Dark Mode" }, - "reload": "Reload", + "mining_benefits": "Mining Benefits", + "mining_benefits_help": "Select which types of drops you want to mine.", + "reload": "Reload Campaigns", "reload_campaigns": "Reload Campaigns", "games_to_watch": "Games to Watch", "games_help": "Select games to watch. Order matters - drag to reorder priority (top = highest priority).", @@ -165,6 +155,51 @@ "manual_mode": "MANUAL", "connected": "Connected", "disconnected": "Disconnected" + }, + "footer": { + "version": "Version:", + "loading": "Loading...", + "update_available": "Update Available:" + }, + "badges": { + "manual": { + "title": "Manual mode active - watching specific game" + }, + "auto": { + "title": "Automatic mode - following game priority" + }, + "proxy": { + "title": "Proxy connection active" + } + }, + "wanted": { + "name": "Wanted Drops Queue", + "none": "No wanted drops queued..." + }, + "inventory": { + "no_campaigns": "No campaigns loaded yet...", + "status": { + "active": "Active ✔", + "upcoming": "Upcoming ⏳", + "expired": "Expired ❌", + "claimed": "Claimed ✔" + }, + "starts": "Starts: {time}", + "ends": "Ends: {time}", + "claimed_drops": "claimed", + "filters": { + "active": "Active", + "not_linked": "Not Linked", + "upcoming": "Upcoming", + "expired": "Expired", + "finished": "Finished", + "item": "Item", + "badge": "Badge", + "emote": "Emote", + "other": "Other", + "clear": "Clear Filters", + "search_placeholder": "Type to search games..." + } } } -} +} \ No newline at end of file diff --git a/lang/Español.json b/lang/Español.json index 1d33307..f5de985 100644 --- a/lang/Español.json +++ b/lang/Español.json @@ -106,7 +106,20 @@ "starts": "Comienza: {time}", "ends": "Termina: {time}", "no_campaigns": "Aún no se han cargado campañas...", - "claimed_drops": "reclamado" + "claimed_drops": "reclamado", + "filters": { + "active": "Activo", + "not_linked": "No Vinculado", + "upcoming": "Próximo", + "expired": "Caducado", + "finished": "Terminado", + "item": "Objeto", + "badge": "Insignia", + "emote": "Emote", + "other": "Otro", + "clear": "Limpiar Filtros", + "search_placeholder": "Buscar juegos..." + } }, "settings": { "general": { @@ -127,7 +140,9 @@ "all_games_selected": "Todos los juegos están seleccionados o no hay juegos disponibles.", "actions": "Acciones", "connection_quality": "Calidad de conexión:", - "minimum_refresh": "Intervalo mínimo de actualización (minutos):" + "minimum_refresh": "Intervalo mínimo de actualización (minutos):", + "mining_benefits": "Beneficios de Minería", + "mining_benefits_help": "Selecciona qué tipos de drops deseas minar." }, "help": { "about": "Acerca de Twitch Drops Miner", @@ -164,6 +179,26 @@ "manual_mode": "MANUAL", "connected": "Conectado", "disconnected": "Desconectado" + }, + "footer": { + "version": "Versión:", + "loading": "Cargando...", + "update_available": "Actualización disponible:" + }, + "badges": { + "manual": { + "title": "Modo manual activo - viendo juego específico" + }, + "auto": { + "title": "Modo automático - siguiendo prioridad de juegos" + }, + "proxy": { + "title": "Conexión proxy activa" + } + }, + "wanted": { + "name": "Cola de Drops Deseados", + "none": "No hay drops deseados en cola..." } } -} +} \ No newline at end of file diff --git a/lang/Français.json b/lang/Français.json index caf5258..550e02a 100644 --- a/lang/Français.json +++ b/lang/Français.json @@ -106,7 +106,20 @@ "starts": "Début : {time}", "ends": "Fin : {time}", "no_campaigns": "Aucune campagne chargée pour le moment...", - "claimed_drops": "récupéré" + "claimed_drops": "récupéré", + "filters": { + "active": "Actif", + "not_linked": "Non lié", + "upcoming": "À venir", + "expired": "Expiré", + "finished": "Terminé", + "item": "Objet", + "badge": "Badge", + "emote": "Émote", + "other": "Autre", + "clear": "Effacer les filtres", + "search_placeholder": "Rechercher des jeux..." + } }, "settings": { "general": { @@ -127,7 +140,9 @@ "all_games_selected": "Tous les jeux sont sélectionnés ou aucun jeu disponible.", "actions": "Actions", "connection_quality": "Qualité de connexion :", - "minimum_refresh": "Intervalle minimum de rafraîchissement (minutes) :" + "minimum_refresh": "Intervalle minimum de rafraîchissement (minutes) :", + "mining_benefits": "Avantages du Minage", + "mining_benefits_help": "Sélectionnez les types de drops que vous souhaitez miner." }, "help": { "about": "À propos de Twitch Drops Miner", @@ -164,6 +179,26 @@ "manual_mode": "MANUEL", "connected": "Connecté", "disconnected": "Déconnecté" + }, + "footer": { + "version": "Version :", + "loading": "Chargement...", + "update_available": "Mise à jour disponible :" + }, + "badges": { + "manual": { + "title": "Mode manuel actif - regarde un jeu spécifique" + }, + "auto": { + "title": "Mode automatique - suit la priorité des jeux" + }, + "proxy": { + "title": "Connexion proxy active" + } + }, + "wanted": { + "name": "File d'attente Drops souhaités", + "none": "Aucun drop souhaité en file d'attente..." } } -} +} \ No newline at end of file diff --git a/lang/Indonesian.json b/lang/Indonesian.json index b431498..b884735 100644 --- a/lang/Indonesian.json +++ b/lang/Indonesian.json @@ -109,7 +109,20 @@ "starts": "Mulai : {time}", "ends": "Akhir : {time}", "no_campaigns": "Belum ada kampanye yang dimuat...", - "claimed_drops": "diklaim" + "claimed_drops": "diklaim", + "filters": { + "active": "Aktif", + "not_linked": "Tidak Terhubung", + "upcoming": "Mendatang", + "expired": "Kadaluarsa", + "finished": "Selesai", + "item": "Item", + "badge": "Lencana", + "emote": "Emote", + "other": "Lainnya", + "clear": "Hapus Filter", + "search_placeholder": "Cari game..." + } }, "settings": { "general": { @@ -130,7 +143,9 @@ "actions": "Tindakan", "connection_quality": "Kualitas Koneksi:", "minimum_refresh": "Interval Refresh Minimum (menit):", - "reload_campaigns": "Muat Ulang Kampanye" + "reload_campaigns": "Muat Ulang Kampanye", + "mining_benefits": "Manfaat Penambangan", + "mining_benefits_help": "Pilih jenis drops yang ingin Anda tambang." }, "help": { "about": "Tentang Twitch Drops Miner", @@ -167,6 +182,26 @@ "manual_mode": "MANUAL", "connected": "Terhubung", "disconnected": "Terputus" + }, + "footer": { + "version": "Versi:", + "loading": "Memuat...", + "update_available": "Pembaruan Tersedia:" + }, + "badges": { + "manual": { + "title": "Mode manual aktif - menonton game tertentu" + }, + "auto": { + "title": "Mode otomatis - mengikuti prioritas game" + }, + "proxy": { + "title": "Koneksi proxy aktif" + } + }, + "wanted": { + "name": "Antrean Drops yang Diinginkan", + "none": "Tidak ada drops yang diinginkan dalam antrean..." } } } \ No newline at end of file diff --git a/lang/Italiano.json b/lang/Italiano.json index c4d7041..ecaccc1 100644 --- a/lang/Italiano.json +++ b/lang/Italiano.json @@ -109,7 +109,20 @@ "starts": "Inizia: {time}", "ends": "Finisce: {time}", "no_campaigns": "Nessuna campagna caricata ancora...", - "claimed_drops": "reclamato" + "claimed_drops": "reclamato", + "filters": { + "active": "Attivo", + "not_linked": "Non Collegato", + "upcoming": "In Arrivo", + "expired": "Scaduto", + "finished": "Finito", + "item": "Oggetto", + "badge": "Badge", + "emote": "Emote", + "other": "Altro", + "clear": "Pulisci Filtri", + "search_placeholder": "Cerca giochi..." + } }, "settings": { "general": { @@ -130,7 +143,9 @@ "actions": "Azioni", "connection_quality": "Qualità della connessione:", "minimum_refresh": "Intervallo minimo di aggiornamento (minuti):", - "reload_campaigns": "Ricarica campagne" + "reload_campaigns": "Ricarica campagne", + "mining_benefits": "Vantaggi Minatori", + "mining_benefits_help": "Seleziona quali tipi di drop vuoi minare." }, "help": { "about": "Informazioni su Twitch Drops Miner", @@ -167,6 +182,26 @@ "manual_mode": "MANUALE", "connected": "Connesso", "disconnected": "Disconnesso" + }, + "footer": { + "version": "Versione:", + "loading": "Caricamento...", + "update_available": "Aggiornamento disponibile:" + }, + "badges": { + "manual": { + "title": "Modalità manuale attiva - guardando gioco specifico" + }, + "auto": { + "title": "Modalità automatica - seguendo priorità giochi" + }, + "proxy": { + "title": "Connessione proxy attiva" + } + }, + "wanted": { + "name": "Coda Drops Desiderati", + "none": "Nessun drop desiderato in coda..." } } } \ No newline at end of file diff --git a/lang/Nederlandse.json b/lang/Nederlandse.json index 35b517b..2eba6a9 100644 --- a/lang/Nederlandse.json +++ b/lang/Nederlandse.json @@ -109,7 +109,20 @@ "starts": "Begint: {time}", "ends": "Eindigt: {time}", "no_campaigns": "Nog geen campagnes geladen...", - "claimed_drops": "geclaimd" + "claimed_drops": "geclaimd", + "filters": { + "active": "Actief", + "not_linked": "Niet gekoppeld", + "upcoming": "Aankomend", + "expired": "Verlopen", + "finished": "Voltooid", + "item": "Item", + "badge": "Badge", + "emote": "Emote", + "other": "Overig", + "clear": "Filters wissen", + "search_placeholder": "Zoek spellen..." + } }, "settings": { "general": { @@ -130,7 +143,9 @@ "actions": "Acties", "connection_quality": "Verbindingskwaliteit:", "minimum_refresh": "Minimaal verversingsinterval (minuten):", - "reload_campaigns": "Herlaad Campagnes" + "reload_campaigns": "Herlaad Campagnes", + "mining_benefits": "Mining Voordelen", + "mining_benefits_help": "Selecteer welke soorten drops je wilt minen." }, "help": { "about": "Over Twitch Drops Miner", @@ -167,6 +182,26 @@ "manual_mode": "HANDMATIG", "connected": "Verbonden", "disconnected": "Verbinding verbroken" + }, + "footer": { + "version": "Versie:", + "loading": "Laden...", + "update_available": "Update beschikbaar:" + }, + "badges": { + "manual": { + "title": "Handmatige modus actief - specifiek spel kijken" + }, + "auto": { + "title": "Automatische modus - spelprioriteit volgen" + }, + "proxy": { + "title": "Proxy-verbinding actief" + } + }, + "wanted": { + "name": "Gewenste Drops Wachtrij", + "none": "Geen gewenste drops in de wachtrij..." } } } \ No newline at end of file diff --git a/lang/Polski.json b/lang/Polski.json index 3a0d456..7de230d 100644 --- a/lang/Polski.json +++ b/lang/Polski.json @@ -109,7 +109,20 @@ "starts": "Rozpoczęcie: {time}", "ends": "Koniec: {time}", "no_campaigns": "Nie załadowano jeszcze żadnych kampanii...", - "claimed_drops": "odebrano" + "claimed_drops": "odebrano", + "filters": { + "active": "Aktywne", + "not_linked": "Niepołączone", + "upcoming": "Nadchodzące", + "expired": "Wygasłe", + "finished": "Zakończone", + "item": "Przedmiot", + "badge": "Odznaka", + "emote": "Emotka", + "other": "Inne", + "clear": "Wyczyść filtry", + "search_placeholder": "Szukaj gier..." + } }, "settings": { "general": { @@ -131,7 +144,9 @@ "actions": "Akcje", "connection_quality": "Jakość połączenia:", "minimum_refresh": "Minimalny interwał odświeżania (minuty):", - "reload_campaigns": "Przeładuj kampanie" + "reload_campaigns": "Przeładuj kampanie", + "mining_benefits": "Korzyści z Minowania", + "mining_benefits_help": "Wybierz rodzaje dropów, które chcesz zdobywać." }, "help": { "about": "O Twitch Drops Miner", @@ -168,6 +183,26 @@ "manual_mode": "RĘCZNY", "connected": "Połączono", "disconnected": "Rozłączono" + }, + "footer": { + "version": "Wersja:", + "loading": "Ładowanie...", + "update_available": "Aktualizacja dostępna:" + }, + "badges": { + "manual": { + "title": "Tryb ręczny aktywny - oglądanie konkretnej gry" + }, + "auto": { + "title": "Tryb automatyczny - według priorytetu gier" + }, + "proxy": { + "title": "Połączenie proxy aktywne" + } + }, + "wanted": { + "name": "Kolejka Pożądanych Dropów", + "none": "Brak pożądanych dropów w kolejce..." } } } \ No newline at end of file diff --git a/lang/Português.json b/lang/Português.json index 9dcd8da..b81eab1 100644 --- a/lang/Português.json +++ b/lang/Português.json @@ -109,7 +109,20 @@ "starts": "Começa em: {time}", "ends": "Termina em: {time}", "no_campaigns": "Nenhuma campanha carregada ainda...", - "claimed_drops": "reivindicado" + "claimed_drops": "reivindicado", + "filters": { + "active": "Ativo", + "not_linked": "Não Vinculado", + "upcoming": "Próximo", + "expired": "Expirado", + "finished": "Terminado", + "item": "Item", + "badge": "Distintivo", + "emote": "Emote", + "other": "Outro", + "clear": "Limpar Filtros", + "search_placeholder": "Pesquisar jogos..." + } }, "settings": { "general": { @@ -130,7 +143,9 @@ "actions": "Ações", "connection_quality": "Qualidade da conexão:", "minimum_refresh": "Intervalo mínimo de atualização (minutos):", - "reload_campaigns": "Recarregar Campanhas" + "reload_campaigns": "Recarregar Campanhas", + "mining_benefits": "Benefícios de Mineração", + "mining_benefits_help": "Selecione quais tipos de drops você deseja minerar." }, "help": { "about": "Sobre o Twitch Drops Miner", @@ -167,6 +182,26 @@ "manual_mode": "MANUAL", "connected": "Conectado", "disconnected": "Desconectado" + }, + "footer": { + "version": "Versão:", + "loading": "Carregando...", + "update_available": "Atualização disponível:" + }, + "badges": { + "manual": { + "title": "Modo manual ativo - assistindo jogo específico" + }, + "auto": { + "title": "Modo automático - seguindo prioridade de jogos" + }, + "proxy": { + "title": "Conexão proxy ativa" + } + }, + "wanted": { + "name": "Fila de Drops Desejados", + "none": "Nenhum drop desejado na fila..." } } } \ No newline at end of file diff --git a/lang/Română.json b/lang/Română.json index 6168519..7604bb9 100644 --- a/lang/Română.json +++ b/lang/Română.json @@ -109,7 +109,20 @@ "starts": "Începe : {time}", "ends": "Se termină : {time}", "no_campaigns": "Nicio campanie încărcată încă...", - "claimed_drops": "revendicat" + "claimed_drops": "revendicat", + "filters": { + "active": "Activ", + "not_linked": "Neconectat", + "upcoming": "Viitor", + "expired": "Expirat", + "finished": "Terminat", + "item": "Obiect", + "badge": "Insignă", + "emote": "Emoticon", + "other": "Altele", + "clear": "Șterge filtre", + "search_placeholder": "Caută jocuri..." + } }, "settings": { "general": { @@ -130,7 +143,9 @@ "actions": "Acțiuni", "connection_quality": "Calitatea conexiunii:", "minimum_refresh": "Interval minim de reîmprospătare (minute):", - "reload_campaigns": "Reîncarcă campaniile" + "reload_campaigns": "Reîncarcă campaniile", + "mining_benefits": "Beneficii Minerit", + "mining_benefits_help": "Selectați ce tipuri de drops doriți să minați." }, "help": { "about": "Despre Twitch Drops Miner", @@ -167,6 +182,26 @@ "manual_mode": "MANUAL", "connected": "Conectat", "disconnected": "Deconectat" + }, + "footer": { + "version": "Versiune:", + "loading": "Se încarcă...", + "update_available": "Actualizare disponibilă:" + }, + "badges": { + "manual": { + "title": "Mod manual activ - vizionare joc specific" + }, + "auto": { + "title": "Mod automat - urmărire prioritate jocuri" + }, + "proxy": { + "title": "Conexiune proxy activă" + } + }, + "wanted": { + "name": "Coadă Drops Dorite", + "none": "Niciun drop dorit în coadă..." } } } \ No newline at end of file diff --git a/lang/Türkçe.json b/lang/Türkçe.json index b2b95b0..2a57443 100644 --- a/lang/Türkçe.json +++ b/lang/Türkçe.json @@ -109,7 +109,20 @@ "starts": "Başladı: {time}", "ends": "Bitiş: {time}", "no_campaigns": "Henüz yüklenen kampanya yok...", - "claimed_drops": "talep edildi" + "claimed_drops": "talep edildi", + "filters": { + "active": "Aktif", + "not_linked": "Bağlı Değil", + "upcoming": "Yaklaşan", + "expired": "Süresi Dolmuş", + "finished": "Tamamlanmış", + "item": "Eşya", + "badge": "Rozet", + "emote": "İfade", + "other": "Diğer", + "clear": "Filtreleri Temizle", + "search_placeholder": "Oyun ara..." + } }, "settings": { "general": { @@ -130,7 +143,9 @@ "actions": "İşlemler", "connection_quality": "Bağlantı kalitesi:", "minimum_refresh": "Minimum yenileme aralığı (dakika):", - "reload_campaigns": "Kampanyaları Yeniden Yükle" + "reload_campaigns": "Kampanyaları Yeniden Yükle", + "mining_benefits": "Madencilik Avantajları", + "mining_benefits_help": "Hangi tür dropları toplamak istediğinizi seçin." }, "help": { "about": "Twitch Drops Miner Hakkında", @@ -167,6 +182,26 @@ "manual_mode": "MANUEL", "connected": "Bağlandı", "disconnected": "Bağlantı kesildi" + }, + "footer": { + "version": "Sürüm:", + "loading": "Yükleniyor...", + "update_available": "Güncelleme Mevcut:" + }, + "badges": { + "manual": { + "title": "Manuel mod aktif - belirli oyun izleniyor" + }, + "auto": { + "title": "Otomatik mod - oyun önceliği takip ediliyor" + }, + "proxy": { + "title": "Proxy bağlantısı aktif" + } + }, + "wanted": { + "name": "İstenen Drop Kuyruğu", + "none": "Kuyrukta istenen drop yok..." } } } \ No newline at end of file diff --git a/lang/Čeština.json b/lang/Čeština.json index 5e77a06..5bdb431 100644 --- a/lang/Čeština.json +++ b/lang/Čeština.json @@ -108,7 +108,20 @@ "starts": "Začíná: {time}", "ends": "Začíná: {time}", "no_campaigns": "Zatím nejsou načteny žádné kampaně...", - "claimed_drops": "získáno" + "claimed_drops": "získáno", + "filters": { + "active": "Aktivní", + "not_linked": "Nepřipojeno", + "upcoming": "Nadcházející", + "expired": "Vypršelo", + "finished": "Dokončeno", + "item": "Předmět", + "badge": "Odznak", + "emote": "Emotikon", + "other": "Jiné", + "clear": "Vymazat filtry", + "search_placeholder": "Hledat hry..." + } }, "settings": { "general": { @@ -130,7 +143,9 @@ "actions": "Akce", "connection_quality": "Kvalita připojení:", "minimum_refresh": "Minimální interval obnovení (minuty):", - "reload_campaigns": "Znovu načíst kampaně" + "reload_campaigns": "Znovu načíst kampaně", + "mining_benefits": "Výhody Těžby", + "mining_benefits_help": "Vyberte typy dropů, které chcete těžit." }, "help": { "about": "O Twitch Drops Miner", @@ -167,6 +182,26 @@ "manual_mode": "MANUÁLNÍ", "connected": "Připojeno", "disconnected": "Odpojeno" + }, + "footer": { + "version": "Verze:", + "loading": "Načítání...", + "update_available": "Dostupná aktualizace:" + }, + "badges": { + "manual": { + "title": "Manuální režim aktivní - sledování konkrétní hry" + }, + "auto": { + "title": "Automatický režim - podle priority her" + }, + "proxy": { + "title": "Proxy připojení aktivní" + } + }, + "wanted": { + "name": "Fronta chtěných Dropů", + "none": "Žádné chtěné dropy ve frontě..." } } } \ No newline at end of file diff --git a/lang/Русский.json b/lang/Русский.json index cb7e158..133fc3f 100644 --- a/lang/Русский.json +++ b/lang/Русский.json @@ -109,7 +109,20 @@ "starts": "Начало: {time}", "ends": "Окончание: {time}", "no_campaigns": "Кампании ещё не загружены...", - "claimed_drops": "получено" + "claimed_drops": "получено", + "filters": { + "active": "Активные", + "not_linked": "Не связаны", + "upcoming": "Предстоящие", + "expired": "Истекшие", + "finished": "Завершенные", + "item": "Предмет", + "badge": "Значок", + "emote": "Смайлик", + "other": "Другое", + "clear": "Очистить фильтры", + "search_placeholder": "Поиск игр..." + } }, "settings": { "general": { @@ -131,7 +144,9 @@ "actions": "Действия", "connection_quality": "Качество соединения:", "minimum_refresh": "Минимальный интервал обновления (минуты):", - "reload_campaigns": "Перезагрузить кампании" + "reload_campaigns": "Перезагрузить кампании", + "mining_benefits": "Преимущества майнинга", + "mining_benefits_help": "Выберите типы drops, которые вы хотите майнить." }, "help": { "about": "О Twitch Drops Miner", @@ -168,6 +183,26 @@ "manual_mode": "РУЧНОЙ", "connected": "Подключено", "disconnected": "Отключено" + }, + "footer": { + "version": "Версия:", + "loading": "Загрузка...", + "update_available": "Доступно обновление:" + }, + "badges": { + "manual": { + "title": "Ручной режим - просмотр конкретной игры" + }, + "auto": { + "title": "Автоматический режим - по приоритету игр" + }, + "proxy": { + "title": "Прокси подключен" + } + }, + "wanted": { + "name": "Очередь желаемых дропов", + "none": "Нет желаемых дропов в очереди..." } } } \ No newline at end of file diff --git a/lang/Українська.json b/lang/Українська.json index 6751454..9fabc9a 100644 --- a/lang/Українська.json +++ b/lang/Українська.json @@ -109,7 +109,20 @@ "starts": "Починається {time}", "ends": "Завершується {time}", "no_campaigns": "Кампанії ще не завантажені...", - "claimed_drops": "отримано" + "claimed_drops": "отримано", + "filters": { + "active": "Активні", + "not_linked": "Не пов'язані", + "upcoming": "Майбутні", + "expired": "Завершені", + "finished": "Закінчені", + "item": "Предмет", + "badge": "Значок", + "emote": "Смайлик", + "other": "Інше", + "clear": "Очистити фільтри", + "search_placeholder": "Пошук ігор..." + } }, "settings": { "general": { @@ -130,7 +143,9 @@ "actions": "Дії", "connection_quality": "Якість з'єднання:", "minimum_refresh": "Мінімальний інтервал оновлення (хвилини):", - "reload_campaigns": "Перезавантажити кампанії" + "reload_campaigns": "Перезавантажити кампанії", + "mining_benefits": "Переваги майнінгу", + "mining_benefits_help": "Виберіть типи drops, які ви хочете майнити." }, "help": { "about": "Про Twitch Drops Miner", @@ -167,6 +182,26 @@ "manual_mode": "РУЧНИЙ", "connected": "Підключено", "disconnected": "Відключено" + }, + "footer": { + "version": "Версія:", + "loading": "Завантаження...", + "update_available": "Доступне оновлення:" + }, + "badges": { + "manual": { + "title": "Ручний режим - перегляд конкретної гри" + }, + "auto": { + "title": "Автоматичний режим - за пріоритетом ігор" + }, + "proxy": { + "title": "Проксі підключено" + } + }, + "wanted": { + "name": "Черга бажаних дропів", + "none": "Немає бажаних дропів у черзі..." } } } \ No newline at end of file diff --git a/lang/العربية.json b/lang/العربية.json index b2c8a55..641da5d 100644 --- a/lang/العربية.json +++ b/lang/العربية.json @@ -109,7 +109,20 @@ "starts": "{time} :يبدأ", "ends": "{time} :ينتهي", "no_campaigns": "لم يتم تحميل أي حملات بعد...", - "claimed_drops": "تم المطالبة" + "claimed_drops": "تم المطالبة", + "filters": { + "active": "نشط", + "not_linked": "غير مرتبط", + "upcoming": "قادم", + "expired": "منتهي", + "finished": "مكتمل", + "item": "عنصر", + "badge": "شارة", + "emote": "رمز تعبيري", + "other": "آخر", + "clear": "مسح المرشحات", + "search_placeholder": "البحث عن الألعاب..." + } }, "settings": { "general": { @@ -130,7 +143,9 @@ "actions": "الإجراءات", "connection_quality": "جودة الاتصال:", "minimum_refresh": "الحد الأدنى لفترة التحديث (بالدقائق):", - "reload_campaigns": "إعادة تحميل الحملات" + "reload_campaigns": "إعادة تحميل الحملات", + "mining_benefits": "فوائد التعدين", + "mining_benefits_help": "اختر أنواع الجوائز التي تريد تعدينها." }, "help": { "about": "حول Twitch Drops Miner", @@ -167,6 +182,26 @@ "manual_mode": "يدوي", "connected": "متصل", "disconnected": "غير متصل" + }, + "footer": { + "version": "النسخة:", + "loading": "جاري التحميل...", + "update_available": "تحديث متوفر:" + }, + "badges": { + "manual": { + "title": "الوضع اليدوي نشط - مشاهدة لعبة محددة" + }, + "auto": { + "title": "الوضع التلقائي - اتباع أولوية الألعاب" + }, + "proxy": { + "title": "اتصال البروكسي نشط" + } + }, + "wanted": { + "name": "قائمة انتظار Drops المطلوبة", + "none": "لا توجد drops مطلوبة في قائمة الانتظار..." } } } \ No newline at end of file diff --git a/lang/日本語.json b/lang/日本語.json index f7b6857..d598a56 100644 --- a/lang/日本語.json +++ b/lang/日本語.json @@ -109,7 +109,20 @@ "starts": "開始:{time}", "ends": "終了:{time}", "no_campaigns": "まだキャンペーンが読み込まれていません...", - "claimed_drops": "受け取り済み" + "claimed_drops": "受け取り済み", + "filters": { + "active": "アクティブ", + "not_linked": "未連携", + "upcoming": "近日開催", + "expired": "期限切れ", + "finished": "完了", + "item": "アイテム", + "badge": "バッジ", + "emote": "エモート", + "other": "その他", + "clear": "フィルター解除", + "search_placeholder": "ゲームを検索..." + } }, "settings": { "general": { @@ -130,7 +143,9 @@ "actions": "アクション", "connection_quality": "接続品質:", "minimum_refresh": "最小更新間隔(分):", - "reload_campaigns": "キャンペーンを再読み込み" + "reload_campaigns": "キャンペーンを再読み込み", + "mining_benefits": "マイニング特典", + "mining_benefits_help": "マイニングしたいドロップの種類を選択してください。" }, "help": { "about": "Twitch Drops Minerについて", @@ -167,6 +182,26 @@ "manual_mode": "手動", "connected": "接続済み", "disconnected": "切断" + }, + "footer": { + "version": "バージョン:", + "loading": "読み込み中...", + "update_available": "更新利用可能:" + }, + "badges": { + "manual": { + "title": "手動モード - 特定のゲームを視聴中" + }, + "auto": { + "title": "自動モード - ゲーム優先度に従う" + }, + "proxy": { + "title": "プロキシ接続中" + } + }, + "wanted": { + "name": "欲しいDropsキュー", + "none": "キューに欲しいDropsはありません..." } } } \ No newline at end of file diff --git a/lang/简体中文.json b/lang/简体中文.json index fc07ce3..3c1d634 100644 --- a/lang/简体中文.json +++ b/lang/简体中文.json @@ -106,7 +106,20 @@ "starts": "开始时间: {time}", "ends": "结束时间: {time}", "no_campaigns": "尚未加载任何活动...", - "claimed_drops": "已领取" + "claimed_drops": "已领取", + "filters": { + "active": "活动中", + "not_linked": "未关联", + "upcoming": "即将开始", + "expired": "已过期", + "finished": "已完成", + "item": "物品", + "badge": "徽章", + "emote": "表情", + "other": "其他", + "clear": "清除过滤器", + "search_placeholder": "搜索游戏..." + } }, "settings": { "general": { @@ -127,7 +140,9 @@ "actions": "操作", "connection_quality": "连接质量:", "minimum_refresh": "最小刷新间隔(分钟):", - "reload_campaigns": "重新加载活动" + "reload_campaigns": "重新加载活动", + "mining_benefits": "挖掘奖励", + "mining_benefits_help": "选择您想要获取的掉落类型。" }, "help": { "about": "关于 Twitch 掉宝矿工", @@ -164,6 +179,26 @@ "manual_mode": "MANUAL", "connected": "已连接", "disconnected": "已断开" + }, + "footer": { + "version": "版本:", + "loading": "加载中...", + "update_available": "可用更新:" + }, + "badges": { + "manual": { + "title": "手动模式已激活 - 正在观看特定游戏" + }, + "auto": { + "title": "自动模式 - 遵循游戏优先级" + }, + "proxy": { + "title": "代理连接已激活" + } + }, + "wanted": { + "name": "期望掉落队列", + "none": "队列中没有期望的掉落..." } } } \ No newline at end of file diff --git a/lang/繁體中文.json b/lang/繁體中文.json index bf0fad2..29952f8 100644 --- a/lang/繁體中文.json +++ b/lang/繁體中文.json @@ -109,7 +109,20 @@ "starts": "開始時間:{time}", "ends": "結束時間:{time}", "no_campaigns": "尚未載入任何活動...", - "claimed_drops": "已領取" + "claimed_drops": "已領取", + "filters": { + "active": "活動中", + "not_linked": "未連結", + "upcoming": "即將開始", + "expired": "已過期", + "finished": "已完成", + "item": "物品", + "badge": "徽章", + "emote": "表情", + "other": "其他", + "clear": "清除過濾器", + "search_placeholder": "搜尋遊戲..." + } }, "settings": { "general": { @@ -130,7 +143,9 @@ "actions": "操作", "connection_quality": "連線品質:", "minimum_refresh": "最小重新整理間隔(分鐘):", - "reload_campaigns": "重新載入活動" + "reload_campaigns": "重新載入活動", + "mining_benefits": "挖掘獎勵", + "mining_benefits_help": "選擇您想要獲取的掉落類型。" }, "help": { "about": "關於 Twitch Drops Miner", @@ -167,6 +182,26 @@ "manual_mode": "手動", "connected": "已連接", "disconnected": "已中斷連接" + }, + "footer": { + "version": "版本:", + "loading": "載入中...", + "update_available": "可用更新:" + }, + "badges": { + "manual": { + "title": "手動模式已啟用 - 正在觀看特定遊戲" + }, + "auto": { + "title": "自動模式 - 遵循遊戲優先級" + }, + "proxy": { + "title": "代理連接已啟用" + } + }, + "wanted": { + "name": "期望掉落隊列", + "none": "隊列中沒有期望的掉落..." } } } \ No newline at end of file diff --git a/src/i18n/translator.py b/src/i18n/translator.py index a74d3cb..6f7575a 100644 --- a/src/i18n/translator.py +++ b/src/i18n/translator.py @@ -112,6 +112,41 @@ class GUIChannels(TypedDict): viewers: str +class GUIFooter(TypedDict): + version: str + loading: str + update_available: str + + +class GUIBadgeItem(TypedDict): + title: str + + +class GUIBadges(TypedDict): + manual: GUIBadgeItem + auto: GUIBadgeItem + proxy: GUIBadgeItem + + +class GUIWanted(TypedDict): + name: str + none: str + + +class GUIInvFilters(TypedDict): + active: str + not_linked: str + upcoming: str + expired: str + finished: str + item: str + badge: str + emote: str + other: str + clear: str + search_placeholder: str + + class GUIInvStatus(TypedDict): active: str expired: str @@ -125,6 +160,7 @@ class GUIInventory(TypedDict): starts: str ends: str claimed_drops: str + filters: GUIInvFilters class GUISettingsGeneral(TypedDict): @@ -134,6 +170,8 @@ class GUISettingsGeneral(TypedDict): class GUISettings(TypedDict): general: GUISettingsGeneral + mining_benefits: str + mining_benefits_help: str reload: str reload_campaigns: str games_to_watch: str @@ -185,6 +223,9 @@ class GUIMessages(TypedDict): settings: GUISettings help: GUIHelp header: GUIHeader + footer: GUIFooter + badges: GUIBadges + wanted: GUIWanted class Translation(TypedDict): diff --git a/web/index.html b/web/index.html index 8f8a4e9..fa36ed6 100644 --- a/web/index.html +++ b/web/index.html @@ -69,8 +69,8 @@
-

Current Drop

-
+

Campaign Progress

+
No active drop