{"id":28443453,"url":"https://github.com/fl1s/turron","last_synced_at":"2026-04-29T15:05:06.629Z","repository":{"id":295481055,"uuid":"989530455","full_name":"Fl1s/turron","owner":"Fl1s","description":"A search system that analyzes short video snippets (2–5 secs) and finds highly accurate matches using keyframe-based perceptual hashing. Selfhosted Video Shazam.","archived":false,"fork":false,"pushed_at":"2026-01-06T11:16:35.000Z","size":69168,"stargazers_count":67,"open_issues_count":3,"forks_count":5,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-09T04:37:31.823Z","etag":null,"topics":["hacktoberfest","hacktoberfest2025","hamming-distance","java","phash","shazam","spring-boot","video","video-processing","video-recognition","video-shazam"],"latest_commit_sha":null,"homepage":"https://turron.pw","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Fl1s.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-05-24T09:34:34.000Z","updated_at":"2026-01-06T11:16:39.000Z","dependencies_parsed_at":"2026-01-03T06:10:29.703Z","dependency_job_id":null,"html_url":"https://github.com/Fl1s/turron","commit_stats":null,"previous_names":["fl1s/turron"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/Fl1s/turron","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Fl1s%2Fturron","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Fl1s%2Fturron/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Fl1s%2Fturron/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Fl1s%2Fturron/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Fl1s","download_url":"https://codeload.github.com/Fl1s/turron/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Fl1s%2Fturron/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32430807,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-29T13:34:34.882Z","status":"ssl_error","status_checked_at":"2026-04-29T13:34:29.830Z","response_time":110,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hacktoberfest","hacktoberfest2025","hamming-distance","java","phash","shazam","spring-boot","video","video-processing","video-recognition","video-shazam"],"created_at":"2025-06-06T07:40:35.447Z","updated_at":"2026-04-29T15:05:06.623Z","avatar_url":"https://github.com/Fl1s.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ca id=\"readme-top\"\u003e\u003c/a\u003e\n\n\u003c!-- PROJECT SHIELDS --\u003e\n\u003cp align=\"center\"\u003e\n  \n  [![Contributors][contributors-shield]][contributors-url]\n  [![Forks][forks-shield]][forks-url]\n  [![Stargazers][stars-shield]][stars-url]\n  [![Issues][issues-shield]][issues-url]\n  [![Apache 2.0 License][license-shield]][license-url]\n  \n\u003c/p\u003e\n\n\u003c!-- PROJECT LOGO --\u003e\n\n\u003cdiv align=\"center\"\u003e  \n\u003cp align=\"center\"\u003e\n  \u003cimg width=\"480\" height=\"480\" alt=\"turron-logo-readme\" src=\"https://github.com/user-attachments/assets/d0ba8800-3067-4f7c-a554-19aac4833c7b\" /\u003e\n\u003c/p\u003e\n\nhttps://github.com/user-attachments/assets/8bf90c7a-ce68-4d38-a140-e363fc421395\n\nhttps://github.com/user-attachments/assets/5651a9fb-029d-4126-b192-ba42ca269a5d\n\nhttps://github.com/user-attachments/assets/db4aef2f-349c-499e-bcb6-a3aac1d57e1a\n\n\u003cp align=\"center\"\u003e\n  .\n  \u003ca href=\"#quickstart\"\u003e\u003cstrong\u003eQuickstart\u003c/strong\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n·\n    \u003ca href=\"https://github.com/fl1s/turron/issues/new?labels=bug\"\u003eReport Bug\u003c/a\u003e\n    ·\n    \u003ca href=\"https://github.com/fl1s/turron/issues/new?labels=enhancement\"\u003eRequest Feature\u003c/a\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n\n\u003c!-- SKILL ICONS --\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://skillicons.dev/icons?i=java,spring,postgres,kafka,docker,kubernetes,prometheus,grafana,gradle,postman,git\" /\u003e\n\u003c/p\u003e\n\n\u003c!-- TABLE OF CONTENTS --\u003e\n\u003cdetails\u003e\n  \u003csummary\u003eTable of Contents\u003c/summary\u003e\n  \u003col\u003e\n    \u003cli\u003e\n      \u003ca href=\"#about-the-project\"\u003eAbout\u003c/a\u003e\n      \u003cul\u003e\n        \u003cli\u003e\u003ca href=\"#microservices\"\u003eMicroservices\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"#built-with\"\u003eBuilt With\u003c/a\u003e\u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/li\u003e\n    \u003cli\u003e\n      \u003ca href=\"#getting-started\"\u003eGetting Started\u003c/a\u003e\n      \u003cul\u003e\n        \u003cli\u003e\u003ca href=\"#prerequisites\"\u003ePrerequisites\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e\u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#usage\"\u003eUsage\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#ci-cd\"\u003eCI/CD\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#monitoring\"\u003eMonitoring\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#roadmap\"\u003eRoadmap\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#contributing\"\u003eContributing\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#license\"\u003eLicense\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#contact\"\u003eContact\u003c/a\u003e\u003c/li\u003e\n  \u003c/ol\u003e\n\u003c/details\u003e\n\n\u003c!-- ABOUT THE PROJECT --\u003e\n\n## About The Project\n\nA video recognition system that works like Shazam — but for video. It analyzes short snippets (2–5 seconds), breaks them\ninto keyframes, and uses perceptual hashing to identify the exact or near-exact source, even if the clip has been edited\nor altered. This preserves the full context of the snippet and enables reliable tracking of original video content.\n\nKey features:\n\n* Upload full video snippets, not just images — automatic extraction of keyframes for context-aware matching.\n* Accurate source identification via perceptual hashing tolerant to modifications.\n* Optimized for quick, precise matching of short video fragments.\n* Scalable microservices architecture built to handle heavy traffic without performance loss.\n\n[//]: # (* Instant search results thanks to Redis caching.)\n\n[//]: # (* Open and extensible API for easy integration and community-driven improvements \u0026#40;coming soon\u0026#41;.)\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n### Microservices\n\nTurron is structured into 6 microservices, each with bounded responsibilities:\n\n- **Eureka Server**: Manages service discovery using Netflix Eureka with `@DiscoveryClient`.\n\n- **Upload Service**: Accepts short videos via REST API, stores them in MinIO, and sends processing tasks to Kafka.\n\u003cimg width=\"900\" alt=\"upload-service\" src=\"https://github.com/user-attachments/assets/21d8f136-0eab-40de-9be3-21d73094c03a\" /\u003e\n\n\n- **Frame Extraction Service**: Extracts 5-10 keyframes from videos using FFmpeg, normalizes orientation for robustness, and forwards frames to Kafka for hashing.\n\n\u003cimg width=\"900\" alt=\"extraction-first\" src=\"https://github.com/user-attachments/assets/6d6e7850-062d-45bb-81b2-ddde60c6a0fb\" /\u003e\n\n...\n\n\u003cimg width=\"900\" alt=\"extraction-third\" src=\"https://github.com/user-attachments/assets/3effd378-634a-41c2-b0c2-2cd770780647\" /\u003e\n\n- **Hashing Service**: Computes pHashes for keyframes and stores it in database.\n\u003cimg width=\"1130\" alt=\"hashing-first\" src=\"https://github.com/user-attachments/assets/db3dfc52-741a-41ea-bdba-6ca57b2c4ef1\" /\u003e\n...\n\u003cimg width=\"1269\" alt=\"hashing-third\" src=\"https://github.com/user-attachments/assets/5bbfea23-7659-4613-b427-4298cd9dfe6b\" /\u003e\n\n- **Search Service**: Matches snippet videos to source videos using perceptual hash comparisons with sliding-window Hamming distance, storing results in database.\n\u003cimg width=\"999\" alt=\"cleanup\" src=\"https://github.com/user-attachments/assets/635f377e-6233-4a19-9e22-5d8541cbd4f0\" /\u003e\n\n\n\u003cimg width=\"1232\" alt=\"another snippet bc previous is cleaned up\" src=\"https://github.com/user-attachments/assets/8d11e6ca-143b-463b-be5e-10450c162156\" /\u003e\n\n\n- **API Gateway**: Centralized REST API endpoint managing requests, authentication, and response aggregation.\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n### Built With\n\n* [![Java][Java]][Java-url] [![Gradle][Gradle]][Gradle-url] [![Spring Boot][Spring]][Spring-url] [![PostgreSQL][PostgreSQL]][PostgreSQL-url] [![Kafka][Kafka]][Kafka-url]\n* [![Docker][Docker]][Docker-url] [![Kubernetes][Kubernetes]][Kubernetes-url] [![Prometheus][Prometheus]][Prometheus-url] [![Grafana][Grafana]][Grafana-url]\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\u003ca id=\"quickstart\"\u003e\u003c/a\u003e\n## Quickstart\n\n1. Clone the repository:\n   ```sh\n   git clone https://github.com/fl1s/turron.git\n   ```\n2. Navigate to the project directory:\n   ```sh\n   cd turron\n   ```\n3. Create .env.dev file from the .env.example:\n   ```sh\n   cd .config/secrets\n   ```\n   bash / zsh / powerShell\n   ```bash\n   cp .env.example .env.dev\n   ```\n   cmd\n   ```cmd\n   copy .env.example .env.dev\n   ```\n4. Start the dev environment with Docker Compose:\n   ```sh\n   docker-compose --env-file .config/secrets/.env.dev up\n   ```\n5. Verify services are running:\n   ```sh\n   docker ps\n   ```\n6. Go to the \u003ca href=\"#api\"\u003eAPI Endpoints\u003c/a\u003e\n\n\u003c!-- GETTING STARTED --\u003e\n\n## Getting Started (advanced)\n\nSet up Turron locally using Docker Compose for the dev environment or deploy to Kubernetes for production.\n\n### Prerequisites\n\nEnsure you have the following installed:\n\n* Java 21\n* Gradle\n* Docker and Docker Compose\n* kubectl (for prod environment)\n* PostgreSQL, Kafka, MinIO (or use Docker Compose)\n  ```sh\n  java --version\n  gradle --version\n  docker --version\n  ```\n\n### Installation\n\n1. Clone the repository:\n   ```sh\n   git clone https://github.com/fl1s/turron.git\n   ```\n2. Navigate to the project directory:\n   ```sh\n   cd turron\n   ```\n3. Build all microservices with Gradle:\n   ```sh\n   cd microservice-name\n   ./gradlew clean build\n   ```\n4. Start the dev environment with Docker Compose:\n   ```sh\n   docker-compose up -d\n   ```\n5. Verify services are running:\n   ```sh\n   docker ps\n   ```   \n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\u003c!-- API ENDPOINTS --\u003e\n\u003ca id=\"api\"\u003e\u003c/a\u003e\n\n## API Endpoints\n\n## Upload snippet-video\n\n**POST** `{{api-gateway}}/api/v1/upload/snippet`\n\n**Form Data:**\n\n| Field | Description    | Type       |\n|-------|----------------|------------|\n| file  | MP4 video file | file (mp4) |\n\n**Response:**\n\n```json\n{\n  \"snippetId\": \"...\"\n}\n```\n\n---\n\n## Upload source-video\n\n**POST** `{{api-gateway}}/api/v1/upload/source`\n\n**Form Data:**\n\n| Field | Description    | Type       |\n|-------|----------------|------------|\n| file  | MP4 video file | file (mp4) |\n\n**Response:**\n\n```json\n{\n  \"sourceId\": \"...\"\n}\n```\n\n---\n\n## Find best-match\n\n**GET** `{{api-gateway}}/api/v1/search/best-match/:snippetId`\n\n**Path Parameter:**\n\n| Parameter | Description      | Type   |\n|-----------|------------------|--------|\n| snippetId | Snippet video ID | string |\n\n**Response:**\n\n```json\n{\n  \"downloadUrl\": \"...\"\n}\n```\n\n## CI/CD\n\nThis project uses GitHub Actions.\n\nThe `.github/workflows/build.yml` workflow runs on every push and pull request to the `main` branch. It performs the following steps:\n\n1. Checks out the repository\n2. Sets up Java 21 using the Temurin distribution\n3. Caches Gradle dependencies to speed up builds\n4. Builds each service defined in the job matrix:\n    - `eureka-server`\n    - `upload-service`\n    - `extraction-service`\n    - `hashing-service`\n    - `search-service`\n    - `api-gateway`\n5. Authenticates to GitHub Container Registry (GHCR)\n6. Builds and pushes Docker images using a composite action located at `.github/actions/docker-build-push`\n\nEach Docker image is tagged with:\n- `latest`\n- a short Git commit SHA\n- a date-based tag in `YYYYMMDD` format\n\n\u003c!-- MONITORING --\u003e\n\n## Monitoring\n\nOur project includes out-of-the-box monitoring setup using **Prometheus** and **Grafana**.\n\n### How it works\n\n- Each microservice exposes metrics via Spring Boot Actuator on the `/actuator/prometheus` endpoint.\n- Prometheus scrapes these endpoints regularly to collect metrics.\n- Grafana connects to Prometheus as a data source to visualize metrics on dashboards.\n\n### Prometheus configuration\n\nPrometheus config is located at:  \n`monitoring/prometheus/prometheus.yml`\n\n### Folder structure\n\n* `monitoring/prometheus/` — Prometheus config files\n* `monitoring/grafana/` — Grafana dashboards and config files\n\n### Getting started\n\n1. Start **Prometheus** using the config from `monitoring/prometheus/prometheus.yml`(it's already configured in docker-compose).\n2. Start **Grafana** and add Prometheus as a data source (`http://localhost:9090`).\n3. Create your dashboards in Grafana or import community dashboards for Spring Boot metrics.\n4. Access your dashboards to monitor service health, performance, and custom metrics.\n\n## Cloudflare (optional)\n1. In Zero Trust you need to go to `Network -\u003e Tunnels`, then create a new tunnel with the name of your domain.\n2. Get the token and put its value in the field `CLOUDFLARED_TUNNEL_TOKEN` in `.config/secrets/.env.prod`.\n3. In `published application routes`, set root route to `nginx:80`.\n4. Also, don't forget to set the minio subdomain (s3.yourdomain) and route to `nginx:80`.\n5. In `Additional application settings -\u003e HTTP settings` set `HTTP Host header` value `minio:9000`\n6. In `.config/nginx/nginx.conf` change `server_name` to your domain and subdomain.\n\n\u003c!-- CONTRIBUTING --\u003e\n\n## Contributing\n\nContributions are welcome to enhance Turron! Follow these steps:\n\n1. Fork the Project.\n2. Create your Feature Branch:\n   ```sh\n   git checkout -b feature/fuzz-buzz-creature\n   ```\n3. Commit your Changes:\n   ```sh\n   git commit -m 'feat: add some fuzzBuzzCreatureFeature'\n   ```\n4. Push to the Branch:\n   ```sh\n   git push --set-upstream origin feature/fuzz-buzz-creature\n   ```\n5. Open a Pull Request.\n\nRead our [Contributing Guidelines](CONTRIBUTING.md) for more details(I also do it later).\n\n### Top Contributors\n\n\u003ca href=\"https://github.com/fl1s/turron/graphs/contributors\"\u003e\n  \u003cimg src=\"https://contrib.rocks/image?repo=fl1s/turron\" alt=\"contrib.rocks image\" /\u003e\n\u003c/a\u003e\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\u003c!-- LICENSE --\u003e\n\n## License\n\nDistributed under the Apache 2.0 License. See [LICENSE](LICENSE) for more information.\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\u003c!-- CONTACT --\u003e\n\n## Contact\n\nfl1s - [GitHub](https://github.com/fl1s)\n\nProject Link: [https://github.com/fl1s/turron](https://github.com/fl1s/turron)\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\u003c!-- MARKDOWN LINKS \u0026 IMAGES --\u003e\n\n[contributors-shield]: https://img.shields.io/github/contributors/fl1s/turron.svg?style=for-the-badge\n\n[contributors-url]: https://github.com/fl1s/turron/graphs/contributors\n\n[forks-shield]: https://img.shields.io/github/forks/fl1s/turron.svg?style=for-the-badge\n\n[forks-url]: https://github.com/fl1s/turron/network/members\n\n[stars-shield]: https://img.shields.io/github/stars/fl1s/turron.svg?style=for-the-badge\n\n[stars-url]: https://github.com/fl1s/turron/stargazers\n\n[issues-shield]: https://img.shields.io/github/issues/fl1s/turron.svg?style=for-the-badge\n\n[issues-url]: https://github.com/fl1s/turron/issues\n\n[license-shield]: https://img.shields.io/github/license/fl1s/turron.svg?style=for-the-badge\n\n[license-url]: https://github.com/fl1s/turron/blob/main/LICENSE\n\n[Java]: https://img.shields.io/badge/Java-007396?style=for-the-badge\u0026logo=java\u0026logoColor=white\n\n[Java-url]: https://www.java.com/\n\n[Spring]: https://img.shields.io/badge/Spring_Boot-6DB33F?style=for-the-badge\u0026logo=springboot\u0026logoColor=white\n\n[Spring-url]: https://spring.io/projects/spring-boot\n\n[PostgreSQL]: https://img.shields.io/badge/PostgreSQL-316192?style=for-the-badge\u0026logo=postgresql\u0026logoColor=white\n\n[PostgreSQL-url]: https://www.postgresql.org/\n\n[Kafka]: https://img.shields.io/badge/Apache_Kafka-231F20?style=for-the-badge\u0026logo=apachekafka\u0026logoColor=white\n\n[Kafka-url]: https://kafka.apache.org/\n\n[Docker]: https://img.shields.io/badge/Docker-2496ED?style=for-the-badge\u0026logo=docker\u0026logoColor=white\n\n[Docker-url]: https://www.docker.com/\n\n[Kubernetes]: https://img.shields.io/badge/Kubernetes-326CE5?style=for-the-badge\u0026logo=kubernetes\u0026logoColor=white\n\n[Kubernetes-url]: https://kubernetes.io/\n\n[Prometheus]: https://img.shields.io/badge/Prometheus-E6522C?style=for-the-badge\u0026logo=prometheus\u0026logoColor=white\n\n[Prometheus-url]: https://prometheus.io/\n\n[Grafana]: https://img.shields.io/badge/Grafana-F46800?style=for-the-badge\u0026logo=grafana\u0026logoColor=white\n\n[Grafana-url]: https://grafana.com/\n\n[Gradle]: https://img.shields.io/badge/Gradle-02303A?style=for-the-badge\u0026logo=gradle\u0026logoColor=white\n\n[Gradle-url]: https://gradle.org/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffl1s%2Fturron","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffl1s%2Fturron","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffl1s%2Fturron/lists"}