{"id":18849948,"url":"https://github.com/abinba/funny-dolphine","last_synced_at":"2026-05-10T06:37:32.477Z","repository":{"id":222863033,"uuid":"702550358","full_name":"abinba/funny-dolphine","owner":"abinba","description":"Interactive Audiobooks. Dive deep into fascinating stories.","archived":false,"fork":false,"pushed_at":"2024-02-16T16:22:37.000Z","size":2475,"stargazers_count":0,"open_issues_count":14,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-12-30T15:35:22.435Z","etag":null,"topics":["audiobooks","fastapi","postgresql","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/abinba.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-10-09T14:26:23.000Z","updated_at":"2024-03-22T12:27:17.000Z","dependencies_parsed_at":"2024-02-16T17:41:21.095Z","dependency_job_id":null,"html_url":"https://github.com/abinba/funny-dolphine","commit_stats":null,"previous_names":["abinba/funny-dolphine"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abinba%2Ffunny-dolphine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abinba%2Ffunny-dolphine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abinba%2Ffunny-dolphine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abinba%2Ffunny-dolphine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/abinba","download_url":"https://codeload.github.com/abinba/funny-dolphine/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239786251,"owners_count":19696772,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["audiobooks","fastapi","postgresql","python"],"created_at":"2024-11-08T03:27:02.962Z","updated_at":"2026-02-03T05:30:16.534Z","avatar_url":"https://github.com/abinba.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Interactive Audiobooks\n\n## Overview\n\nThe main idea of the project is to create a platform for interactive audiobooks. \n\nUsers can listen to audiobooks and follow the story by listening to the chapters and picking the choices that will lead to different outcomes when prompted.\n\nCurrent repository is a backend part of the project.\n\nConcept:\n\n\u003cimg src=\"./docs/img/Preview.png\" alt=\"Preview\"\u003e\n\n## Technology Stack\n\n- Python 3.11\n- FastAPI\n- SQLAlchemy + Alembic\n- PostgreSQL\n\n\n## Project structure overview\n```\n.\n├── src \u003c-- Main source code folder\n│   ├── app.py \u003c-- Entrypoint for FastAPI web-server\n│   ├── settings.py \u003c-- Settings file where all the environment variables are loaded\n│   ├── core \u003c-- Core folder where all the bussiness logic is stored\n│   │   └── ...\n│   ├── db \u003c-- Models, db configuration and migrations are stored here\n│   │   ├── models.py\n│   │   └── migrations \u003c-- Migrations folder\n│   ├── repo \u003c-- All the database operations related to each table are stored here\n│   │   └── ...\n│   ├── apis \u003c-- All the endpoints are stored here\n│   ├── schemas \u003c-- Pydantic schemas for each model/endpoint response are stored here\n│   ├── web \u003c-- Includes the initialization of the web-server, auth and admin\n│   │   └── ... \n│   ├── exceptions\n│   │   └── ...\n│   ├── utils\n│   │   └── ...\n│   ├── templates\n│   │   └── ...\n│   └── scripts \u003c-- Scripts for various tasks are stored here\n│       └── ...\n├── tests \u003c-- Unit/Integration tests folder\n│   └── ...\n├── README.md\n├── .gitignore\n├── .flake8\n├── alembic.ini \u003c-- Alembic configuration\n├── github/workflows/build.yaml \u003c-- CI/CD pipeline\n├── pyproject.toml \u003c-- Poetry configuration file (all packages and their corresponding versions are stored here)\n├── poetry.lock\n├── Dockerfile\n├── docker-compose.yml\n├── docker_deploy.sh \u003c-- Used for deploying the project to the server\n├── db.env\n├── web.env\n```\n\n## Environment variables\n\nMake sure that you have those environment files in the project root before running the application:\n\n- db.env - all of the database related environment variables\n- web.env\n\nExample of db.env:\n```\nPOSTGRES_PASSWORD=postgres\nPOSTGRES_DB=funny_dolphine\n```\n\nExample of web.env:\n\n```\nJWT_SECRET_KEY=secret\nADMIN_PASSWORD=***\nAPI_KEY=***\n```\n- API_KEY - API key for all the application endpoints\n- JWT_SECRET_KEY - secret key for JWT token\n- ADMIN_PASSWORD - hash of the password for admin user generated by: \n\n```python\nfrom passlib.context import CryptContext\nprint(CryptContext(schemes=[\"sha256_crypt\"], deprecated=\"auto\").hash(\"password\"))\n```\n\n\n## How to run migrations\n\nCreate a new revision first:\n```bash\n$ alembic revision --autogenerate -m \"Add new table\"\n```\n\nThen run the migration:\n```bash\n$ alembic upgrade head\n```\n\nSee more in [Alembic documentation](https://alembic.sqlalchemy.org/en/latest/tutorial.html#create-a-migration-script).\n\n## How to run tests\n\n```bash\n$ pytest\n```\n\nSee more in [pytest documentation](https://docs.pytest.org/en/stable/usage.html).\n\n## How to run the app\nConfiguration of the app can be found at src/settings.py.\n\nThe alternative way to configure those variables is to set them up in the settings.py file or using export command in the terminal.\n\n```bash\n$ uvicorn main:app --reload\n```\n\n## How to run the app with docker\n\n```bash\n$ docker-compose up --build\n```\n\n\n## Database management\n\nYou would need to create following Database trigger:\n\n```sql\n-- Create a function to update first_chapter_id in Audiobook\nCREATE OR REPLACE FUNCTION update_first_chapter_id()\nRETURNS TRIGGER AS $$\nBEGIN\n    -- Check if first_chapter_id is NULL in Audiobook\n    IF NEW.audiobook_id IS NOT NULL AND NEW.parent_id IS NULL THEN\n        UPDATE audiobook\n        SET first_chapter_id = NEW.chapter_id\n        WHERE audiobook_id = NEW.audiobook_id;\n    END IF;\n    RETURN NEW;\nEND;\n$$ LANGUAGE plpgsql;\n\n-- Create a trigger to call the function before insert on Chapter\nCREATE TRIGGER before_insert_chapter_trigger\nAFTER INSERT ON chapter\nFOR EACH ROW\nEXECUTE FUNCTION update_first_chapter_id();\n```\n\nThis will change in the future to be done automatically by alembic.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabinba%2Ffunny-dolphine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fabinba%2Ffunny-dolphine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabinba%2Ffunny-dolphine/lists"}