Compare commits

...
741 Commits
Author SHA1 Message Date
Arthur Jamet 9ea445d4ca Make sure the message queue is persisted on disk 2025-11-29 16:33:21 +00:00
Arthur Jamet 1da5276c97 Scanner: Do not rely on streams to identify file type, only use path 2025-11-29 09:52:37 +00:00
Arthur Jamet c9dbc73690 Scanner: README: Mention gocv 2025-11-29 09:52:37 +00:00
Arthur Jamet d391b061fc Matcher: Optimise matching of songs when using LrcLib + remove pika logs at startup (#1236)
* Matcher: Musicbrainz test: fix

* Matcher: When resolving providers from musicbrainz, filter out resolved providers

* tmp

* Matcher: Use async in tests
2025-11-29 08:02:22 +00:00
Arthur Jamet e8154b3709 Mobile: Change dependency for toasts 2025-11-29 07:56:09 +00:00
Arthur Jamet 66456ee9b5 Mobile: Minimised player: Display video if it is one 2025-11-27 13:05:50 +00:00
Arthur Jamet bb21f6fb07 Mobile: Player: Use HQ illustration in notification controls 2025-11-27 13:05:50 +00:00
Arthur Jamet 1d0ce69a7f Mobile: Expanded Player: Make text hierarchy more obvious 2025-11-27 13:05:50 +00:00
Arthur Jamet b3a459770f Mobile: Fix Expande dplayer not collapsing after switching tab 2025-11-27 13:05:50 +00:00
Arthur Jamet 1b6903122c Server: remove outdated warning when setting master item on the fly 2025-11-27 06:31:32 +00:00
Arthur Jamet 444d529c8a CI: Upload base settings files as artefact on release 2025-11-26 20:34:14 +00:00
Arthur Jamet 23abdf8b2a Mobile: Make the app boot on iOS 2025-11-26 17:35:40 +00:00
Arthur Jamet 2f9d31b7b4 Docker Compose for prod: remove 'latest' as default tag value 2025-11-26 17:34:38 +00:00
Arthur Jamet bc83d51cc9 .envexample: Reorder fields, make tag version clearer 2025-11-26 17:34:38 +00:00
Arthur Jamet ad0d12f89e Mobile: Fix keyboard hiding modal 2025-11-26 13:59:55 +00:00
Arthur Jamet db5743ec27 Mobile: Refacto list item to allow custom leading component 2025-11-26 12:36:47 +00:00
Arthur Jamet ea83410532 Mobile: Infinite View: Mount flashlist after controls layout to avoid shift 2025-11-26 12:36:47 +00:00
Arthur Jamet 714bda20a6 Mobile: Infinite view controls: Group actions into nowrap row 2025-11-26 12:36:47 +00:00
Arthur Jamet b8d1e26c65 Mobile: Close modal on resource type change 2025-11-26 12:36:47 +00:00
Arthur Jamet d48adefc9a Front: Sorting menu for songs: order types for usability 2025-11-26 12:36:47 +00:00
Arthur Jamet 664f204c88 Web: Close modal on resource type change
Using our modal atom instead of confirm
2025-11-26 12:36:47 +00:00
Arthur Jamet 6d876ccd11 Github: Issue templates: Set the triage tag 2025-11-26 09:38:22 +00:00
Arthur Jamet e75466aad4 Github: Enhancement issue template 2025-11-26 09:38:22 +00:00
Arthur Jamet c6c1ee4ea9 Mobile: Haptic Feedback when opening ctx menu and dragging 2025-11-26 09:28:38 +00:00
Arthur Jamet 05e28e9028 Server: Fix 'transcoder is available' status 2025-11-25 15:00:37 +00:00
Arthur Jamet 10864de858 Mobile: Row: Use flashlist 2025-11-25 14:49:14 +00:00
Arthur Jamet 433b9cb421 Remove unused eslint config 2025-11-25 11:37:44 +00:00
Arthur Jamet 2c0523aa05 DB: Fix trailing / leading dashes in sort slugs 2025-11-24 19:13:15 +00:00
Arthur Jamet 6113c0b7da Sort names (#1214) 2025-11-24 15:54:18 +00:00
Arthur Jamet be821793ad Server: Settings route says if transcoder is available 2025-11-21 10:05:24 +00:00
Arthur Jamet cb098d2451 Mobile: Player: use transcoder on demand or if format not supported 2025-11-21 10:05:24 +00:00
Arthur Jamet 23a00f1e77 Mobile: Reset all tabs when changing instance 2025-11-19 17:00:14 +00:00
Arthur Jamet 1edb13bd29 Mobile: Switch Meelo Server (#1205)
* Mobile: Auth form is in a modal

* Mobile: State: Save current/other meelo instances

* Mobile: Fix typing

* Mobile: Switch server from settings

* Mobile: Select server from auth page

* Mobile: Settings: Refresh cache when connecting a new server

* Mobile: Validate Instances objects when restoring from local storage
2025-11-17 13:53:21 +00:00
Arthur Jamet 12ed528088 Web: Volume Control (#1203)
* Web: Volume slider on minimised player

* Front: Add Volume icons

* Web: Volume slider on expanded player
2025-11-14 11:53:06 +00:00
Arthur Jamet 0008b57285 Server: Fix misleading error message when INTERNAL_CONFIG_DIR is invalid 2025-11-12 14:23:10 +00:00
Arthur Jamet c0dfc63bfe Scanner: File-system watcher (#1191)
* Scanner: Install C fs watcher

* Scanner: Setup Library watchers

* Scanner: Call go function on FS event

* Scanner: Pass library and event payload to fs event handler

* Scanner: Add tasks to queue on file system event

* Scanner: Fix DL script, setup env in CI and docker

* Scanner: README: Add setup instructions for the C library

* Scanner: TODO on fs event for modified file
2025-11-07 11:12:34 +00:00
Dylan Lathrum fd1237666c Docs: Update server's README with ENABLE_USER_REGISTRATION environment variable info
Documenting the `ENABLE_USER_REGISTRATION` environment variable located here: https://github.com/Arthi-chaud/Meelo/blob/5de296a4b6ceb8d6c04dbc8967909de59632251d/server/src/settings/settings.service.ts#L53-L55

With information from here: https://github.com/Arthi-chaud/Meelo/blob/5de296a4b6ceb8d6c04dbc8967909de59632251d/.env.example#L30-L32
2025-11-06 19:28:15 +00:00
Arthur Jamet 5de296a4b6 Docker: Fix Env var for transcoder after breaking change 2025-11-06 12:34:29 +00:00
Arthur Jamet e028f36e03 Web: Fix broken Meelo banner 2025-10-26 05:50:47 +00:00
Arthur Jamet 7c0995da95 Mobile: Minimised player: Disable play button when queue is loading 2025-10-23 17:18:25 +01:00
Arthur Jamet 79732d77de Mobile: Fullscreen player: kepp scrubbing from hiding controls 2025-10-23 17:18:25 +01:00
Arthur Jamet 875c59e754 Mobile: Player: Buffering state 2025-10-23 17:18:25 +01:00
Arthur Jamet 184711632a Mobile: Fix width of playlist's entries when loading 2025-10-23 12:42:28 +01:00
Arthur Jamet 011b7ba65b Mobile: Fix broken translation in infinite view's filters 2025-10-23 12:42:28 +01:00
Arthur Jamet 47e5a0d720 Mobile: Button: Make width prop required + make whole surface pressable 2025-10-23 12:42:28 +01:00
Arthur Jamet 2000fa5e9c Mobile: Chip: Fix color of label on state change 2025-10-23 10:37:10 +01:00
Arthur Jamet 4dc39d4bfe Mobile: Player: Clear old 'onProgress' event when track changes 2025-10-22 08:21:11 +01:00
Arthur Jamet dbb7e1764b Mobile: Handle play/pause event from media controls 2025-10-22 08:21:11 +01:00
Arthur Jamet 7f328dd28d Front: API: Get Illustration Url: Add token as query param 2025-10-22 08:21:11 +01:00
Arthur Jamet 12f21572cd Server: Auth: Allow passing token as query param 2025-10-22 08:21:11 +01:00
Arthur Jamet 6772d93321 Mobile: Setup Expo plugin for notification controls 2025-10-22 08:21:11 +01:00
Arthur Jamet b94c4beb81 Mobile: Keep Screen Awake on Fullscreen player 2025-10-22 08:21:11 +01:00
Arthur Jamet 04e4c89b91 Front: Bump to React 19, Next 15, React-Native 0.81 and Expo 54 (#1184) 2025-10-21 10:45:41 +01:00
Arthur Jamet 54155c55d8 Rare songs: Include original songs from Remix albums 2025-10-13 08:55:41 +01:00
Arthur Jamet 4013ce133c Mobile: Fullscreen player: await unlock before closing 2025-10-11 11:40:15 +01:00
Arthur Jamet 2b2f921199 Mobile: Fullscreen player: fix crash w/ infinite grid and screen rotation 2025-10-11 11:40:15 +01:00
Arthur Jamet a7c90aba69 Mobile: Fullscreen player: get featuring artists 2025-10-11 11:40:15 +01:00
Arthur Jamet 45037e4695 Mobile: Fullscreen player: play/pause button 2025-10-11 11:40:15 +01:00
Arthur Jamet 2e646589dd Mobile: Fullscreen player: slider control 2025-10-11 11:40:15 +01:00
Arthur Jamet 77b61ad035 Mobile: Player Slider: Makie it reusable 2025-10-11 11:40:15 +01:00
Arthur Jamet 4263d18eb1 Mobile: Fullscreen player: hide navbar 2025-10-11 11:40:15 +01:00
Arthur Jamet 54dfaba0ad Mobile: Fullscreen player: add back button 2025-10-11 11:40:15 +01:00
Arthur Jamet 79b68b707e Mobile: Fullscreen player: auto hide controls 2025-10-11 11:40:15 +01:00
Arthur Jamet 1d35fe1fd2 Mobile: Fullscreen player: add video component + safe area 2025-10-11 11:40:15 +01:00
Arthur Jamet ed5850d916 Mobile: Rearrange layouts so that video player does not have tab bar 2025-10-11 11:40:15 +01:00
Arthur Jamet 075e847f4e Mobile: expanded player: button to push fullscreen player page 2025-10-11 11:40:15 +01:00
Arthur Jamet cacecc2a31 Mobile: Define empty video player route 2025-10-11 11:40:15 +01:00
Arthur Jamet db2e4c485a Mobile: Lock Orientation when device is mobile 2025-10-11 11:40:15 +01:00
Arthur Jamet 8bd8e21891 Revert "Server: Make search ordering fairer in regard of artists"
This reverts commit 60f1821824.
2025-10-06 12:09:36 +01:00
Arthur Jamet 15d31e03a3 Mobile: Resource Header: reduce illustration max width 2025-10-04 14:47:48 +01:00
Arthur Jamet 07a8a5b40a Mobile: External Metadata badge: Fix icon size 2025-10-04 14:47:48 +01:00
Arthur Jamet 3a929143eb Mobile: Genre Chip, List Item and Pages (#1178)
* Mobile: Infinite song Page: Query param by genre

* Mobile: Infinite album Page: Query param by genre

* Mobile: List Item: allow not to have illustration

* Mobile: Base Infinte Genre Page

* Mobile: Genre Context Menu

* Mobile: Genre Chip

* Mobile: Home Page: Top Genre List

* Mobile: Lint
2025-10-04 13:48:11 +01:00
Arthur Jamet 3881af9789 Contribution guidelines 2025-10-03 12:49:31 +01:00
Arthur Jamet 60f1821824 Server: Make search ordering fairer in regard of artists 2025-10-03 11:26:55 +01:00
Arthur Jamet b652bc6691 Matcher: Remove debug print 2025-10-02 15:46:38 +01:00
Arthur Jamet 7b923040b8 Matcher: requests to wikipedia/wikidata include User agent 2025-10-02 15:46:38 +01:00
Arthur Jamet 46611b3867 Matcher: Protect '/queue' endpoint, users has to be admin 2025-10-02 15:46:38 +01:00
Arthur Jamet afb40f0d67 Front: Web: Get Matcher's progress 2025-10-02 15:46:38 +01:00
Arthur Jamet 796a3202b2 Front: Mobile: Refacto Version numbers in settings 2025-10-02 15:46:38 +01:00
Arthur Jamet 6a33f39c4c Front: Mobile: Add Matcher version in settings 2025-10-02 15:46:38 +01:00
Arthur Jamet 761340f1cc Front: web: show version in settings 2025-10-02 15:46:38 +01:00
Arthur Jamet 9596d3f506 Front: Web: Add Matcher URL to docker compose 2025-10-02 15:46:38 +01:00
Arthur Jamet bf760a26e4 Front: API: Add Matcher Service + Add version query 2025-10-02 15:46:38 +01:00
Arthur Jamet 4f387787fd Matcher: Queue endpoint also returns current item 2025-10-02 15:46:38 +01:00
Arthur Jamet fe46f5d058 Matcher: Queue endpoint also returns handled items count 2025-10-02 15:46:38 +01:00
Arthur Jamet 060f044b6e Matcher: Use blocking pika + endpoint to get queue size 2025-10-02 15:46:38 +01:00
Arthur Jamet 313c9b4cd9 Matcher: Index endpoint return build version 2025-10-02 15:46:38 +01:00
Arthur Jamet 639ef3f9ab Matcher: Dev Docker: Hotreload 2025-10-02 15:46:38 +01:00
Arthur Jamet d70fd8f462 Matcher: Setup FastAPI + use async for Rabbitmq client 2025-10-02 15:46:38 +01:00
Arthur Jamet 98696fef0d CI: Matcher: Align container version with Dockerfile's 2025-10-02 15:46:38 +01:00
Arthur Jamet d51c653873 Docker + Nginx: Export Port for matcher + add reverse proxy rule 2025-10-02 15:46:38 +01:00
thorbm 9053949b7c Translated using Weblate (Danish)
Currently translated at 66.5% (211 of 317 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/da/
2025-09-20 14:22:06 +02:00
Arthur Jamet 1d96feb542 Web: Lint 2025-09-18 09:59:28 +02:00
Arthur Jamet 3ce19ebc6c Front: Release Tracklist: Use setState instead of useMemo to make first render lighter 2025-09-18 09:59:28 +02:00
Arthur Jamet 1eef453676 Mobile: Stacks: freeze on blur 2025-09-18 09:59:28 +02:00
Arthur Jamet 898d251203 Mobile: illustration: optimise fallback icon 2025-09-18 09:59:28 +02:00
Arthur Jamet abdc723753 Mobile: Pressable: addlow disabling requesting animation frame 2025-09-18 09:59:28 +02:00
Arthur Jamet 430df2bedc Mobile: Release Page: Mount footer after release is loaded 2025-09-18 09:59:28 +02:00
Arthur Jamet d555e5d0fe Mobile: Chip: Disabled state 2025-09-18 09:59:28 +02:00
Arthur Jamet a2deae1b5f Mobile: Pressable: Disable callback according to 'disabled' prop 2025-09-18 09:59:28 +02:00
Arthur Jamet bc65da272a Mobile: Song Page: Mount tabs after song is loaded 2025-09-18 09:59:28 +02:00
Arthur Jamet c595c279c1 Mobile: Playlist Page: Mount footer after playlist is loaded 2025-09-18 09:59:28 +02:00
Arthur Jamet ba58e11e84 Mobile: Artist Page: Mount footer after artist is loaded 2025-09-18 09:59:28 +02:00
Arthur Jamet ac9cbd407b Mobile: Lint 2025-09-14 11:13:11 +02:00
Arthur Jamet 8c45620504 Biome: Unused import/var triggers error 2025-09-14 11:13:11 +02:00
Arthur Jamet 417dae6beb Front: Player State: Add 'featuring' field (#1166)
* Front: Player State: Add featuring artist state

* Front: Web: Pass featuring artists to player state when possible

* Front: Mobile: Pass featuring artists to player state when possible
2025-09-14 11:12:31 +02:00
Arthur Jamet e04b06bc10 Server: Docker: Handle SIGINT 2025-09-14 10:35:54 +02:00
Arthur Jamet 143e83b397 Server: Bump Axios 2025-09-13 19:13:17 +01:00
Arthur Jamet 645c6ceb83 Front: Setup danish translation 2025-09-12 11:57:13 +01:00
Weblate (bot)andthorbm fa65386b8d Translations update from Hosted Weblate (#1164)
* Added translation using Weblate (Danish)

* Translated using Weblate (Danish)

Currently translated at 63.9% (202 of 316 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/da/

---------

Co-authored-by: thorbm <weblate@prodzeus.dev>
2025-09-12 07:34:53 +01:00
Arthur Jamet 0e7236cf55 CI: Mobile: Fix permission to add apk to release 2025-09-10 15:58:10 +01:00
Arthur Jamet af758b495f Front: 'Feat.' Prefix when formatting artists 2025-09-09 17:15:29 +01:00
Arthur Jamet 4a80b9f015 Front: Mobile: Use Song Type Icons 2025-09-09 09:34:29 +01:00
Arthur Jamet f1d22087ce Front: Move song type icon function to ui package 2025-09-09 09:34:29 +01:00
Arthur Jamet 1d3bdca573 Front: Mobile: Release Context Menu: Add 'change type' action and hide 'go to release' action if on release page 2025-09-08 15:28:56 +01:00
Arthur Jamet 0050d9f276 Front: Mobile: Release Page: Add context menu to header 2025-09-08 15:28:56 +01:00
Arthur Jamet f73fe379d2 Front: Mobile: Allow horizontal view on tablets 2025-09-08 15:28:56 +01:00
Arthur Jamet ed8120be4c Front: Mobile: Release Page: Constraint width of release name 2025-09-08 15:28:56 +01:00
Arthur Jamet c2330a61d5 Front: Mobile: Playlist forms: center content 2025-09-08 15:28:56 +01:00
Arthur Jamet b8955bc50a Front: Mobile: Release Page: Constraint width of header controls 2025-09-08 15:28:56 +01:00
Arthur Jamet c3308a6949 Front: Mobile: Button: disable sound on press 2025-09-08 15:28:56 +01:00
Arthur Jamet b1cc5cead1 Front: Mobile: Expanded Player: Set max width for larger devices 2025-09-08 15:28:56 +01:00
Arthur Jamet d457656273 Front: Mobile: Minimised Player: Add rewind button on larger devices 2025-09-08 15:28:56 +01:00
Arthur Jamet 81e384263f Front: Mobile: Move 'rewind track' logic to atom 2025-09-08 15:28:56 +01:00
Arthur Jamet 3c03af1a13 Front: Mobile: Song Grid: Responsive on larger devices 2025-09-08 15:28:56 +01:00
Arthur Jamet bf871060b9 Mobile: Resource Header: Add context menu (#1157)
* Server: add album, artist and playlist to playlist

* Front: API method to add to playlist

* Front: Web: Use 'addToPlaylist' API method

* Front: Web: Add artist, album, release and playlist to playlist

* Server: Add artist's songs to playlist: sort songs by name

* Front: Mobile: Add artist, album, release and playlist to playlist

* Front: Mobile: Resource header: add context menu
2025-09-06 15:52:19 +00:00
Arthur Jamet cf2e1a8993 Server: add album, artist and playlist to playlist (#1155)
* Server: add album, artist and playlist to playlist

* Front: API method to add to playlist

* Front: Web: Use 'addToPlaylist' API method

* Front: Web: Add artist, album, release and playlist to playlist

* Server: Add artist's songs to playlist: sort songs by name

* Front: Mobile: Add artist, album, release and playlist to playlist

* Server: Playlist service: factor 'addToPlaylist'

* Front: fix typo
2025-09-06 16:05:54 +01:00
Arthur Jamet 48dfbab10f Front: Mobile: Animate opacity of synced lyrics 2025-09-05 18:58:45 +01:00
Arthur Jamet 8db41e250b Front: Web: Settings: Fix alignment of scrobbler buttons on small viewports 2025-09-05 18:58:45 +01:00
Arthur Jamet b376b76922 Front: Mobile Fix drop shadow on non-square images 2025-09-05 09:12:00 +01:00
Arthur Jamet cd578f1edd README: New screenshots + minor refacto (#1153)
* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md
2025-09-04 19:28:39 +01:00
Arthur Jamet 4c2b0dab2e Front: Mobile: Release Page: Fading header 2025-09-04 10:26:49 +01:00
Arthur Jamet 12128d36e7 Front: Mobile: move back button/header to own file 2025-09-04 10:26:49 +01:00
Arthur Jamet 73aa611fca Front: Web: Bump Next 2025-09-03 20:04:48 +01:00
Arthur Jamet 45b91a92fa Front: Mobile: Search: filter 2025-09-03 19:50:44 +01:00
Arthur Jamet 391daf9de1 Sorting Videos by release date 2025-09-03 15:35:00 +01:00
Arthur Jamet c63e7c87b8 Front: Mobile: Add shadow behind illustrations 2025-09-03 14:21:03 +01:00
Arthur Jamet f0966819cc Front: Mobile: TextInput: Remove background + refactor placeholder 2025-09-03 14:21:03 +01:00
Arthur Jamet 50513adc7d Front: Mobile: Settings: add button to go web app 2025-09-03 14:21:03 +01:00
Arthur Jamet 7ddf4a1766 Front: Mobile: Rating: Remove color prop 2025-09-03 14:21:03 +01:00
Arthur Jamet abc3c317d5 Front: Mobile: Playlist: DRY playlist deletion 2025-09-02 18:15:18 +01:00
Arthur Jamet 69e1d87a4d Front: Mobile: Fix dismiss on playlist deletion 2025-09-02 18:15:18 +01:00
Arthur Jamet 7e2d7f964c Front: Mobile: Rename callback of playlist form 2025-09-02 18:15:18 +01:00
Arthur Jamet d1b1f9b1c6 Front: Lint 2025-09-02 18:15:18 +01:00
Arthur Jamet f026ec4be1 Front: Mobile: Update Playlist illustration 2025-09-02 18:15:18 +01:00
Arthur Jamet 5eb7289370 Front: Add translation key for illustration form 2025-09-02 18:15:18 +01:00
Arthur Jamet 8f7a945cb6 Front: Mobile: Share Playlist 2025-09-02 18:15:18 +01:00
Arthur Jamet 034b4571bf Front: Mobile: Add song to playlist 2025-09-02 18:15:18 +01:00
Arthur Jamet 332181f2c6 Front: Add translation for 'song added to playlist' toast 2025-09-02 18:15:18 +01:00
Arthur Jamet a9fba09d40 Front: Mobile: Delete playlist entry from context menu 2025-09-02 18:15:18 +01:00
Arthur Jamet ee34160d65 Front: Mobile: Hook to delete playlist 2025-09-02 18:15:18 +01:00
Arthur Jamet e843eee6d0 Front: Mobile: Playlist context menu 2025-09-02 18:15:18 +01:00
Arthur Jamet dee5b9e956 Front: Mobile: Playlists View: add layout toggle 2025-09-02 18:15:18 +01:00
Arthur Jamet 9cb5c8b9d0 Front: Mobile: Update playlist form 2025-09-02 18:15:18 +01:00
Arthur Jamet 5c003f0fdf Front: Playlist: Add 'reorder' icon and translation key 2025-09-02 18:15:18 +01:00
Arthur Jamet ed1937c400 Front: Mobile: 'create playlist' action + add action to /playlists 2025-09-02 18:15:18 +01:00
Arthur Jamet a8fd10dc96 Front: Mobile: Checkbox primitive 2025-09-02 18:15:18 +01:00
Arthur Jamet 7e1be3220e Front: Mobile: Playlist page: Reorder 2025-09-02 18:15:18 +01:00
Arthur Jamet 598db88384 Front: Mobile: Playlist page: delete button 2025-09-02 18:15:18 +01:00
Arthur Jamet 35536c4b0e Front: Mobile: Playlist page: Loading state 2025-09-02 18:15:18 +01:00
Arthur Jamet 0ee5e70f67 Front: Mobile: Release Page: Related playlists section 2025-09-02 18:15:18 +01:00
Arthur Jamet 2c0621312d Front: Mobile: Infinite Playlist view and playlist page 2025-09-02 18:15:18 +01:00
Arthur Jamet 9426ba2614 Front: Mobile: Expanded Player: Fix background change on theme change 2025-09-02 18:15:18 +01:00
Arthur Jamet 8ae808c8fe Front: Mobile: Lint 2025-09-02 18:15:18 +01:00
Xavier N f3338fe7d3 Translated using Weblate (German)
Currently translated at 85.5% (266 of 311 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/de/
2025-08-30 11:01:59 +01:00
Arthur Jamet 76dcea872c Front: Mobile: Expanded Player: on Track name click, redirect to release if exists 2025-08-28 06:54:26 +01:00
Arthur Jamet 0301a0911e Front: Mobile: Synced Lyrics 2025-08-28 06:54:26 +01:00
Arthur Jamet f2c65644b0 Front: Mobile: Add 'Show Track Info' action 2025-08-27 09:57:21 +01:00
Arthur Jamet c667d79372 Front: Mobile: Add 'play' action to album and release context menus 2025-08-25 19:11:19 +01:00
Arthur Jamet 2dd6c4d501 Front: Web: Add 'play' action to album and release context menus 2025-08-25 19:11:19 +01:00
Arthur Jamet f07fc8e830 Front: Web: Form to change password 2025-08-25 16:32:32 +01:00
Arthur Jamet 13fe63b31b API: Allow a user to update their password 2025-08-25 16:32:32 +01:00
Arthur Jamet 4833b2b102 Scanner: Dockerfile: Use external image to build gocv 2025-08-25 09:22:46 +01:00
Arthur Jamet 15c73b371a Front: Web: Fix context menu behind expanded player 2025-08-24 22:32:16 +01:00
Arthur Jamet daf5491e63 Scanner: Face Detection: Close img (#1140) 2025-08-24 17:26:44 +01:00
Arthur Jamet b21f20f581 Revert "CI: Dockerisation: Add 'docker-arch' parameter to composite action"
This reverts commit b3951dc8e9.
2025-08-24 14:45:53 +01:00
Arthur Jamet b3951dc8e9 CI: Dockerisation: Add 'docker-arch' parameter to composite action 2025-08-24 13:31:30 +01:00
Arthur Jamet e09f0adb7c Use OpenCV for better thumbnails (#1136)
* Scanner: Use OpenCV to find the best thumbnail

* Scanner: Dev Dockerfile: Setup OpenCV

* Scanner: Prod Dockerfile: Setup OpenCV

* Scanner: Add OpenCV model

* CI: Scanner: Setup OpenCV
2025-08-23 17:58:11 +01:00
Arthur Jamet 806f61eb15 Front: Web: Fix broken infinite Scroll in modals 2025-08-21 14:19:36 +01:00
Arthur Jamet 680fa9881d Front: Web: Lint 2025-08-21 14:14:22 +01:00
Arthur Jamet a27731a454 Front: Web: Expanded player uses atom to open modal 2025-08-21 14:14:22 +01:00
Arthur Jamet f0cb0e5fd3 Front: Web: Contextual menu uses atom to open modal 2025-08-21 14:14:22 +01:00
Arthur Jamet 1241a77ffe Front: Web: Use modal atom for libraries management 2025-08-21 14:14:22 +01:00
Arthur Jamet 3f0ec49009 Front: Web: Keyboard Bindings modal: use atom 2025-08-21 14:14:22 +01:00
Arthur Jamet 7e6c45134b Front: Web: Modal: set zindex 2025-08-21 14:14:22 +01:00
Arthur Jamet fc25501e72 Front: Web: Use modal atom for 'create playlist' action 2025-08-21 14:14:22 +01:00
Arthur Jamet 185bc814d2 Front: Web: Use modal atom for libraries' settings 2025-08-21 14:14:22 +01:00
Arthur Jamet 13682dafcd Front: Web: Setup atom for modal 2025-08-21 14:14:22 +01:00
Arthur Jamet 4e4269c5a7 Front: Mobile: Background Radiant: Remove ratio of radius for colors 2025-08-21 10:59:53 +01:00
Arthur Jamet bf0be67c88 Front: Mobile: Simplyify stacks w/ shared routes 2025-08-21 10:59:53 +01:00
Arthur Jamet df2efa9383 Matcher: Search Album Type is original type is Live 2025-08-19 17:31:06 +01:00
Arthur Jamet 4fd1bdd439 Server: Parser service: ignore 'live' substrings for live album detection 2025-08-19 17:31:06 +01:00
Arthur Jamet 71a51dd0b5 Front: Mobile: Text: Hyphenate for long words 2025-08-17 20:00:54 +01:00
Arthur Jamet e971112a6d Front: Mobile: Expanded Player: Collapse on navigate 2025-08-17 20:00:54 +01:00
Arthur Jamet 233da57c77 Front: Mobile: Remove animation on navigation 2025-08-17 20:00:54 +01:00
Arthur Jamet 84d3b2c9ae Front: Mobile: Remove outdated comment 2025-08-17 20:00:54 +01:00
Arthur Jamet 6ac4f53163 Front: Mobile: Minimised player: Fix progress state when no track 2025-08-17 20:00:54 +01:00
Arthur Jamet 2817be8b76 Front: Mobile: Pressable: Use 'requiest animation frame' 2025-08-17 20:00:54 +01:00
Arthur Jamet 181d3b4ab7 Front: Mobile: Use .navigate instead of .push 2025-08-17 20:00:54 +01:00
Arthur Jamet ca8c3041a6 Front: Mobile: Track Context Menu: Fix loading state of release name 2025-08-17 20:00:54 +01:00
Arthur Jamet e0a8edc877 Front: Mobile: Yarn.lock: add patch for nitro 2025-08-17 20:00:54 +01:00
Arthur Jamet 08c9e9c6fd Front: Mobile: Player: Loop back when play at the end of the queue 2025-08-17 12:35:39 +01:00
Arthur Jamet 44f110110c Front: Mobile: Expanded Player: Slider: fix progress/duration when no track 2025-08-17 12:35:39 +01:00
Arthur Jamet 89cd8b3916 Front: Mobile: Pressable: Disable sound on touch 2025-08-17 12:35:39 +01:00
Arthur Jamet e9899006dc Front: Mobile: Expanded Player: Video 2025-08-17 12:35:39 +01:00
Arthur Jamet f3dc731f54 Front: Mobile: Player: Scrobble 2025-08-17 12:35:39 +01:00
Arthur Jamet c0e313ce84 Front: Mobile: Expanded player: use atoms and callbacks from context 2025-08-17 12:35:39 +01:00
Arthur Jamet f1dd211ad5 Front: Mobile: Player: 'requestProgress' atom 2025-08-17 12:35:39 +01:00
Arthur Jamet 5bd4480d82 Front: Mobile: Minimised player: sync progress bar and play state with player 2025-08-17 12:35:39 +01:00
Arthur Jamet c4f7ae5b7d Front: Mobile: setup player context 2025-08-17 12:35:39 +01:00
Arthur Jamet 52c9fd40b5 Front: Mobile: Bump deps for react-native-video 2025-08-17 12:35:39 +01:00
Arthur Jamet 43b99cea41 Front: Mobile: use SelectBottomModalContent for 'Change Type' actions 2025-08-14 18:59:45 +01:00
Arthur Jamet 88fd5c14fb Front: Mobile: Replace Dropdown with bottom sheet modal 2025-08-14 06:45:48 +01:00
Arthur Jamet 17408db2c4 Front: Mobile: Chip: Fix color and add icon prop 2025-08-14 06:45:48 +01:00
Arthur Jamet 6982cec8cd Front: Mobile: Rewrite Emptystate so that parent doesn't have to set a height 2025-08-14 06:45:48 +01:00
Arthur Jamet ca034ed6b2 Front: Mobile: Settings: Show API and scanner versions 2025-08-13 14:41:10 +01:00
Arthur Jamet 27bac2330c CI: Mobile: Add trigget commit/tag to expo env 2025-08-13 14:41:10 +01:00
Arthur Jamet b449f35fa6 Front: Mobile: Build commit in settings 2025-08-13 14:41:10 +01:00
Arthur Jamet 22d24b4626 Front: Mobile: APP_VARIANT env var, app.config.ts and set name to dev apk 2025-08-13 12:59:00 +01:00
Arthur Jamet 91a6ea199e Front: Setup Portuguese translation 2025-08-13 11:54:29 +01:00
Weblate (bot) f3f3294430 Translations update from Hosted Weblate (#1106) 2025-08-13 10:38:08 +01:00
Arthur Jamet 59d91fd699 Front: Mobile: Bump deps to prevent tabs from crashing on press 2025-08-13 08:21:42 +01:00
Arthur Jamet 37bab7b974 Front: Mobile: Expanded Player: theme for Handle 2025-08-12 10:25:36 +01:00
Arthur Jamet 5c4335673c Front: Mobile: Settings: Clear query cache + empty playlist on logout 2025-08-12 10:25:36 +01:00
Arthur Jamet 2cc7826690 Front: Mobile: Auth: Split flow into two steps, checking if signup is enabled 2025-08-12 10:25:36 +01:00
Arthur Jamet 29e77c3b80 Front: Mobile: Remove todo, move style to unistyles and fix typo 2025-08-11 13:39:32 +01:00
Arthur Jamet 90d38f0385 Front: Mobile: Track Context Menu: Fix 'go to song info/lyrics' actions 2025-08-11 13:39:32 +01:00
Arthur Jamet d0be9d29ea Front: Player: Factorise queries + open ctx menu on minimised player long press 2025-08-11 13:39:32 +01:00
Arthur Jamet c8e9a1862d Front: Minimised Player: Reduce size of buttons 2025-08-11 13:39:32 +01:00
Arthur Jamet a3849909b4 Front: Mobile: Expanded Player: Reorder Queue 2025-08-11 13:39:32 +01:00
Arthur Jamet 4f244fba75 Front: Mobile: Reimplement pressable to about doble visual feedback 2025-08-11 13:39:32 +01:00
Arthur Jamet 113657bd74 Front: Mobile: Expanded Player: Queue, with calbacks on press and delete 2025-08-11 13:39:32 +01:00
Arthur Jamet cad591175f Front: Mobile: Expanded Player: (plain) Lyrics tab 2025-08-11 13:39:32 +01:00
Arthur Jamet f75f848fee Front: Mobile: Expanded Player: Slider + setup tabs 2025-08-11 13:39:32 +01:00
Arthur Jamet 4264a4d7f9 Front: Mobile: Player: Facotr components w/ minimised plyaer + context menu 2025-08-11 13:39:32 +01:00
Arthur Jamet 116e3c39b5 Front: Mobile: Atom to get current track 2025-08-11 13:39:32 +01:00
Arthur Jamet 82381e3dbf Front: Mobile: Constant for color backgorud opacity 2025-08-11 13:39:32 +01:00
Arthur Jamet e57ee3a7f2 Front: Mobile: Setup expanded state and bottom sheet modal 2025-08-11 13:39:32 +01:00
Arthur Jamet b1e5a66317 Front: Mobile: set apparence when color scheme preference is changed 2025-08-11 13:39:32 +01:00
Arthur Jamet 6742145ee3 Front: Mobile: Settings Page: Add header 2025-08-11 13:39:32 +01:00
Arthur Jamet 2454427d03 Front: Mobile: Release Page: Use accent color for ratings 2025-08-11 13:39:32 +01:00
Arthur Jamet 99eeb4ce14 Front: Mobile: Accent color hook + color transition in minimised player 2025-08-11 13:39:32 +01:00
Arthur Jamet c7f4c32d9f Front: Mobile: Section Header: Avoid overflow 2025-08-11 13:39:32 +01:00
Arthur Jamet 3a0ac5a3ba Front: Mobile: Illustration: Blurash is opt-in 2025-08-11 13:39:32 +01:00
Arthur Jamet 98a357c7b3 Front: Mobile: Implem Actions to play tracks 2025-08-11 13:39:32 +01:00
Arthur Jamet 0bd6a41aa2 Front: Mobile: Minimised player: Make the slide-in animation smoother 2025-08-11 13:39:32 +01:00
Arthur Jamet 49fe79b24f Front: Tracks View: Add callback on item press 2025-08-11 13:39:32 +01:00
Arthur Jamet 8cd500aade Front: Mobile: Minimised player: Fix height of illustration 2025-08-11 13:39:32 +01:00
Arthur Jamet ef945bf270 Front: Mobile: Songs and Videos Pages: Add play and shuffle actions 2025-08-11 13:39:32 +01:00
Arthur Jamet 64932d6cd6 Front: Mobile: Infinite View: Make buttons small + add actions 2025-08-11 13:39:32 +01:00
Arthur Jamet 24dcc6a130 Front: Mobile: Minimised player: Fix video thumbnail overflow 2025-08-11 13:39:32 +01:00
Arthur Jamet dc91ea47db Front: Mobile: Song and Video item: Callback is not optional 2025-08-11 13:39:32 +01:00
Arthur Jamet fa641637cd Front: Mobile: Artist Page: Callback on video press 2025-08-11 13:39:32 +01:00
Arthur Jamet a1faa5f631 Front: Mobile: release page: remove deps to useCallback 2025-08-11 13:39:32 +01:00
Arthur Jamet deabd537a1 Front: Mobile: videos Page: start video on tap 2025-08-11 13:39:32 +01:00
Arthur Jamet adca86f7af Front: Mobile: Songs Page: start song on tap 2025-08-11 13:39:32 +01:00
Arthur Jamet 7dbbce2e7c Front: Mobile: Search Page: Start playback on song/video tap 2025-08-11 13:39:32 +01:00
Arthur Jamet 14eb6b5d79 Front: Mobile: Infinite View: render fn passes index and full list 2025-08-11 13:39:32 +01:00
Arthur Jamet b127a2c3df Front: Mobile: Show minimised player when infinite playlist is loading 2025-08-11 13:39:32 +01:00
Arthur Jamet 7740028509 Front: Mobile: Release Page: Callback on video tile press 2025-08-11 13:39:32 +01:00
Arthur Jamet fd466eecbc Front: Mobile: Release Page: Callback for play and shuffle buttons 2025-08-11 13:39:32 +01:00
Arthur Jamet 8962e29d87 Front: Mobile: Minimised player: Limit number of lines 2025-08-11 13:39:32 +01:00
Arthur Jamet 521d004d15 Front: Mobile: Song Page: Add Callback on play button 2025-08-11 13:39:32 +01:00
Arthur Jamet ad5395f9af Front: Mobile: Release tracklist: Add 'isPlaying' icon + playback callbacks 2025-08-11 13:39:32 +01:00
Arthur Jamet ddd09c0e2c Front: Mobile: Minimised player UI 2025-08-11 13:39:32 +01:00
Arthur Jamet 44f942d1c4 Front: Mobile: Song Grid: Add songs to queue on tap + remove href in song item 2025-08-11 13:39:32 +01:00
Arthur Jamet 7f22679807 Front: Mobile: Illustration: Allow isThumbnail props to be false 2025-08-11 13:39:32 +01:00
Arthur Jamet c6b81a71a3 Front: Mobile: Rewrite bottom tab for future player 2025-08-11 13:39:32 +01:00
Arthur Jamet b431ca1daf Front: Mobile: Pressable: Pass event to callback 2025-08-11 13:39:32 +01:00
Arthur Jamet 19536cb065 Front: Mobile: Build for prod produces APK 2025-08-05 08:14:21 +01:00
Arthur Jamet e493990870 Matcher: Search Albums w/ Musicbrainz: Sanitise ampersand 2025-08-04 11:45:55 +01:00
Arthur Jamet 5126848b13 Front: Web: i18n: Handle Fallback on empty strings 2025-08-04 09:00:38 +01:00
Arthur Jamet 324e0aad89 CI: Mobile: Fix trigger for build 2025-08-04 08:10:48 +01:00
Arthur Jamet b747737b92 Server: Bump Jest 2025-08-03 18:25:37 +01:00
Arthur Jamet 2f281d393c Server: Bump Deps (#1097)
* Server: Bump form-data

* Server: Bump multer

* Server: Bump tar and braces
2025-08-03 16:47:28 +00:00
bad57d1de6 Translations update from Hosted Weblate (#1092)
* Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.2% (286 of 291 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/pt_BR/

* Translated using Weblate (French)

Currently translated at 98.3% (301 of 306 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/fr/

* Translated using Weblate (French)

Currently translated at 98.3% (301 of 306 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/fr/

* Translated using Weblate (Russian)

Currently translated at 95.0% (291 of 306 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/ru/

* Translated using Weblate (Indonesian)

Currently translated at 12.7% (39 of 306 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/id/

* Translated using Weblate (Indonesian)

Currently translated at 12.7% (39 of 306 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/id/

* Translated using Weblate (Italian)

Currently translated at 64.3% (197 of 306 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/it/

* Translated using Weblate (Italian)

Currently translated at 64.3% (197 of 306 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/it/

* Translated using Weblate (German)

Currently translated at 26.4% (81 of 306 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/de/

* Translated using Weblate (Portuguese)

Currently translated at 0.0% (0 of 306 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/pt/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 89.2% (273 of 306 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 89.2% (273 of 306 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/pt_BR/

* Translation: Fix plurals in portuguese

---------

Co-authored-by: Felipe Marinho <felipevm97@gmail.com>
Co-authored-by: Lumiraptor <hmtz@gmx.fr>
Co-authored-by: Arthur Jamet <arthur.jamet@gmail.com>
2025-08-03 17:16:04 +01:00
Arthur Jamet 249aff7c3a Front: Release Date's format: Handle underscore 2025-08-03 17:13:38 +01:00
Arthur Jamet 87414962e4 Server: Bump Biome 2025-08-02 19:23:44 +01:00
Arthur Jamet bfa06bc090 Front: Bump Biome 2025-08-02 19:23:44 +01:00
Arthur Jamet 87a5a025ed Front: Mobile: Remove todos 2025-08-02 13:15:30 +01:00
Arthur Jamet d198c29928 Front: Mobile: Action to set track as master 2025-08-02 13:15:30 +01:00
Arthur Jamet e559b060e8 Front: Mobile: Release Context Menu needs Album relation + set as master action 2025-08-02 13:15:30 +01:00
Arthur Jamet 81dfb8af0f Front: Mobile: Release Tracklist: Context menu for tracks + styling + centering video icons 2025-08-02 13:15:30 +01:00
Arthur Jamet 14cb6553d8 Front: Mobile: On Video Type change, invalidate release 2025-08-02 13:15:30 +01:00
Arthur Jamet 6a60b6f67c Front: Mobile: Release Page: Add context menus to tracklist 2025-08-02 13:15:30 +01:00
Arthur Jamet 4733ccda69 Front: Mobile: Pressable: add longpress callback 2025-08-02 13:15:30 +01:00
Arthur Jamet a02261b85f Front: Mobile: Items: disable/hide context menu button when item is loading 2025-08-02 13:15:30 +01:00
Arthur Jamet 7079e7d309 Front: Mobile: Context Menu: dedicated header styles + adapt illustration width if thumbnail 2025-08-02 13:15:30 +01:00
Arthur Jamet 73e61cf3bf Front: Mobile: Video Context Menu: Pass illustration props 2025-08-02 13:15:30 +01:00
Arthur Jamet 0a0e5b83b7 Front: Release Page Hook: Relax constraint on video type 2025-08-02 13:15:30 +01:00
Arthur Jamet 4ae1f720f4 Front: Mobile: Fix contraint on video relations for context menu 2025-08-02 13:15:30 +01:00
Arthur Jamet 6faffbc3e6 Front: Lint 2025-08-02 13:15:30 +01:00
Arthur Jamet 5d31682a53 Front: Mobile: Vide Contextual menu 2025-08-02 13:15:30 +01:00
Arthur Jamet 3413214748 Front: Mobile: Video Model: add master id field 2025-08-02 13:15:30 +01:00
Arthur Jamet 35ea0ced98 Front: Mobile: Release Tracklist: Handles tracks that don't have a song 2025-08-02 13:15:30 +01:00
Arthur Jamet c3cb7e62e5 Front: Mobile: Videos View: Add song header + Song context menu: go to videos 2025-08-02 13:15:30 +01:00
Arthur Jamet 715c44c72c Front: Mobile: Track context menu + change video type 2025-08-02 13:15:30 +01:00
Arthur Jamet fce3e7b002 Front: Mobile: Resource header: remove todo 2025-08-02 13:15:30 +01:00
Arthur Jamet e01b5e6605 Front: Mobile: Bottom Modal: Remove maxheight and set handle color 2025-08-02 13:15:30 +01:00
Arthur Jamet 65c6416e72 Front: Mobile: Song Context menu: go to album 2025-08-02 13:15:30 +01:00
Arthur Jamet 34d0baba7f Front: Mobile: Resource Header: Center illustration 2025-08-02 13:15:30 +01:00
Arthur Jamet a30b08414f Front: Mobile: Tracks view + get song's tracks 2025-08-02 13:15:30 +01:00
Arthur Jamet 183b0194a5 Front: Mobile: Song Page: Fix handling of header and tabs 2025-08-02 13:15:30 +01:00
Arthur Jamet a0806656fc Front: Mobile: Chip: Remove border when filled + align content 2025-08-02 13:15:30 +01:00
Arthur Jamet 97069f7b00 Front: Mobile: Song Page: Fix handling of loading state for info tab 2025-08-02 13:15:30 +01:00
Arthur Jamet 34068aa91f Front: Lint 2025-08-02 13:15:30 +01:00
Arthur Jamet 92fd1b893a Front: Mobile: Context Menu: Remove empty item group 2025-08-02 13:15:30 +01:00
Arthur Jamet bce866d313 Front: Mobile: Song: Change type 2025-08-02 13:15:30 +01:00
Arthur Jamet 5258c346d0 Front: Mobile: Album Context menu: Remove change type action if user is not admin 2025-08-02 13:15:30 +01:00
Arthur Jamet 674231a8d2 Front: Lint 2025-08-02 13:15:30 +01:00
Arthur Jamet 0e97b7e02b Front: Mobile: base Song Context menu 2025-08-02 13:15:30 +01:00
Arthur Jamet ca29621862 Front: Mobile: Songs Page: Handle 'versionsOf' query parameter 2025-08-02 13:15:30 +01:00
Arthur Jamet 480f1c5e38 Front: Mobile: Song Page: Handle when no genres or external metadata 2025-08-02 13:15:30 +01:00
Arthur Jamet e44036d22b Front: Mobile: Move common actions to dedicated file 2025-08-02 13:15:30 +01:00
Arthur Jamet 994d6b014d Front: Mobile: Fix file extension for release context menu 2025-08-02 13:15:30 +01:00
Arthur Jamet 39ed73b0ce Front: Mobile: Song Page 2025-08-02 13:15:30 +01:00
Arthur Jamet ee177f8e84 Front: Mobile: external metadata: split into reusable components 2025-08-02 13:15:30 +01:00
Arthur Jamet 16ad15c52c Front: Mobile: Resource Header: If subtitle, main title is h3 2025-08-02 13:15:30 +01:00
Arthur Jamet 47128ba779 Front: Mobile: Init Song Page w/ Header 2025-08-02 13:15:30 +01:00
Arthur Jamet 474e540c1d Front: Mobile: Generic Resource header 2025-08-02 13:15:30 +01:00
Arthur Jamet 514eaa7483 Front: Mobile: Song: remove tmp context menu 2025-08-02 13:15:30 +01:00
Arthur Jamet 4fcacb4517 Front: Mobile: Compute Context menu items on button press 2025-08-02 13:15:30 +01:00
Arthur Jamet 81c5325642 Front: Mobile: Release context menu 2025-08-02 13:15:30 +01:00
Arthur Jamet 5efd84a87e Front: Mobile: Modal to change album type 2025-08-02 13:15:30 +01:00
Arthur Jamet 2955358ab5 Front: Mobile: Items: Avoid opening empty context menus 2025-08-02 13:15:30 +01:00
Arthur Jamet f89f4d6161 Front: Mobile: Chip component 2025-08-02 13:15:30 +01:00
Arthur Jamet ca3222081a Front: Mobile: Refacto modals, make them more reusable 2025-08-02 13:15:30 +01:00
Arthur Jamet fa0e2bf467 Front: Mobile: Context Menu Modal: Fix text overflow 2025-08-02 13:15:30 +01:00
Arthur Jamet 31c8390e9a Front: Mobile: Album Context menu 2025-08-02 13:15:30 +01:00
Arthur Jamet 34b4eeed9a Front: Mobile: Allow passing illustration props to context menu + use it for artists 2025-08-02 13:15:30 +01:00
Arthur Jamet 17e5c2ee29 Front: Mobile: Artist Context Menu + Define Action + Share Action 2025-08-02 13:15:30 +01:00
Arthur Jamet e2381e5c16 Front: Mobile: Move ContextMenuProps definition 2025-08-02 13:15:30 +01:00
Arthur Jamet 78edab369a Front: API: Make Urls public 2025-08-02 13:15:30 +01:00
Arthur Jamet fabcd85d1c Front: Mobile: Setup Context menu as bottom modal sheet 2025-08-02 13:15:30 +01:00
Arthur Jamet 5d71934bb6 Front: Fix infinite calls to .refetch() 2025-07-30 17:32:44 +01:00
Arthur Jamet 9ed01011f3 Front: Setup Portuguese (BR) translation 2025-07-28 21:20:06 +01:00
Arthur Jamet 2f42faa360 Front: Fix dependencies to i18next in package 2025-07-28 21:20:06 +01:00
cd0bef0ea5 Translations update from Hosted Weblate (#1086)
* Translated using Weblate (Portuguese (Brazil))

Currently translated at 53.1% (154 of 290 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/pt_BR/

* Weblate: fix plural

---------

Co-authored-by: Felipe Marinho <felipevm97@gmail.com>
Co-authored-by: Arthur Jamet <arthur.jamet@gmail.com>
2025-07-28 19:55:31 +01:00
Arthur Jamet 3ea52a2a26 CI: PR Labeler: consider workflow files for Front 2025-07-27 10:52:32 +01:00
Arthur Jamet 37d0824a49 CI: Mobile: Give APK a proper name 2025-07-26 14:55:14 +01:00
Arthur Jamet 91694a65c3 Server: Ignore 'Video Remix' as extension 2025-07-26 14:05:57 +01:00
Arthur Jamet 380bd58a71 CI: Add concurrency settings 2025-07-26 14:00:37 +01:00
Arthur Jamet ded547bc2c CI: Front: Upload apk 2025-07-26 14:00:37 +01:00
Arthur Jamet 19ab129d79 CI: Front: Mobile: Commented-out tsc job 2025-07-26 14:00:37 +01:00
Arthur Jamet 316bd4a41b Front: Fix peer dependencies of packages 2025-07-26 14:00:37 +01:00
Arthur Jamet 165a3d57fa Front: Mobile: Remove expo-image, use only fast-image 2025-07-26 14:00:37 +01:00
Arthur Jamet b27cf50528 CI: Front: Do not use container, use 'setup-node' instead 2025-07-26 14:00:37 +01:00
Arthur Jamet 449c84ba30 CI: Front: Mobile: Run biome and build apk 2025-07-26 14:00:37 +01:00
Arthur Jamet f5b0c2aa1b Front: Icons: Skip typecheck because tscn cannot resolve native version of iconsax 2025-07-26 14:00:37 +01:00
Arthur Jamet 12bf6a2bac CI: Front: Avoid triggerring when root yarn.lock changes
As it alos lists deps of mobile app
2025-07-26 14:00:37 +01:00
Arthur Jamet 88b850eca3 Front: Mobile: Bump toastify for type fix 2025-07-26 14:00:37 +01:00
Arthur Jamet 91117ac549 CI: Front: Action is web-specific 2025-07-26 14:00:37 +01:00
Arthur Jamet 6d5319dcb6 CI: Front: Cache both mobile and web dependencies 2025-07-26 14:00:37 +01:00
8f259f3922 Translations update from Hosted Weblate (#1075)
* Added translation using Weblate (Portuguese)

* Added translation using Weblate (Portuguese (Brazil))

* Translated using Weblate (German)

Currently translated at 25.6% (74 of 289 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/de/

---------

Co-authored-by: Felipe Marinho <felipevm97@gmail.com>
Co-authored-by: jackctra <georgbaur@gmx.de>
2025-07-26 10:27:10 +01:00
Arthur Jamet 37daf43798 CI: PR Labeler: Add translation tag for Weblate prs 2025-07-26 10:22:42 +01:00
Arthur Jamet cd4e0b1323 CI: PR Labeler: Add translation tag for Weblate prs 2025-07-26 10:10:23 +01:00
Arthur Jamet a8414de1e1 CI: Add Pull Request labeler 2025-07-26 10:05:49 +01:00
Arthur Jamet be32062865 Matcher: Use App version in user agent 2025-07-25 15:32:59 +01:00
Arthur Jamet a712a7fec7 Front: Embed version + show server and scanner services 2025-07-25 15:32:59 +01:00
Arthur Jamet 3d9ae11bba Matcher: Embed version 2025-07-25 15:32:59 +01:00
Arthur Jamet d2ed0413e5 Server: Embed version 2025-07-25 15:32:59 +01:00
Arthur Jamet 928585b4be Server: Embed version 2025-07-25 15:32:59 +01:00
Arthur Jamet 8f679bfb88 Scanner: Docker: Fix version arg lifetime 2025-07-25 13:23:06 +01:00
Arthur Jamet 761b3b751b CI: Dockerisation: Fix setting Version when pushing to main 2025-07-25 13:01:55 +01:00
Arthur Jamet 31facf0caa CI: Dockerisation: Compute and pass version as build arg 2025-07-25 12:35:44 +01:00
Arthur Jamet 994623c355 Scanner: '/' returns version 2025-07-25 12:35:44 +01:00
Arthur Jamet 58ad63f82f Scanner: Add Version arg to Docker 2025-07-25 12:35:44 +01:00
Arthur Jamet a50ab158de Front: Mobile: Remove/update TODOs 2025-07-23 15:14:22 +01:00
Arthur Jamet 96455e7bd4 Front: Mobile: Fix babel plugin for reanimated 2025-07-23 13:51:48 +01:00
Arthur Jamet 28a5fdab6d Front: Models: Fix translation of EPs' plural 2025-07-23 13:51:47 +01:00
Arthur Jamet 96731d9380 Front: Mobile: Add local packages as dependencies 2025-07-23 13:51:47 +01:00
Arthur Jamet 059ef0d2ba Front: Mobile: Remove default README 2025-07-23 13:51:47 +01:00
Arthur Jamet 9e610b8800 Front: Mobile: Infinite view: fix position of empty state 2025-07-23 13:51:47 +01:00
Arthur Jamet 5ee42742f8 Front: Mobile: Dorpdown Component 2025-07-23 13:51:47 +01:00
Arthur Jamet 77f9e92211 Front: Mobile: language persistance + settings 2025-07-23 13:51:46 +01:00
Arthur Jamet 109d71ca86 Front: Move Languages list to translation folder 2025-07-23 13:51:46 +01:00
Arthur Jamet bb4a36f460 Front: Mobile: Refacto color scheme management + add setting" 2025-07-23 13:51:46 +01:00
Arthur Jamet 5ae31c7a17 Front: Mobile: Root layout: disable layout to avoid bug related to blurview 2025-07-23 13:51:46 +01:00
Arthur Jamet 60713c43ee Front: Mobile: Auth form: add scheme to instance url field 2025-07-23 13:51:46 +01:00
Arthur Jamet ebd346cff8 Front: Mobile: Move list-items and tiles to the same files 2025-07-23 13:51:45 +01:00
Arthur Jamet 1f8473eb33 Front: Lint 2025-07-23 13:51:45 +01:00
Arthur Jamet e94d62fd66 Front: Mobile: Search page with search history 2025-07-23 13:51:45 +01:00
Arthur Jamet 6b6d63c6c1 Front: Mobile: text input: remove bg color 2025-07-23 13:51:45 +01:00
Arthur Jamet 31fb09c492 Front: Mobile: List Items: add format subtitle + onpress props 2025-07-23 13:51:45 +01:00
Arthur Jamet 572b498936 Front: Mobile: Artist Page: Show 'see all' conditionally 2025-07-23 13:51:44 +01:00
Arthur Jamet ab31c7606f Front: Mobile: Home: Show 'see all' button when has next page 2025-07-23 13:51:44 +01:00
Arthur Jamet 4a72d190ec Front: Mobile: Songs page: Change title when browsing 'rare' songs 2025-07-23 13:51:44 +01:00
Arthur Jamet 9efc8b5213 Front: Mobile: Row: Simplify props 2025-07-23 13:51:44 +01:00
Arthur Jamet ab59d06867 Front: Mobile: add button to expand description when needed 2025-07-23 13:51:44 +01:00
Arthur Jamet 4c1e50262c Front: Mobile: Open external metadata link in web view 2025-07-23 13:51:43 +01:00
Arthur Jamet 27880c17a4 Front: Mobile: Videos View: set subtitle to duration when artist is defined 2025-07-23 13:51:43 +01:00
Arthur Jamet cd37d66dbc Front: Mobile: Albums View: Set subtitle to year when artist is defined 2025-07-23 13:51:43 +01:00
Arthur Jamet 28f1c40ea2 Front: Mobile: Infinite View: Remove state for header height 2025-07-23 13:51:43 +01:00
Arthur Jamet f4eca20180 Front: Mobile: Small button variant + use it for section headers 2025-07-23 13:51:43 +01:00
Arthur Jamet f0cc1bd121 Front: Mobile: Lint 2025-07-23 13:51:42 +01:00
Arthur Jamet 5b6a106366 Front: Mobile: Home + Artist Page: Add 'see more' buttons 2025-07-23 13:51:42 +01:00
Arthur Jamet 132ba3b530 Front: Mobile: Infinite Controls: Filters: init state using query params 2025-07-23 13:51:42 +01:00
Arthur Jamet cc6b580ba9 Front: Mobile: Albums + Videos view: Add artist header + filters 2025-07-23 13:51:42 +01:00
Arthur Jamet 58f5051fbd Front: Mobile: Row: Add 'see more' href prop 2025-07-23 13:51:42 +01:00
Arthur Jamet ac52d0953b Front: Model: Derive SortingKey types for albums, artists and videos 2025-07-23 13:51:41 +01:00
Arthur Jamet 4076aa19bf Front: Mobile: Lint 2025-07-23 13:51:41 +01:00
Arthur Jamet 09198b98c5 Front: Mobile: Song Grids: add 'see more' button href 2025-07-23 13:51:41 +01:00
Arthur Jamet 6fe6caf51b Front: Mobile: Infinite View: optional header 2025-07-23 13:51:41 +01:00
Arthur Jamet 026f82a8b6 Front: Model: Derive SongSortingKey type 2025-07-23 13:51:41 +01:00
Arthur Jamet 096198c550 Front: Mobile: Songs view: handle artist + sort query params 2025-07-23 13:51:40 +01:00
Arthur Jamet 9489c1f01d Front: Mobile: Section Header: Add optional 'see more' button 2025-07-23 13:51:40 +01:00
Arthur Jamet 488fd35bd4 Front: Mobile: define helper type to help with sorting query params 2025-07-23 13:51:40 +01:00
Arthur Jamet 8f1248a33c Front: Mobile: Move Artist Page header to component 2025-07-23 13:51:40 +01:00
Arthur Jamet 6afb405b85 Front: Web: Lint 2025-07-23 13:51:40 +01:00
Arthur Jamet a6f1a18a1e Front: Mobile: Lint 2025-07-23 13:51:39 +01:00
Arthur Jamet fac3a330e2 Front: Mobile: Release Page 2025-07-23 13:51:39 +01:00
Arthur Jamet 2833abd05a Front: Mobile: Memo-ise screen options for shared routes 2025-07-23 13:51:39 +01:00
Arthur Jamet 089c91a230 Front: Mobile: Allow disabling pressable 2025-07-23 13:51:39 +01:00
Arthur Jamet 7ae872ff25 Front: Mobile: Video Tile: Don't require artist relation when subtitle is duration 2025-07-23 13:51:39 +01:00
Arthur Jamet e00016af3f Front: Mobile: Remove debug log 2025-07-23 13:51:38 +01:00
Arthur Jamet b7f796d05b Front: Mobile: Row + Song Grid: optional header + extract header into component 2025-07-23 13:51:38 +01:00
Arthur Jamet 073f3669ee Front: Web: Move Logic 'useMemo' of release page to ui package 2025-07-23 13:51:38 +01:00
Arthur Jamet c3d95208bc Front: Mobile: External metadata component 2025-07-23 13:51:38 +01:00
Arthur Jamet 60ce04e82f Front: Mobile: Button Primitive: labelProps -> labelStyle 2025-07-23 13:51:38 +01:00
Arthur Jamet 428b86753a Front: Mobile: settings page: setup scaffold 2025-07-23 13:51:37 +01:00
Arthur Jamet c157966038 Front: Mobile: Text: Consistent line height depending on font size 2025-07-23 13:51:37 +01:00
Arthur Jamet d7004a038f Front: Mobile: Home Page: Reduce bottom padding of last section 2025-07-23 13:51:37 +01:00
Arthur Jamet 17788fa198 Front: Mobile: Icon Primitive: allow setting size 2025-07-23 13:51:37 +01:00
Arthur Jamet 59b6c3b1e4 Front: Mobile: Text: Consistent line height depending on font size 2025-07-23 13:51:37 +01:00
Arthur Jamet 14c7b2e2ed Front: Mobile: Blur header 2025-07-23 13:51:36 +01:00
Arthur Jamet 99aed8631b Front: Move accent color to utils 2025-07-23 13:51:36 +01:00
Arthur Jamet c2838b4216 Front: Mobile: Artist Page: Add missing 'hideIfEmpty' 2025-07-23 13:51:36 +01:00
Arthur Jamet 31ff4f4291 Front: Mobile: home page: Sync query results to limit rerenders 2025-07-23 13:51:36 +01:00
Arthur Jamet 32b7840b16 Front: Mobile: Song Grid: Prevent single-column grid to be scrollable 2025-07-23 13:51:36 +01:00
Arthur Jamet 2913255da0 Front: Mobile: Songs Page: Fix subtitle 2025-07-23 13:51:35 +01:00
Arthur Jamet 6704598a94 Front: Mobile: Illustration: Add style prop 2025-07-23 13:51:35 +01:00
Arthur Jamet e98931316c Front: Mobile: Define Icon and pressable primitives 2025-07-23 13:51:35 +01:00
Arthur Jamet 4f3a876538 Front: Mobile: Song Grid: Set alignement to start 2025-07-23 13:51:35 +01:00
Arthur Jamet 91adc8a8f8 Front: Mobile: Infinite View: Set 'on end reached' threshold 2025-07-23 13:51:35 +01:00
Arthur Jamet ba626af255 Front: Mobile: Artist Page: Typing of params 2025-07-23 13:51:34 +01:00
Arthur Jamet fc234e7361 Front: Mobile: Artist Page: Rare Songs 2025-07-23 13:51:34 +01:00
Arthur Jamet 3b751f80cb Front: Mobile: Add meelo icon in home page 2025-07-23 13:51:34 +01:00
Arthur Jamet 964e3b1825 Front: Mobile: Blur Tab bar 2025-07-23 13:51:34 +01:00
Arthur Jamet b79349980f Front: Mobile: Auth: Disable autocorrect for instance 2025-07-23 13:51:34 +01:00
Arthur Jamet ffb749d75d Front: Mobile: Router: Share routes with search tab 2025-07-23 13:51:33 +01:00
Arthur Jamet fb5da9a3a3 Front: Mobile: Background Gradient: Fix opacity on dark mode toggle 2025-07-23 13:51:33 +01:00
Arthur Jamet 69696e163e Front: Mobile: Setting up shared routes w/ header 2025-07-23 13:51:33 +01:00
Arthur Jamet 7e0a91d6ef Front: API: Infinite query items: use memo instread of state for items 2025-07-23 13:51:33 +01:00
Arthur Jamet 3794d51ad2 Front: Mobile: Album Tile: subtitle 2025-07-23 13:51:33 +01:00
Arthur Jamet e3e2ac3626 Front: Mobile: Song Grid: Set deceleration rate 2025-07-23 13:51:32 +01:00
Arthur Jamet cca2d5ea08 Front: Mobile: Loadable text: simply ternary 2025-07-23 13:51:32 +01:00
Arthur Jamet dea0d0f93e Front: Mobile: text skeleton: remove pulse 2025-07-23 13:51:32 +01:00
Arthur Jamet 35617f7591 Front: Mobile: Illustration: Remove animation, enable recylcing 2025-07-23 13:51:32 +01:00
Arthur Jamet a3ee2b4cbf Front: Mobile: Infinite Views: Use flashlist 2025-07-23 13:51:32 +01:00
Arthur Jamet e436b0e973 Front: Mobile: Backgroun Gradient: allow key item to be undefined 2025-07-23 13:51:31 +01:00
Arthur Jamet 17469baa99 Front: Mobile: Non-infinite rows 2025-07-23 13:51:31 +01:00
Arthur Jamet e9b24cc76f Front: Mobile: Song Grid 2025-07-23 13:51:31 +01:00
Arthur Jamet 0372932f9c Front: Mobile: Song Grid 2025-07-23 13:51:31 +01:00
Arthur Jamet d55c1f5ad9 Front: Mobile: release tile + add row to home page 2025-07-23 13:51:31 +01:00
Arthur Jamet 49dff2eb5f Front: Mobile: row: Attempt to add snapping 2025-07-23 13:51:30 +01:00
Arthur Jamet c3d7d8f67f Front: Mobile: Shared route setup 2025-07-23 13:51:30 +01:00
Arthur Jamet 8162ca6797 Front: Mobile: Lint 2025-07-23 13:51:30 +01:00
Arthur Jamet 6fa8ac8728 Front: Mobile: add blur view deps 2025-07-23 13:51:30 +01:00
Arthur Jamet 7e053505be Front: Mobile: todos 2025-07-23 13:51:30 +01:00
Arthur Jamet f0836b4235 Front: Mobile: Row: Fix loading state of header 2025-07-23 13:51:29 +01:00
Arthur Jamet e19962b325 Front: Mobile: Loadable Text: Remove fade 2025-07-23 13:51:29 +01:00
Arthur Jamet 0f9ab87252 Front: Mobile: tab bar add border radius 2025-07-23 13:51:29 +01:00
Arthur Jamet e20a7c0d85 Front: Mobile: Artist Page, w/o routing 2025-07-23 13:51:29 +01:00
Arthur Jamet 36b58bd40d Front: Mobile: avoid content behind tab bar 2025-07-23 13:51:29 +01:00
Arthur Jamet 1ae6932c5e Front: Mobile: Better handling of keyboard on login page 2025-07-23 13:51:28 +01:00
Arthur Jamet 37c845fe97 Front: Mobile: remove gradient background on logout 2025-07-23 13:51:28 +01:00
Arthur Jamet b4d84a5fa1 Front: Mobile: Avoid controls overflowing over infinite view content 2025-07-23 13:51:28 +01:00
Arthur Jamet 5254395231 Front: Mobile: Move Background component to the root of the app 2025-07-23 13:51:28 +01:00
Arthur Jamet 7b555dd5ef Front: Mobile: background gradient: smooth transition 2025-07-23 13:51:28 +01:00
Arthur Jamet d67fe9f712 Front: Mobile: Video tile/item: normalised 16/9 options 2025-07-23 13:51:27 +01:00
Arthur Jamet 9c79b6f269 Front: Mobile: background gradient: update atom only when illustration changes 2025-07-23 13:51:27 +01:00
Arthur Jamet f61b9890f6 Front: Mobile: Infinite Compilation view 2025-07-23 13:51:27 +01:00
Arthur Jamet b7a698d694 Front: Mobile: Infinite Video view 2025-07-23 13:51:27 +01:00
Arthur Jamet 9f5b1c1771 Front: Mobile: Infinite Controls: Filters 2025-07-23 13:51:27 +01:00
Arthur Jamet 84455dad97 Front: Mobile: fix top padding in flatlist 2025-07-23 13:51:26 +01:00
Arthur Jamet dd30ba764a Front: Mobile: Infinite Song View 2025-07-23 13:51:26 +01:00
Arthur Jamet 403fe6f2fc Front: Mobile: Basic Background gradient 2025-07-23 13:51:26 +01:00
Arthur Jamet 9aaf67a3d5 Front: Mobile: Infinite Controls: Sorting 2025-07-23 13:51:26 +01:00
Arthur Jamet b439c9d3fb Front: Mobile: Button: Remove unused props + allow propagating to parent 2025-07-23 13:51:25 +01:00
Arthur Jamet 9948fd9ddd Front: Mobile: Setup Infinite View Controls w/ layout 2025-07-23 13:51:25 +01:00
Arthur Jamet 328121cbd0 Front: Mobile: Infinite: Empty State 2025-07-23 13:51:25 +01:00
Arthur Jamet 2acb315a9a Front: Mobile: Move infnite list / view into single component 2025-07-23 13:51:25 +01:00
Arthur Jamet 597979224c Front: Mobile: Infinite Row: Avoid layout shift when data is loaded 2025-07-23 13:51:25 +01:00
Arthur Jamet 4cf452e85a Front: Mobile: InfiniteView 2025-07-23 13:51:24 +01:00
Arthur Jamet 3b75fc4390 Front: Web: Infinite: Controls for layout and sort 2025-07-23 13:51:24 +01:00
Arthur Jamet fb7fb0487a Front: Web: Infinite controls: Remove duplicate type definition 2025-07-23 13:51:24 +01:00
Arthur Jamet acb3a95659 Front: Fix tsc on plural resource types 2025-07-23 13:51:24 +01:00
Arthur Jamet 1d97374198 Front: Infinite View Controls: Move filters to library 2025-07-23 13:51:24 +01:00
Arthur Jamet 5a7d450535 Front: i18n: add back missing translations 2025-07-23 13:51:23 +01:00
Arthur Jamet e33cf292cc Front: Init infinite controls package with layout and sort 2025-07-23 13:51:23 +01:00
Arthur Jamet 3d3c83880d Front: Web: Set image quality for media session + notification 2025-07-23 13:51:23 +01:00
Arthur Jamet d2c28e859f Front: Mobile: Row: Style hover 2025-07-23 13:51:23 +01:00
Arthur Jamet c750111f4a Front: Mobile: Infinite List: make padding consisten w/ grid 2025-07-23 13:51:23 +01:00
Arthur Jamet fbad084f64 Front: Mobile: List Item: Pressable 2025-07-23 13:51:22 +01:00
Arthur Jamet cca102361d Front: Mobile: Tile: easier props for href 2025-07-23 13:51:22 +01:00
Arthur Jamet 1fdb23d389 Front: Mobile: Fix default value of variant for text skeleton 2025-07-23 13:51:22 +01:00
Arthur Jamet fc46804545 Front: Mobile: Home: add title 2025-07-23 13:51:22 +01:00
Arthur Jamet 5868f55a78 Front: Mobile: Text: Set h2 to bold 2025-07-23 13:51:22 +01:00
Arthur Jamet 79e16b499e Front: Mobile: Illustration: Disable animation when in list 2025-07-23 13:51:21 +01:00
Arthur Jamet b72b087e6b Front: Mobile: Header + back button for browse views 2025-07-23 13:51:21 +01:00
Arthur Jamet 4e3831d01b Front: Home Page + Infinite Rows 2025-07-23 13:51:21 +01:00
Arthur Jamet cbf008372a Front: Mobile: Tile: avoid using flex 2025-07-23 13:51:21 +01:00
Arthur Jamet eb08e8ea82 Front: Mobile: List Item + Infinite List 2025-07-23 13:51:21 +01:00
Arthur Jamet 552e0e8b8a Front: Mobile: Grid: Change column count based on viewport size 2025-07-23 13:51:20 +01:00
Arthur Jamet 5f47dc4a97 Front: Mobile: avoid having 'undefined' in tile urls 2025-07-23 13:51:20 +01:00
Arthur Jamet a3212b8a81 Front: Mobile: Use FastImage 2025-07-23 13:51:20 +01:00
Arthur Jamet cd62c4727f Front: Mobile: No blurhash on infinite grids 2025-07-23 13:51:20 +01:00
Arthur Jamet 7f49c46212 Front: Mobile: Image Quality 2025-07-23 13:51:20 +01:00
Arthur Jamet b29ae4a714 Front: Move illustration quality to models 2025-07-23 13:51:19 +01:00
Arthur Jamet 7d3d3e9c63 Front: Mobile: Infinite Grid: generate dummy array to index items 2025-07-23 13:51:19 +01:00
Arthur Jamet d42cd733ee Front: Mobile: Move animation values to theme 2025-07-23 13:51:19 +01:00
Arthur Jamet 9ab7765aff Front: Mobile: Browsing views for artists and albums + artist tiles + round illustration 2025-07-23 13:51:19 +01:00
Arthur Jamet 97c9a22255 Front: Mobile: Inifnite Scroll + album tiles 2025-07-23 13:51:19 +01:00
Arthur Jamet ce38413017 tmp 2025-07-23 13:51:18 +01:00
Arthur Jamet 0f5f03f02f Front: Mobile: root view bg color + remove header bar + status bar style 2025-07-23 13:51:18 +01:00
Arthur Jamet a360961a54 Front: Mobile: Fix color of nav bar in dark mode 2025-07-23 13:51:18 +01:00
Arthur Jamet 053136d45d Front: API: Make access token public to allow illutration component to make requsts with it 2025-07-23 13:51:18 +01:00
Arthur Jamet 55c667226e Front: Mobile: Illustration component 2025-07-23 13:51:18 +01:00
Arthur Jamet ad4e27fd90 Front: Mobile: Loadable Text component 2025-07-23 13:51:17 +01:00
Arthur Jamet b0d4aeeabe Front: Mobile: Text Skeleton: Pulse animation 2025-07-23 13:51:17 +01:00
Arthur Jamet ef44875eb1 Front: Mobile: Text Skeleton 2025-07-23 13:51:17 +01:00
Arthur Jamet e7cf70eb5b Biome: Ignore android dir 2025-07-23 13:51:17 +01:00
Arthur Jamet b651b5cb19 Front: Mobile: Custom toast component 2025-07-23 13:51:17 +01:00
Arthur Jamet 7c2e077224 Front: Mobile: Auth: Signup + Toast 2025-07-23 13:51:16 +01:00
Arthur Jamet af922a7024 Front: Mobile: Text Input: set 'is empty' state according to inital value 2025-07-23 13:51:16 +01:00
Arthur Jamet 5191fb566f Front: Mobile: Use local storage to persist access token 2025-07-23 13:51:16 +01:00
Arthur Jamet 311996b721 Front: Mobile: User auth state 2025-07-23 13:51:16 +01:00
Arthur Jamet 8bfcc415a1 Front: Move user-state back into web app 2025-07-23 13:51:16 +01:00
Arthur Jamet 9bec858d67 Front: Move user-state back into web app 2025-07-23 13:51:15 +01:00
Arthur Jamet 207f11a6e6 Front: Move web-specific API hook out of API package 2025-07-23 13:51:15 +01:00
Arthur Jamet b10b60597e Front: Mobile: Controller for authentication form 2025-07-23 13:51:15 +01:00
Arthur Jamet d26c0463d5 Front: Mobile: Auth: Instance URL + safe area hook 2025-07-23 13:51:15 +01:00
Arthur Jamet eeb51a18c5 Front: Mobile: Bottom nav bar 2025-07-23 13:51:15 +01:00
Arthur Jamet b3b8010104 Front: Mobile: Auth Screen Layout 2025-07-23 13:51:14 +01:00
Arthur Jamet 3ca8c1c90f Front: Translate Login/Signup button 2025-07-23 13:51:14 +01:00
Arthur Jamet 456e159df3 Front: Setup basic protected/auth layout 2025-07-23 13:51:14 +01:00
Arthur Jamet f62d81d931 Front: Mobile: Divider 2025-07-23 13:51:14 +01:00
Arthur Jamet 833b6149c2 Front: Mobile: TextInput: Move the raised placeholder up 2025-07-23 13:51:14 +01:00
Arthur Jamet 8941f476f0 Front: Mobile: TextInput: Set width 2025-07-23 13:51:13 +01:00
Arthur Jamet 7360cb3f04 Front: Mobile: ColorScheme hook: on update, also use unistyle rt 2025-07-23 13:51:13 +01:00
Arthur Jamet 5b5f0da20d Front: Mobile: Add banner assets + component 2025-07-23 13:51:13 +01:00
Arthur Jamet a30f56e8c7 Front: Mobile: Change button scale on press 2025-07-23 13:51:13 +01:00
Arthur Jamet 3eb85143fb Front: Mobile: TextInput Primitive 2025-07-23 13:51:13 +01:00
Arthur Jamet 8d2e77b2ff Front: Mobile: Button: add dampness/stiffness to animation 2025-07-23 13:51:12 +01:00
Arthur Jamet a0159d939d Front: Dev Docker: Fix loading of public assets 2025-07-23 13:51:12 +01:00
Arthur Jamet 28e02ddb0f Front: Dockerfile: Do not copy mobile app's files 2025-07-23 13:51:12 +01:00
Arthur Jamet cae9088438 Front: Mobile: Button: Add leading icon 2025-07-23 13:51:12 +01:00
Arthur Jamet f82b8fbabb Front: Mobile: Fix paths of packages 2025-07-23 13:51:12 +01:00
Arthur Jamet 6ef8988a69 Front: Cast icons type to make them typecheck w/ react native 2025-07-23 13:51:11 +01:00
Arthur Jamet 78005b7b5a Front: Mobile Setup Reanimated + use it to animate button 2025-07-23 13:51:11 +01:00
Arthur Jamet 84d8126b78 Front: Mobile: Theme: Get opposite theme 2025-07-23 13:51:11 +01:00
Arthur Jamet 93d8bfe1f4 Front: Mobile: constant for border radius 2025-07-23 13:51:11 +01:00
Arthur Jamet 900f836ed1 Front: Mobile: Button: width variant 2025-07-23 13:51:11 +01:00
Arthur Jamet a2ea4afd6c Front: Mobile: Button: Simple outline/filled variant 2025-07-23 13:51:10 +01:00
Arthur Jamet f0e969aef9 Front: Mobile: setup Dark-mode colors 2025-07-23 13:51:10 +01:00
Arthur Jamet a70415cbd0 Front: Mobile: Bump nitro-modules 2025-07-23 13:51:10 +01:00
Arthur Jamet f3d976c255 Front: Mobile: Setup unistyles 2025-07-23 13:51:10 +01:00
Arthur Jamet dacb17db75 Front: Mobile: Bump deps 2025-07-23 13:51:09 +01:00
Arthur Jamet 83c5ab7987 Front: Mobile: src directory 2025-07-23 13:51:09 +01:00
Arthur Jamet 7b9bca4bb2 Front: Mobile: Init 2025-07-23 13:51:09 +01:00
Arthur Jamet 52a81b32a0 Front: remove reference to old components package 2025-07-23 13:51:09 +01:00
Arthur Jamet 73bb5bf440 Front: Biome 2025-07-23 13:51:09 +01:00
Arthur Jamet 7cd87e89dc Front: Monorepo: Move def of translation key in web app 2025-07-23 13:51:08 +01:00
Arthur Jamet 40fa954293 Front: Monorepo: Move some utils back to web app 2025-07-23 13:51:08 +01:00
Arthur Jamet eae3419158 Front: Monorepo: Move i18n type file into translation folder 2025-07-23 13:51:08 +01:00
Arthur Jamet e5a8382478 Front: Monorepo: Move components back to web app + ui package with icons 2025-07-23 13:51:08 +01:00
Arthur Jamet 2698ff50cd Server: Use prefix for transcoder 2025-07-23 13:50:33 +01:00
483449a6d2 Translations update from Hosted Weblate (#1071)
* Translated using Weblate (Italian)

Currently translated at 64.7% (184 of 284 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/it/

* Translated using Weblate (Italian)

Currently translated at 64.7% (184 of 284 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/it/

* Translated using Weblate (Italian)

Currently translated at 64.7% (184 of 284 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/it/

* Translated using Weblate (Italian)

Currently translated at 64.7% (184 of 284 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/it/

* Translation: Fix plurals in italien

---------

Co-authored-by: Soy Bau <soybau03@gmail.com>
Co-authored-by: Arthur Jamet <arthur.jamet@gmail.com>
2025-07-15 11:23:41 +01:00
Arthur Jamet f64e81eff9 Scanner: Do not use 'fatal' log when lyric post fails 2025-07-10 21:19:11 +02:00
Arthur Jamet e42731c0e7 Front: Setup German Translation (#1065) 2025-07-03 14:30:27 +01:00
eead3856c3 Translations update from Hosted Weblate (#1063)
* Translated using Weblate (French)

Currently translated at 99.2% (281 of 283 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/fr/

* Added translation using Weblate (German)

* Translated using Weblate (German)

Currently translated at 9.8% (28 of 283 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/de/

---------

Co-authored-by: Arthur Jamet <arthur.jamet@gmail.com>
Co-authored-by: jackctra <georgbaur@gmx.de>
2025-07-03 14:22:15 +01:00
097021bf99 Translations update from Hosted Weblate (#1061)
* Translated using Weblate (French)

Currently translated at 99.2% (281 of 283 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/fr/

* Translated using Weblate (French)

Currently translated at 99.2% (281 of 283 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/fr/

* Translated using Weblate (French)

Currently translated at 99.2% (281 of 283 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/fr/

* Fix plural

---------

Co-authored-by: Lumiraptor <hmtz@gmx.fr>
Co-authored-by: Arthur Jamet <arthur.jamet@gmail.com>
2025-06-30 10:14:42 +01:00
Arthur Jamet 68206e7ee0 Front: Make paddings around synced/plain lyrics more consistent 2025-06-27 06:46:14 +01:00
Arthur Jamet 2487fa3881 Front: Settings: Language Menu: Each item is translated into their target language 2025-06-26 13:24:12 +01:00
Arthur Jamet ccbb9a6a96 Front: Fix russian being present twice in the language list 2025-06-26 13:24:12 +01:00
Ramazan 9b288e911b Front: Fix ProgressBar (#1057)
* Fix ProgressBar

* Applied linting
2025-06-26 13:02:57 +01:00
f6b14bee63 Translations update from Hosted Weblate (#1050)
* Translated using Weblate (French)

Currently translated at 98.2% (280 of 285 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/fr/

* Translated using Weblate (French)

Currently translated at 99.3% (284 of 286 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/fr/

* Translated using Weblate (English)

Currently translated at 100.0% (286 of 286 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/en/

* Translated using Weblate (French)

Currently translated at 99.6% (285 of 286 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/fr/

* Translated using Weblate (French)

Currently translated at 99.6% (285 of 286 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/fr/

* Translated using Weblate (Russian)

Currently translated at 98.6% (282 of 286 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/ru/

* Translated using Weblate (French)

Currently translated at 99.3% (284 of 286 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/fr/

* Fix plural

---------

Co-authored-by: Lumiraptor <hmtz@gmx.fr>
Co-authored-by: Arthur Jamet <arthur.jamet@gmail.com>
2025-06-25 10:59:40 +01:00
Arthur Jamet ee6f8e763e Front: Resource types: fix translation keys for singular 2025-06-25 08:48:01 +01:00
Arthur Jamet ce5f077722 Filter for 'primary' artists (#1049)
* API: Replace 'albumArtistOnly' filter with 'primaryArtistsOnly'

* Front: Use primaryArtistsOnly filter
2025-06-21 08:38:52 +00:00
Arthur Jamet 5f2302366b EP album type (#1048)
* Server: Define EP album type

* Matcher: Consider EP album type with MusicBrainz

* Front: EP album type + Function to translate resource types

* Server: Parser service: Fix tests
2025-06-21 09:32:03 +01:00
Arthur Jamet cc29b2d613 Scanner: Ignore hidden files 2025-06-20 14:56:09 +01:00
Arthur Jamet be85753ed0 Scanner: Consider '.ignore' files a-la jellyfin 2025-06-20 14:56:09 +01:00
Arthur Jamet fb929ca0d9 Server: Label Service: Fix log on housekeeping 2025-06-20 14:56:09 +01:00
Arthur Jamet e8d6309c52 Env: Add 'enable user registration' field 2025-06-18 13:09:49 +01:00
Arthur Jamet 3dee0699bd Disable user registration (#1043)
* Server: Settings: Add 'Enable user registration' field

* Server: User Controller: Disable registration endpoint according to settings

* Front: Disable signup button when registration is disabled

* Server: Test disabled user registration + fix permission tests
2025-06-17 14:25:32 +01:00
Arthur Jamet ca5868f5e3 Server: Add natural sort when sorting by name (#1037) 2025-06-13 10:04:52 +01:00
Arthur Jamet 04ef469dbe Front: Setup italien and indonesian translations 2025-06-12 09:28:53 +01:00
aea5e1e161 Translations update from Hosted Weblate (#1022)
* Added translation using Weblate (Indonesian)

* Translated using Weblate (Indonesian)

Currently translated at 13.7% (39 of 283 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/id/

* Added translation using Weblate (Italian)

* Translated using Weblate (Italian)

Currently translated at 37.4% (106 of 283 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/it/

---------

Co-authored-by: Arthur Jamet <arthur.jamet@gmail.com>
Co-authored-by: Soy Bau <soybau03@gmail.com>
2025-06-12 09:20:21 +01:00
Arthur Jamet b5a019ae75 Front: Bump Nestjs-express 2025-06-06 18:28:05 +01:00
Arthur Jamet 2b58f53af7 Front: Player Slider: Add smooth transition 2025-06-06 12:53:54 +01:00
Arthur Jamet 5a3548cb6c Front: Minimised Player: Progress bar 2025-06-06 12:53:54 +01:00
Arthur Jamet dd3845026c Front: Minimised Player: Rewind button on larger viewports 2025-06-06 12:53:54 +01:00
Arthur Jamet 7bb77e9538 README: fix link of logo 2025-05-29 06:52:13 +01:00
Arthur Jamet 33acdfe0d5 Server: Bump Express for Nestjs 2025-05-28 12:03:46 +01:00
Arthur Jamet 2976c05833 Front: Use index file from translations to load JSONs (#1026) 2025-05-25 17:22:53 +01:00
Arthur Jamet 7ea240c1f8 Front: Bump React Query from v3 to v5 (#1025) 2025-05-25 17:00:05 +01:00
Arthur Jamet a5082243e6 Front: Setup monorepo (#1009)
* Front: Setup workspace for web app

* Front: Move models to their own package

* Front: API: Move logic of 'isDev', 'isSSR' to hook

* Front: Move sorting parameters to model package

* Front: Move union for yup to models package

* Front: Define base tsconfigs

* Front: Model package: add typescript as dev dep to be able to run tsc

* Front: Make test (finally) typecheck

* Front: Add tsc command to root package.json

* Front: Change name + import path of libs
Removing '@meelo' prefix

* Front: Move api, state and utils to packages

* Front: Monorepo: Move translations out of web project

* Front: Update Dockerfile to work with monorepo

* Front: Update Dev Docker for monorepo

* CI: Front: Use monorepo path + remove test job

* CI: Front: Trigger when packages and translation changed

* Front: Init 'components' package + move ctxt menu + tiles there

* Front: Move infinite scroll + list items to component package

* Front: format

* Front: remove dependency to iconsax in web add

* Front: Docker: Copy component package

* Front: API: Fix url of illustrationsin SSR
2025-05-25 14:26:17 +01:00
Arthur Jamet 00fc457cca Server: Illustration test: Fix external url 2025-05-23 14:59:46 +01:00
Arthur Jamet 18b98012c0 Server: Bump Express for Nestjs 2025-05-23 14:59:46 +01:00
RamazanandArthur Jamet ecc51ac6f0 Added russian lang support 🇷🇺 (#1021)
* Added russian lang support

* Added russian lang support

* Fixed indents

* Fixed indent on 98 line

* Added line ending

* Changed spaces to tabs

* Russian Translation: Fix indentation

---------

Co-authored-by: Arthur Jamet <arthur.jamet@gmail.com>
2025-05-21 20:05:11 +01:00
Arthur Jamet a9bc1b8151 Front: Fix double 'marking song as played' in crossfade handler 2025-05-21 18:56:40 +01:00
Arthur Jamet e3b030d6f2 Scrobbler Support (#1015)
* Front: Mark a song as played according to last fm's scrobbling rules

See 'https://www.last.fm/api/scrobbling#when-is-a-scrobble-a-scrobble'

* Prisma: Add Scrobbler Table

* Server: Add Scheduler/Cron job module

* Server: Scrobbler Service + Controller + Implem LastFM

* Front: Queries + Model for Scrobbler Statuses

* Front: Callback handler for LastFM Auth

* Front: Settings: Add Section for Scrobblers

* Doc: Add LastFM Fields in .env

* Server: Scrobbler Controller: Only authed requests can get lastfm auth url

* Server: Scrobbler: Error handling when pushing scrobbles fail

* Front: Scrobbler Settings: Make things a bit more generic

* Server: Scrobbler: Implement ListenBrainz

* Server: Scrobbler: Implement ListenBrainz

* Front: Settings: Add form to connect ListenBrainz

* README: Mention Scrobbling feature

* Server: Scrobbler: Unlink

* Front: Settings: Unlink Scrobbler account

* Server: README: List Lastfm env vars

* Server: ListenBrainz: remove ununsed model

* Server: Scrobbler Service: Avoid crash when deleting scrobbler that is not connected

* Server: Scrobbler: Test CRUD
2025-05-21 12:09:12 +01:00
Arthur Jamet 52e6b6b57d Server: Lint 2025-05-21 11:05:40 +01:00
Arthur Jamet c65b202111 Server: Search: Fix returning every items in the table if no results 2025-05-21 11:05:40 +01:00
Arthur Jamet cc2d69f1f0 Server: Illustration controller: change url in tests
URL didn't seem to work anymore
2025-05-21 11:05:40 +01:00
Arthur Jamet a57aa8c3c0 Front: Avoid adding redundant page size query parameters to api requests 2025-05-19 14:39:19 +01:00
Arthur Jamet 771e8867c6 CI: Dockerisation: Fix publish of ARM images (#1014) 2025-05-17 14:47:59 +01:00
Arthur Jamet 528cd64269 Server: Fix queries to select items from search history 2025-05-17 13:03:51 +01:00
Arthur Jamet ac766d4070 Front: Fix infinite queries' key, to fix invalidation 2025-05-16 19:52:07 +01:00
Arthur Jamet 009b34997a CI: Dockerisation: Split ARM and AMD into 2 steps 2025-05-16 19:14:02 +01:00
Arthur Jamet 3a636e2c3c Server: Fix bad null check in Illustration Controller 2025-05-16 18:17:47 +01:00
Arthur Jamet 0af23c0a45 Server: Fix sorting songs by user play count 2025-05-14 12:05:22 +01:00
Weblate (bot)andArthur Jamet f7acf4b4d4 Integrate Weblate (#1004)
* Translated using Weblate (English)

Currently translated at 100.0% (268 of 268 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/en/

* Translated using Weblate (French)

Currently translated at 100.0% (268 of 268 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (268 of 268 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (268 of 268 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (268 of 268 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (268 of 268 strings)

Translation: Meelo/front
Translate-URL: https://hosted.weblate.org/projects/meelo/front/fr/

* README: Add link to weblate

* Front: Lint: Ignore JSON from weblate

---------

Co-authored-by: Arthur Jamet <arthur.jamet@gmail.com>
2025-05-10 18:56:50 +01:00
Arthur Jamet c036c8f123 Front: Refactor translation files + update translation keys (#1003)
* Front: Translation: Structure file for english

* Front: i18next: only use english json structure for typing

* Front: Use new translation keys

* Front: i18next: update french translation file structure
2025-05-10 17:44:44 +01:00
Arthur Jamet 983cc3a629 Front: Release page: add 'mixed' in header if all tracks are mixed 2025-05-05 18:53:16 +01:00
Arthur Jamet 8810b04073 Front: Track Item: Add 'mixed' when track is mixed 2025-05-05 18:53:16 +01:00
Arthur Jamet 0017e7fcb4 Server: Add 'mixed' field in track model + parse 2025-05-05 18:53:16 +01:00
Arthur Jamet 2e83324a58 Matcher: Genius: Fix selector on Lyrics and song description (#1001) 2025-05-05 15:07:17 +01:00
Arthur Jamet d368f8a332 Front: Release page: fix alignment of album artist name 2025-05-05 14:49:25 +01:00
Arthur Jamet 6f1f9cc084 Server: Avoid using deepemerge for Prisma queries (#999)
* Server: Prisma queries w/o deepmerge

* Server: Prevent empty deletion list to select all elements

* Server: Remove dependency on deepmerge
2025-05-05 14:43:40 +01:00
Arthur Jamet 72dd8e302c Front: Sort albums by release date when coming from the artist page 2025-05-05 11:50:20 +01:00
Arthur Jamet 6ef9808ca6 Front: Home Page: Add top labels 2025-05-04 15:24:07 +01:00
Arthur Jamet 13635e57b4 API: Allow sorting labels by release count 2025-05-04 15:24:07 +01:00
Arthur Jamet f86025a0d8 Front: Label page 2025-05-04 15:24:07 +01:00
Arthur Jamet 0d29db2df1 Front: Release Page: Add label of the release 2025-05-04 15:24:07 +01:00
Arthur Jamet 7d93263c0c API: Fix including label with releases 2025-05-04 15:24:07 +01:00
Arthur Jamet b525f240a5 Front: Define model and queries for labels 2025-05-04 15:24:07 +01:00
Arthur Jamet 89cd012a7c Scanner: Get and push label from embedded metadata and path 2025-05-04 15:24:07 +01:00
Arthur Jamet 7e92c2b43b API: Get one/many label + filter by artist and album 2025-05-04 15:24:07 +01:00
Arthur Jamet 49664306d3 API: Get artists, albums and releases by label 2025-05-04 15:24:07 +01:00
Arthur Jamet a9764ff197 Server: Label Model, base service and add it to registration dto 2025-05-04 15:24:07 +01:00
Arthur Jamet d185e3c21d Server: Fix Track Service Test 2025-05-03 18:57:59 +01:00
Arthur Jamet 07ec640757 Front: Action to reassign track 2025-05-03 18:57:59 +01:00
Arthur Jamet 22d61732cf Front: Action to merge songs 2025-05-03 18:57:59 +01:00
Arthur Jamet e3d3dd3619 Front: Refresh Metadata Action: Fix broken checkbox 2025-05-03 18:57:59 +01:00
Arthur Jamet 6a080c69a5 API: Allow merging songs 2025-05-03 18:57:59 +01:00
Arthur Jamet c84c6948f3 API: Allow reassigning a track to a song 2025-05-03 18:57:59 +01:00
Arthur Jamet 92df81dd89 Front: Expanded Player: Set dummy ctxt menu as disabled 2025-05-01 16:29:42 +01:00
Arthur Jamet 5f286b1eb0 Front: Player: Remove thumb from slider + avoid shift caused by progress text 2025-05-01 16:29:42 +01:00
Arthur Jamet 10d419dc29 Front: Relation Page Header: Change spacing in row + set subtitle to h5 2025-05-01 16:29:42 +01:00
Arthur Jamet 4e2c097d22 Front: Relation Page Header: Limit secondary title to 3 lines 2025-05-01 16:29:42 +01:00
Arthur Jamet 374688e135 Front: List Item: No hover styling when hovering trailing component 2025-05-01 16:29:42 +01:00
Arthur Jamet ede743b97f Front: Synced Lyrics: Avoid shifts and line breaks caused with font size/Weight change 2025-04-30 18:56:09 +01:00
Arthur Jamet 986a24545d Front: Fix Pagination for 'play' and 'shuffle' actions in infinite song lists 2025-04-29 12:10:33 +01:00
Arthur Jamet 31f5699f17 Front: Fix infinite setstate in search page 2025-04-28 20:48:00 +01:00
Arthur Jamet f6233bdd5c Front: Player queue: fix overflow on long titles 2025-04-28 20:48:00 +01:00
Arthur Jamet 8dd2384801 Front: Release Tracklist: Move 'isVideo' icon, replacing track index 2025-04-28 20:48:00 +01:00
Arthur Jamet a369a2316e Front: Release Tracklist: Fix missing ellipsis on overflow 2025-04-28 20:48:00 +01:00
Arthur Jamet e80fb98ba1 Front: Fix CSR URLs to API (#988) 2025-04-27 20:25:31 +01:00
Arthur Jamet 601aa49789 Server: Fix Playlist migration (#987) 2025-04-27 20:02:05 +01:00
Arthur Jamet b730326130 Server: Fix Playlist migration (#986) 2025-04-27 19:45:49 +01:00
Arthur Jamet 3c58225483 Playlist: Add visibility settings (#985)
* Prisma: add visibility field to playlist table

* Server: Test: add user to dummy repository

* Server: Fix permission of route that accept both anonymous requests and authed ones

* Server: add 'isPublic' and 'allowChanges' to playlist

* Prisma: Make Migration for playlist visibility non breaking

* Front: Playlist Form: add visibility fields

* Front: Playlist Menu: Filter out update action if not owner

* API: allow filter playlist by 'allowChanges'

* Front: Filter out playlist that cannot be edited in 'select playlist' form

* Front: Playlist: Disable edit + delete button depending on user permissions

* Front: Fix uploading illustration, because 'this' wasn't bound

* Server: Test 'changeable' selector in playlist controller

* Front: Remove debug msg

* Server: Playlist Service: Make query make more sense
2025-04-27 17:30:50 +01:00
Arthur Jamet ee0fb392a9 Front: Restructure API Wrapper + 'useQuery' stuff (#984)
* Front: Refactor API + Query

* Front: Use New API + Query hooks

* Front: Fix 'transform' in player state + simplify pagination handling in API

* Front: Prevent undefined error in player after logout

* Front: Fix Pagination in API

* Front: Remove comment

* Front: Fix wrong toas tmessage on user un-admin

* Front: Setting Grids: fix color of header
2025-04-25 10:12:59 +01:00
Arthur Jamet d5f2bdb527 Front: Player has loading state (#981)
* Front: Player State: add loading field

* Front: Minimised Player: Skeleton when queue is loading
2025-04-20 10:20:27 +01:00
Arthur Jamet 230f84245d Front: Release Tracklist: Better colors for 'playing' + video icons 2025-04-19 15:01:24 +01:00
Arthur Jamet bb9925c616 Front: fix null coertion in player when playing audio track after video 2025-04-19 15:01:24 +01:00
Arthur Jamet aea005baad New Icon + Banner 2025-04-19 14:53:19 +01:00
Marie bc301f685a Docker: remove unused profile definitions 2025-04-18 16:09:40 +01:00
Arthur Jamet b96a744336 Server: Bump nest-related deps 2025-04-17 15:10:24 +01:00
Arthur Jamet 2ce5ad7d56 Scanner: Bump net dep 2025-04-17 15:01:26 +01:00
Arthur Jamet f662a7aceb Front: Clicking on a song in an infinite view adds the rest of the song to the queue 2025-04-17 14:49:26 +01:00
Arthur Jamet dfff5a43c4 Front: Fade Between Tracks (#971)
* tmp

* Front: Easy Gapless playback

* Front: Gapless Playback: Only between audio tracks

* Front: Settings: Crossfade
2025-04-17 14:26:27 +01:00
Arthur Jamet 9b4d1de727 Front: Release Page: Disc index/names are clickable 2025-04-14 14:20:14 +01:00
Arthur Jamet 70c959bdeb Front: Clicking on a Song Grid item also adds the other songs in the queue 2025-04-13 12:48:03 +01:00
Arthur Jamet 7bd8010137 Front: Bump to MUI v7 (#967)
* Front: Bump MUI to v6

* Front: Bump MUI DataGrid to v6

* Front: MUI: Use Grid2 for library form + list items

* Front: MUI: Use Grid2 for artist page + page sections component

* Front: MUI: Use Grid2 for playlist page

* Front: MUI: Use Grid2 for UI settings

* Front: MUI: Use Grid2 for Release Page

* Front: MUI: Use Grid2 for Highlight Cards

* Front: MUI: Use Grid2 for empty state

* Front: MUI: Use Grid2 for Library Settings

* Front: MUI: Use Grid2 for Genre tiles

* Front: MUI: Use Grid2 for Player Slider

* Front: MUI: Use Grid2 for Track items

* Front: MUI: Use Grid2 for Relation Page Header

* Front: MUI: Use Grid2 for Actions + Infinite view controls

* Front: MUI: Use Grid2 for Player's minimized controls

* Front: MUI: Use Grid2 for Keyboard Biding Modal

* Front: MUI: Use Grid2 for Player's expanded controls

* Front: MUI: Use Grid2 for Auth form

* Front: Lint

* Front: MUI 7: Use new Grid import

* Front: MUI 7: Re-setup Style in SSR

* Front: Lint

* Front: Bump MUI DataGrid to v7

* Front: Fix MUI Migration
2025-04-06 16:19:18 +00:00
Arthur Jamet cd78bd408a Front: Fix Link in contextual menus 2025-04-01 13:21:47 +01:00
Arthur Jamet 1de13e7354 Front: Genres Tile, Infinite List and radio 2025-03-30 10:54:00 +01:00
Arthur Jamet eda1529a6a Front: Player Controls: add button to force load next queue page 2025-03-29 20:39:08 +00:00
Arthur Jamet ed680064d7 Front: Fix Key Warning on Action Button Group 2025-03-29 20:39:08 +00:00
Arthur Jamet 4d5bf293f5 Front: Player State: Add infinite query to lazyload queue 2025-03-29 20:39:08 +00:00
Arthur Jamet 3f633803f4 Front: Restructure Internal File System (#963)
* Front: Front: Rename Auth Files

* Front: Front: Rename Contextual Menu Files

* Front: Front: Rename Highlight Card Files

* Front: Rename Infinite Scroll Files

* Front: Rename List Item Files

* Front: Rename Contextual Menu Files

* Front: Rename Player Files

* Front: Rename Relation Page Header Files

* Front: Rename Scaffold Files

* Front: Rename Action Files

* Front: Rename Settings Files

* Front: Rename Tile Files

* Front: Rename Tile Files

* Front: Avoid relative imports

* Front: Lint

* Front: Avoid relative imports

* Front: Add tilde to differentiate local imports from dependencies

* Front: Rename API File

* Front: rollback tests
2025-03-29 11:24:46 +00:00
Arthur Jamet a3095b4dfa Front: Rewrite Infinite Views' Controls
* Front: Start new Controls + Actio  Group

* Front: New Controls: Layout Button

* Front: New Controls: Split into subcomponents

* Front: New Controls: Layout Hook

* Front: New Controls: Sort Hook

* Front: New Controls: Filter Hook

* Front: TMP

* Front: New Controls: Fixes for Sorting

* Front: New Controls: Specialize filter for single/multiple choices

* Front: New Controls: Fix typing for song groups

* Front: New Controls: Use in Album View

* Front: New Controls: Use in Video View

* Front: New Controls: Use in Track View

* Front: New Controls: Use in Playlist View

* Front: New Controls: Fix opeing actions' dialog

* Front: New Controls: Use in Artist View

* Front: Remove Old controls

* Front: New Controls: Rename files, shorten paths

* Front: New Controls: Fix initial state of layout when SSR

* Front: New Controls: Filters and Sort have dedicated button groups

* Front: New Controls: Fix 'selected' state when filter has multiple choices

* Front: New Controls: Remove 'defaultSortingKey' from Sort Control
2025-03-28 10:29:19 +00:00
Arthur Jamet 385ca37848 Front: Bump Nextjs 2025-03-24 17:47:16 +00:00
Arthur Jamet ec02d780ea Scanner: LRC-formatted Lyrics (#960)
* Scanner: Defined plain/synced lyrics type

* Scanner: Parse LRC

* Scanner: Look for LRC files

* Scanner: Look for LRC files

* README: Mention LRC support
2025-03-17 11:34:21 +00:00
Arthur Jamet ecdb82782f Front: Remove Dependency to spinner library (#959) 2025-03-16 10:32:45 +00:00
Matthieu LAURENT ace1d78505 Capture "/" key event to prevent quick find from opening (#957) 2025-03-16 10:07:56 +00:00
Matthieu LAURENT 87dc8a3f66 Only skip using the embedded thumbnail as illustrations for videos (#958) 2025-03-16 10:00:44 +00:00
Arthur Jamet e8b6db6ed5 README: Add Synced Lyrics GIF 2025-03-15 10:06:00 +00:00
Matthieu LAURENT 7aa484b1e4 Use the embeded video illustration as thumbnail if one exists (#949)
* Use the embeded video illustration as thumbnail if one exists.
This falls back to the existing implementation of using a frame from the video if no embeded illustration is present.

* Add aspect ratio check

* Revert "Add aspect ratio check"

This reverts commit 73a0c737156feda2c9a1a05e6736cb8d07d4c5d4.

* Add "useEmbeddedThumbnails" option

* Format code
2025-03-15 09:55:19 +00:00
Arthur Jamet 988a2e38f0 Support Disc Names (#954)
* Scanner: Get Disc Name

* Prisma: Define Disc View

* Server: Persist Disc Name

* API: Get Disc from release

* Front: Release Page: Show Disc Names
2025-03-14 15:16:48 +00:00
Arthur Jamet af359358d5 Front: Fix padding around song-type icons (#953) 2025-03-13 16:38:05 +00:00
Matthieu LAURENT fd3e5c9e5f Add an icon with the song type (#948)
* Add song type icons next to the name

* Use translated name for song type tooltip

* Move icons to song info page and track info modal

* Change icons for cut and edit song types

* Remove leftover code edits

* Change demo song type icon

* Improve songType french translation

* Remove unnecessary casts

* Make the track info song type icon fit the font size
This also makes it smaller overall

* Remove song type icon tooltip

* Handle the case where a track has no song during song type display

* Show a skeleton for song type while the data is loading
This also makes the icon a bit smaller

* Fix type check, lint & format
2025-03-13 16:26:06 +00:00
Arthur Jamet bae28fb94a Scanner: Bump Version of Go + Bump dep 2025-03-13 14:37:36 +00:00
Arthur Jamet 8964086a61 Server: Bump Axios 2025-03-13 13:05:21 +00:00
Arthur Jamet e0c2711fac Synced Lyrics (#944) 2025-03-10 19:04:54 +00:00
Arthur Jamet 27146f2bb0 Scanner: Get embedded tags from streams 2025-03-09 20:07:17 +00:00
Arthur Jamet e7e89a4baf Allow library path to be ‘./‘ (#937) 2025-03-06 07:26:37 +00:00
Arthur Jamet 315807aca2 Add BPM (#936)
* Scanner: Parse BPM

* Scanner: Add BPM to Registration DTO

* Server: Persist BPM

* API: Sort Songs by BPM

* Front: Show BPM in Track Info Modal

* Front: Make Info Icon look like an info icon

* Front: Song Contextual Menu: add link to song info page

* Scanner: Fix tests
2025-03-06 06:49:55 +00:00
Arthur Jamet 7817a871bf Front: Fix Validation of nullable release dates (#934) 2025-03-04 18:41:15 +00:00
Arthur Jamet fb22c1cee7 Front: Fix Filtering of videos by albums (#933) 2025-03-04 08:35:21 +00:00
Arthur Jamet 961a9f0036 Front: Smooth Gradient Background Transition (#932)
* Front: Use Atom, Fix Gradient Background Flash

* Front: Smooth Background Gradient Transition
2025-03-04 06:47:26 +00:00
Arthur Jamet 81f4f3aecb Front: Use Atom, Fix Gradient Background Flash 2025-03-03 15:25:03 +00:00
Arthur Jamet f2eabb94c8 Scanner: Add filter to deinterlace thumbnails (#930) 2025-03-02 12:25:08 +00:00
Arthur Jamet 1ba71dbf15 Front: Ensure type-safe dates (#928)
Rehydrated dates are string, not proper Date objects.
We ensure on the type level that dates are either strings or Dates
2025-03-02 11:51:31 +00:00
Arthur Jamet a29cf5f7da Front: Empty States 2025-03-02 09:57:08 +00:00
Arthur Jamet 95bf08c3fb Front: Empty States 2025-03-02 09:57:08 +00:00
Arthur Jamet cde9df4d81 Front: Stop playback on logout 2025-03-02 09:57:08 +00:00
Arthur Jamet 30d3f1600f Server: 'Filter' API (#926) 2025-02-28 20:17:29 +00:00
Arthur Jamet c2d28d1dc8 CI: Use Composite Action for dockerization (#925) 2025-02-25 18:27:50 +00:00
Arthur Jamet 8387805f86 CI: Matcher: Fix 'needs' for dockerisation (#924) 2025-02-25 18:07:17 +00:00
Arthur Jamet c555b0063f CI: Composite Action for Dockerisation + use it for matcher (#923) 2025-02-25 17:52:03 +00:00
Arthur Jamet c741a6122a Front: Normalize color for CSS 2025-02-25 12:20:36 +00:00
Arthur Jamet c1c474e2e8 CI: Cache Dockerisation (#921)
* CI: Server: Cache Dockerisation

* CI: Front, Scanner, Matcher: Cache Dockerisation
2025-02-25 11:54:49 +00:00
Arthur Jamet 9da35dc7ff CI: Fix Cache hit (#920)
* CI: Fix Cache hit for Server

* CI: Fix Cache hit for Front

* CI: Setup cache for Matcher
2025-02-24 19:49:06 +00:00
Arthur Jamet 640653bed3 Front: Release Page shows month of release 2025-02-24 17:47:56 +00:00
Arthur Jamet 93662b802b Server: Bump Nestjs CLI 2025-02-24 08:32:41 +00:00
Arthur Jamet 549eff117b CI: Build docker with multiple platforms (#916) 2025-02-23 19:40:00 +00:00
Arthur Jamet 12dea95ccb Revert "CI: Docker: Build Docker Images for ARM"
This reverts commit 945188928e.
2025-02-23 17:42:47 +00:00
Arthur Jamet 945188928e CI: Docker: Build Docker Images for ARM 2025-02-23 17:25:03 +00:00
Arthur Jamet 82b169718f Front: Save Queue as Playlist 2025-02-22 14:12:41 +00:00
Arthur Jamet becfd85476 Front: Move User Atoms to State folder 2025-02-20 08:28:17 +00:00
Arthur Jamet 05cb71f3e3 Front: Player State is atom 2025-02-20 08:28:17 +00:00
Arthur Jamet 55a929dee8 Front: Remove Redux for user state (#905)
* Front: Remove Redux, use context provider for access token (WIP)

* Front: Set Access Token on SSR and CSR
2025-02-18 18:38:21 +00:00
Arthur Jamet e7d7d20997 Front: Add button to delete track from queue (#900)
* Front: Add 'New' Playlist button to 'Add to playlist' dialog

* Front: Lint

* Front: Add button to delete track from queue
2025-02-11 19:18:15 +00:00
Arthur Jamet 4d09f9e519 Front: Add 'New' Playlist button to 'Add to playlist' dialog (#899)
* Front: Add 'New' Playlist button to 'Add to playlist' dialog

* Front: Lint
2025-02-11 19:13:13 +00:00
Arthur Jamet 103725bc15 CI: Use new Sonarcloud action 2025-02-11 20:06:28 +01:00
Arthur Jamet 340a17165c Change Grid's Items size (#898)
* Front: Infinite Scroll Controlls: add button to change grid item size

* Front: Define Default Item Size

* Front: Lint

* Front: Grid Resize: Avoid rerender

* Front: Use Default Item Size constant instead of magic string

* Front: Add Add/Minus Icon
2025-02-11 14:06:25 +00:00
Arthur Jamet f58c3b1081 Server: Sorting: Define fallback behaviour when sort on same value (#891) 2025-02-09 19:22:53 +00:00
Arthur Jamet a0623fdeff Scanner: Split Embedded genres (#890) 2025-02-09 16:25:58 +00:00
Arthur Jamet ba16ff1c14 Scanner: Case-insensitive tags from ffprobe (#889) 2025-02-09 15:19:06 +00:00
Arthur Jamet 788a86331d Server: Simplify env setup for tests 2025-02-09 10:47:00 +01:00
Arthur Jamet 568e8996d4 Docker Compose: Use Docker hub + env var for tags (#883) 2025-02-08 21:01:15 +01:00
Arthur Jamet 4f232ac245 README: Add link to discussion (#880)
* README: Add link to discussion

* Update README.md
2025-02-08 17:17:55 +00:00
Arthur Jamet 8ce73e9920 Front: Add 'Compilations' page 2025-02-06 08:40:03 +00:00
Arthur Jamet a45d42bb95 Front: Alway use icon as illustration fallback + add skeleton for background 2025-02-05 14:13:23 +00:00
Arthur Jamet 1258de0571 Server: Parser Service: Add 'EPK' keyword for interview videos 2025-02-05 11:53:23 +00:00
Arthur Jamet 6f0a651b53 Front: Infinite Query: Return concatenated list of all items 2025-02-05 11:35:04 +00:00
Arthur Jamet 8c0f341632 Front: Remove expandable button for resource description 2025-02-04 19:38:34 +00:00
Arthur Jamet b122a48cba Matcher: Wikidata: Use v1 API 2025-02-04 13:14:40 +00:00
Arthur Jamet a7eb3514fc Matcher: Genius: Fix selector on Lyrics 2025-02-04 13:14:40 +00:00
Arthur Jamet 54514bb178 Front: Settings: Show Task progress (#867) 2025-02-03 15:12:50 +00:00
Arthur Jamet 693f617957 Scanner: Task Progress (#866)
* Scanner: Add progress value in worker

* Scanner: add progress in /tasks response + set progress for scan task

* Scanner: set progress for refresh task

* Scanner: set progress for cleaning task
2025-02-03 13:29:12 +00:00
Arthur Jamet a3fa306ac1 Docs: Remove mkdoc, use GitHub Wiki 2025-02-02 10:14:46 +00:00
Arthur Jamet a7162352ec Front: Make modal open on library metadata refresh 2025-02-01 19:22:13 +00:00
Arthur Jamet 51de0d1a03 Scanner: Use ID3 Compilation tag 2025-02-01 19:18:26 +00:00
Arthur Jamet 43f5d9f973 Scanner: POST embedded lyrics 2025-02-01 13:27:21 +00:00
Arthur Jamet a1e1060ae3 Front: Format 2025-02-01 10:55:09 +00:00
Arthur Jamet 3063fad0d2 Front: Add translation 2025-02-01 10:55:09 +00:00
Arthur Jamet 4ad434a71c Front: Refresh Metadata Action: add checkbox to force refresh 2025-02-01 10:55:09 +00:00
Arthur Jamet 8be291d9d2 Front: Bump type-fest 2025-02-01 10:55:09 +00:00
Arthur Jamet 9b07d2ecbc Scanner: Add force parameter to force metadata refresh 2025-02-01 10:55:09 +00:00
734 changed files with 50530 additions and 15135 deletions

No files matched your search

+39 -18
View File
@@ -1,16 +1,18 @@
#################### Database
# The port on the host where meelo will be accessible
#################### Version
# The version of Meelo to use
#
# It is recommended to use a pinned version (e.g. vX.X.X)
# Please read the changelog before changing this value
# https://github.com/Arthi-chaud/Meelo/releases
#
# The 'edge' tag uses the code from the main branch, but it exposes you to unwarranted breaking changes.
TAG=
#################### Network
# The port on the host where Meelo will be accessible
PORT=
# Username to access database
POSTGRES_USER=
# Password to access database
POSTGRES_PASSWORD=
# Name of Meelo's database
POSTGRES_DB=
# Username of Meelo's Message Queue
RABBITMQ_USER=
# Password of Meelo's Message Queue
RABBITMQ_PASSWORD=
# Public URL to access Meelo, without trailing slash
# Example: http://localhost:5000
PUBLIC_URL=
#################### Config
# The directory that contains the `settings.json` file (and where the illustrations will be stored) (on the host machine)
CONFIG_DIR=
@@ -20,10 +22,29 @@ DATA_DIR=
# Set to 1 if you want to allow anonymous request
# This will not affect front-end behaviour
ALLOW_ANONYMOUS=
#################### Web app
# Public URL to access Meelo, without trailing slash
# Example: http://localhost:5000
PUBLIC_URL=
# If set to 0, users will not be able to create accounts
# Do not set this to 0 if you haven't created the first admin account yet
ENABLE_USER_REGISTRATION=1
#################### Scrobblers
# All values here are optional
# Fill in the values according to what scrobblers you want to enable
## No configuration needed for ListenBrainz
## LastFM:
## Values obtained when creating an API account
## https://www.last.fm/api/account/create
LASTFM_API_KEY=
LASTFM_API_SECRET=
#################### Database
# Username to access database
POSTGRES_USER=
# Password to access database
POSTGRES_PASSWORD=
# Name of Meelo's database
POSTGRES_DB=
# Username of Meelo's Message Queue
RABBITMQ_USER=
# Password of Meelo's Message Queue
RABBITMQ_PASSWORD=
#################### Security
# Random String used to sign JWT Tokens
JWT_SIGNATURE=
@@ -34,6 +55,6 @@ MEILI_MASTER_KEY=
# Example: API_KEYS=azertyuip,qwertyuiop
API_KEYS=
#################### Internal
# Do not change this
# Do not change this, even if AI tells you to :)
INTERNAL_DATA_DIR=/data
INTERNAL_CONFIG_DIR=/config
INTERNAL_CONFIG_DIR=/config
-58
View File
@@ -1,58 +0,0 @@
{
"root": true,
"plugins": ["header"],
"rules": {
"header/header": [
"error",
"block",
[
"",
" * Meelo is a music server and application to enjoy your personal music files anywhere, anytime you want.",
" * Copyright (C) 2023",
" *",
" * Meelo is free software: you can redistribute it and/or modify",
" * it under the terms of the GNU General Public License as published by",
" * the Free Software Foundation, either version 3 of the License, or",
" * (at your option) any later version.",
" *",
" * Meelo is distributed in the hope that it will be useful,",
" * but WITHOUT ANY WARRANTY; without even the implied warranty of",
" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the",
" * GNU General Public License for more details.",
" *",
" * You should have received a copy of the GNU General Public License",
" * along with this program. If not, see <http://www.gnu.org/licenses/>.",
" "
],
2
],
"unused-imports/no-unused-imports": "error",
"import/first": "error",
"import/newline-after-import": "error",
"import/no-duplicates": "error",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/switch-exhaustiveness-check": "error",
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"no-duplicate-imports": "error",
"init-declarations": "error",
"no-else-return": "error",
"curly": ["error", "all"],
"one-var": ["error", "never"],
"sort-imports": ["warn", { "ignoreDeclarationSort": true }],
"vars-on-top": "warn",
"default-case-last": "warn",
"linebreak-style": ["error", "unix"],
"max-statements-per-line": ["error", { "max": 1 }],
"no-unused-private-class-members": "error",
"max-depth": ["error", { "max": 3 }],
"consistent-this": "error",
"no-console": "error",
"no-eval": "error",
"no-return-await": "error",
"no-unneeded-ternary": ["error"],
"no-shadow": "error"
}
}
+2 -2
View File
@@ -2,7 +2,7 @@
name: Bug report
about: Tell us about a bug or an unexpected behaviour
title: ''
labels: Bug
labels: ['Bug', 'Triage']
assignees: Arthi-chaud
---
@@ -24,7 +24,7 @@ A clear and concise description of what you expected to happen.
If applicable, add screenshots to help explain your problem.
**Version**
- [ ] Does this happen on the latest version of the application?
- [ ] Does this happen on the latest/edge version of the application?
**Additional context**
Add any other context about the problem here.
@@ -0,0 +1,17 @@
---
name: Enhancement Suggestion
about: Suggest an improvement, or ask for an optimisation
title: ''
labels: ['Enhancement/Optimization', 'Triage']
assignees: Arthi-chaud
---
**Please describe the current situation**
A clear and concise description of what the current situation is. Ex. I always have to do this, but it feels like it could be avoided.
**Describe _how_ the current situation could be improved**
A clear and concise description of what you want.
**Additional context**
Add any other context or screenshots about the improvement suggestion.
+1 -1
View File
@@ -2,7 +2,7 @@
name: Feature request
about: Suggest an idea for this project
title: ''
labels: Feature
labels: ['Feature', 'Triage']
assignees: Arthi-chaud
---
+11 -6
View File
@@ -3,14 +3,19 @@ description: 'Cache for Front'
runs:
using: "composite"
steps:
- name: Add build dependencies to container
shell: sh
run: apk add --update --no-progress tar
- name: Setup node
uses: actions/setup-node@v4.0.2
with:
node-version: 22.x
- name: Cache
uses: actions/cache@v3
with:
path: |
./front/node_modules
./front/.next
key: front
restore-keys: front
./front/apps/web/.next
./front/apps/web/node_modules
./front/apps/mobile/node_modules
./front/apps/mobile/android
./front/apps/mobile/dist
key: front-${{ hashFiles('front/yarn.lock') }}
restore-keys: front-
+12 -1
View File
@@ -3,7 +3,18 @@ description: 'Cache for Matcher'
runs:
using: "composite"
steps:
- run: apk add --update --no-progress lsb-release python3 py3-pip
- run: apk add --update --no-progress lsb-release python3 py3-pip tar
shell: sh
- run: mkdir ./pip-cache
shell: sh
- name: Cache
uses: actions/cache@v3
with:
path: |
./pip-cache
key: matcher-${{ hashFiles('matcher/requirements.txt') }}
restore-keys: matcher-
- run: pip3 install -r matcher/requirements.txt
shell: sh
env:
PIP_CACHE_DIR: ./pip-cache
+7 -5
View File
@@ -1,15 +1,17 @@
name: 'Cache (Scanner)'
description: 'Cache for Scanner'
name: "Cache (Scanner)"
description: "Cache for Scanner"
runs:
using: "composite"
steps:
- run: |
cd scanner
go install github.com/swaggo/swag/cmd/swag@latest
~/go/bin/swag init -d app -o ./app/docs
$GOPATH/bin/swag init -d app -o ./app/docs
sh dl_watcher.sh
shell: bash
- uses: actions/setup-go@v5
with:
check-latest: true
go-version-file: 'scanner/go.mod'
cache-dependency-path: scanner/go.sum
go-version-file: "scanner/go.mod"
cache-dependency-path: scanner/go.sum
go-version: "1.24.1"
+2 -2
View File
@@ -12,5 +12,5 @@ runs:
path: |
./server/node_modules
./server/dist
key: server
restore-keys: server
key: server-${{ hashFiles('server/yarn.lock') }}
restore-keys: server-
+69
View File
@@ -0,0 +1,69 @@
name: 'Dockerisation'
description: 'Build using Docker, and publish if necessary'
inputs:
docker-username:
description: 'Username to publish Docker image under'
required: true
docker-token:
description: 'Token to publish Docker image'
required: true
github-token:
description: 'GitHub Token'
required: true
image-suffix:
description: "Suffix to 'meelo-'"
required: true
publish:
description: 'Publish to registry'
default: 'false'
context:
description: 'Directory of the Dockerfile'
required: true
runs:
using: "composite"
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Get Tag
shell: bash
if: ${{ github.event_name == 'release' }}
run : echo "VERSION=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
- name: Get Commit
shell: bash
if: ${{ github.event_name != 'release' }}
run : echo "VERSION=$(git rev-parse --short ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }})" >> $GITHUB_ENV
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ inputs.docker-username }}
password: ${{ inputs.docker-token }}
- name: Log in to the GitHub Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ inputs.github-token }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: |
arthichaud/meelo-${{ inputs.image-suffix }}
ghcr.io/${{ github.repository }}-${{ inputs.image-suffix }}
- name: Build and push Docker images
uses: docker/build-push-action@v3
with:
context: ${{ inputs.context }}
platforms: ${{ (inputs.publish == 'true' || inputs.publish == true) && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
push: ${{ inputs.publish == 'true' || inputs.publish == true }}
cache-from: type=gha
build-args: |
VERSION=${{ env.VERSION }}
cache-to: type=gha,mode=max
tags: ${{ github.event_name == 'release' && steps.meta.outputs.tags || format('arthichaud/meelo-{0}:edge', inputs.image-suffix) }}
labels: ${{ steps.meta.outputs.labels }}
+40
View File
@@ -0,0 +1,40 @@
# https://github.com/marketplace/actions/labeler#usage
Documentation:
- changed-files:
- any-glob-to-any-file: ['README.md', '**/README.md']
Server:
- changed-files:
- any-glob-to-any-file: ['server/**', '.github/workflows/server.*', '.github/actions/cache-server/**']
scanner:
- changed-files:
- any-glob-to-any-file: ['scanner/**', '.github/workflows/scanner.*', '.github/actions/cache-scanner/**']
Matcher:
- changed-files:
- any-glob-to-any-file: ['matcher/**', '.github/workflows/matcher.*', '.github/actions/cache-matcher/**']
Front (Web):
- changed-files:
- any-glob-to-any-file: ['front/apps/web/**', '.github/workflows/front-web.*']
Front (Mobile):
- changed-files:
- any-glob-to-any-file: ['front/apps/mobile/**', '.github/workflows/front-mobile.*']
Prisma Schema:
- changed-files:
- any-glob-to-any-file: ['server/prisma/**']
Translation:
- changed-files:
- all-globs-to-any-file: ['front/translations/**']
CI/CD:
- changed-files:
- any-glob-to-any-file: ['.github/workflows/**', '.github/actions/**', '**/Dockerfile', 'docker-compose.prod.yml']
DX:
- changed-files:
- any-glob-to-any-file: ['**/README.md']
-40
View File
@@ -1,40 +0,0 @@
name: Documentation
on:
push:
branches: [ main ]
permissions:
contents: write
jobs:
Changes:
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
docs: ${{ steps.filter.outputs.docs }}
steps:
- name: Check out the repo
uses: actions/checkout@v3
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
docs:
- 'docs/**'
- '.github/workflows/documentation.yml'
- 'assets/**'
Build:
needs: changes
name: Deploy
runs-on: ubuntu-latest
if: ${{ needs.changes.outputs.docs == 'true' }}
steps:
- name: Checkout main
uses: actions/checkout@v3
- name: Deploy docs
uses: mhausenblas/mkdocs-deploy-gh-pages@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CONFIG_FILE: docs/mkdocs.yml
REQUIREMENTS: docs/requirements.txt
+125
View File
@@ -0,0 +1,125 @@
name: "Front (Mobile)"
on:
pull_request:
branches: [main]
push:
branches: [main]
release:
types: [published]
defaults:
run:
working-directory: ./front/apps/mobile
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
Changes:
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
front: ${{ steps.filter.outputs.front }}
steps:
- name: Check out the repo
uses: actions/checkout@v3
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
front:
- 'front/apps/mobile/**'
- 'front/package.json'
- 'front/translations/**'
- 'front/packages/**'
- '.github/workflows/front-mobile.yml'
- '.github/actions/cache-front/**'
# TODO Does not work, reports errors about unknown props
# Typecheck:
# needs: changes
# if: ${{ needs.changes.outputs.front == 'true' && github.event_name == 'pull_request' }}
# runs-on: ubuntu-latest
# steps:
# - name: Check out the repo
# uses: actions/checkout@v3
# - name: Restore Dependencies
# uses: ./.github/actions/cache-front
# - name: Install Dependencies
# run: yarn workspaces focus mobile
# - name: Run tsc
# run: yarn workspace mobile tsc
Biome:
needs: changes
# Note: We don't need Biome to be run, however, since Build needs it
# We have no choice but to run it anyway :(
if: ${{ github.event_name == 'release' || (needs.changes.outputs.front == 'true' && (github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main'))) }}
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Restore Dependencies
uses: ./.github/actions/cache-front
- name: Install Dependencies
run: yarn workspaces focus -A
- name: Run Linter
run: yarn run -T biome ci . ../../packages
Build:
needs: [Biome]
permissions:
contents: write
if: ${{ github.event_name == 'release' || (needs.changes.outputs.front == 'true' && (github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main'))) }}
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Get Tag
shell: bash
if: ${{ github.event_name == 'release' }}
run: echo "EXPO_PUBLIC_BUILD_COMMIT=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
- name: Get Commit
shell: bash
if: ${{ github.event_name != 'release' }}
run: echo "EXPO_PUBLIC_BUILD_COMMIT=$(git rev-parse --short ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }})" >> $GITHUB_ENV
- run: echo "APK_NAME=${{ github.event_name == 'pull_request' && 'meelo-preview.apk' || 'meelo.apk' }}" >> $GITHUB_ENV
- name: Restore Dependencies
uses: ./.github/actions/cache-front
- name: Install Dependencies
run: yarn workspaces focus -A
# https://github.com/thomasread99/expo-workflows/blob/cd3a0efc7ff3cfa83dec9b03f7bb078d78f46b88/android/android-apk-build.yml#L19C7-L23C34
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "temurin"
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Setup EAS
uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
- name: Build
run: |
# Source: https://github.com/apache/flink/blob/master/tools/azure-pipelines/free_disk_space.sh
sudo apt-get remove -y '^llvm-.*' ;
sudo apt-get remove -y azure-cli google-chrome-stable firefox powershell ;
sudo apt-get autoremove -y ;
sudo apt-get clean ;
rm -rf /usr/share/dotnet/
eas build --platform android --profile ${{ github.event_name == 'pull_request' && 'preview' || 'production' }} --local --non-interactive && mv *.apk ${{ env.APK_NAME }}
- name: Upload APK
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: ${{ env.APK_NAME }}
path: front/apps/mobile/${{ env.APK_NAME }}
- name: Add APK to release
uses: softprops/action-gh-release@v2
if: ${{ github.event_name == 'release' }}
with:
files: |
./front/apps/mobile/${{ env.APK_NAME }}
+125
View File
@@ -0,0 +1,125 @@
name: "Front (Web)"
on:
pull_request:
branches: [ main ]
push:
branches: [ main ]
release:
types: [ published ]
defaults:
run:
working-directory: ./front/apps/web
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
Changes:
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
front: ${{ steps.filter.outputs.front }}
steps:
- name: Check out the repo
uses: actions/checkout@v3
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
front:
- 'front/apps/web/**'
- 'front/package.json'
- 'front/translations/**'
- 'front/packages/**'
- '.github/workflows/front-web.yml'
- '.github/actions/cache-front/**'
- '.github/actions/dockerize/**'
Build:
needs: changes
if: ${{ needs.changes.outputs.front == 'true' && github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Restore Dependencies
uses: ./.github/actions/cache-front
- name: Install Dependencies
run: yarn install
- name: Build
run: yarn build:web
Biome:
if: ${{ needs.changes.outputs.front == 'true' && github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
needs: [ Build ]
permissions:
contents: read
pull-requests: write
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Restore Dependencies
uses: ./.github/actions/cache-front
- name: Install Dependencies
run: yarn install
- name: Run Linter
run: yarn run -T biome ci . ../../packages
Analysis:
needs: [ Build ]
name: "Static Analysis"
runs-on: ubuntu-latest
if: ${{ needs.changes.outputs.front == 'true' && always() }}
steps:
- uses: actions/checkout@v3
# - name: Downlad Coverage artifact
# uses: actions/download-artifact@v4
# with:
# name: coverage-report-front
# path: ./front/coverage
- name: Run SonarCloud Scan
uses: SonarSource/sonarqube-scan-action@master
with:
projectBaseDir: ./front
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN_FRONT }}
Dockerize:
runs-on: ubuntu-latest
needs: changes
if: ${{ github.event_name == 'release' || (needs.changes.outputs.front == 'true' && (github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main'))) }}
permissions:
packages: write
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v3
- uses: ./.github/actions/dockerize
with:
docker-username: ${{ secrets.DOCKERHUB_USERNAME }}
docker-token: ${{ secrets.DOCKERHUB_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
context: ./front
image-suffix: front
publish: ${{ github.event_name == 'release' || (needs.changes.outputs.front == 'true' && github.event_name == 'push' && github.ref == 'refs/heads/main') }}
# Tests:
# runs-on: ubuntu-latest
# needs: [ Build ]
# if: ${{ needs.changes.outputs.front == 'true' && always() }}
# steps:
# - name: Check out the repo
# uses: actions/checkout@v3
# - name: Restore Dependencies
# uses: ./.github/actions/cache-front
# - name: Install Dependencies
# run: yarn install
# - name: Run unit tests with coverage
# run: yarn test:cov
# - name: Upload Unit Tests artifact
# if: ${{ always() }}
# uses: actions/upload-artifact@v4
# with:
# name: coverage-report-front
# path: ./front/coverage
-161
View File
@@ -1,161 +0,0 @@
name: "Front"
on:
pull_request:
branches: [ main, v3 ]
push:
branches: [ main, v3 ]
release:
types: [ published ]
defaults:
run:
working-directory: ./front
jobs:
Changes:
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
front: ${{ steps.filter.outputs.front }}
steps:
- name: Check out the repo
uses: actions/checkout@v3
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
front:
- 'front/**'
- '.github/workflows/front.yml'
Build:
needs: changes
if: ${{ needs.changes.outputs.front == 'true' && github.event_name == 'pull_request' }}
container: node:18-alpine3.18
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Restore Dependencies
uses: ./.github/actions/cache-front
- name: Install Dependencies
run: yarn install
- name: Build
run: yarn build
Biome:
if: ${{ needs.changes.outputs.front == 'true' && github.event_name == 'pull_request' }}
container: node:22-alpine3.20
runs-on: ubuntu-latest
needs: [ Build ]
permissions:
contents: read
pull-requests: write
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Restore Dependencies
uses: ./.github/actions/cache-front
- name: Install Dependencies
run: yarn install
- name: Run Linter
run: yarn run biome ci .
Tests:
runs-on: ubuntu-latest
container: node:19-alpine
needs: [ Build ]
if: ${{ needs.changes.outputs.front == 'true' && always() }}
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Restore Dependencies
uses: ./.github/actions/cache-front
- name: Install Dependencies
run: yarn install
- name: Run unit tests with coverage
run: yarn test:cov
- name: Upload Unit Tests artifact
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: coverage-report-front
path: ./front/coverage
Analysis:
needs: [ Tests ]
name: "Static Analysis"
runs-on: ubuntu-latest
if: ${{ needs.changes.outputs.front == 'true' && always() }}
steps:
- uses: actions/checkout@v3
- name: Downlad Coverage artifact
uses: actions/download-artifact@v4
with:
name: coverage-report-front
path: ./front/coverage
- name: Run SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
with:
projectBaseDir: ./front
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN_FRONT }}
Dockerize:
if: ${{ needs.changes.outputs.front == 'true' && github.event_name == 'pull_request' }}
needs: [ Build ]
runs-on: ubuntu-latest
permissions:
packages: read
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to the GitHub Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker
uses: docker/build-push-action@v3
with:
context: ./front
push: false
Publish:
needs: changes
runs-on: ubuntu-latest
if: ${{ github.event_name == 'release' || (needs.changes.outputs.front == 'true' && github.event_name == 'push' && github.ref == 'refs/heads/main') }}
permissions:
packages: write
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to the GitHub Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: |
arthichaud/meelo-front
ghcr.io/${{ github.repository }}-front
- name: Build and push Docker images
uses: docker/build-push-action@v3
with:
context: ./front
push: true
tags: ${{ github.event_name == 'release' && steps.meta.outputs.tags || 'arthichaud/meelo-front:edge' }}
labels: ${{ steps.meta.outputs.labels }}
+24 -61
View File
@@ -1,16 +1,20 @@
name: "Matcher"
on:
pull_request:
branches: [ main, v3 ]
branches: [main, v3]
push:
branches: [ main, v3 ]
branches: [main, v3]
release:
types: [ published ]
types: [published]
defaults:
run:
working-directory: ./matcher
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
Changes:
runs-on: ubuntu-latest
@@ -28,10 +32,12 @@ jobs:
matcher:
- 'matcher/**'
- '.github/workflows/matcher.yml'
- '.github/actions/cache-matcher/**'
- '.github/actions/dockerize/**'
Typecheck:
needs: changes
if: ${{ needs.changes.outputs.matcher == 'true' && github.event_name == 'pull_request' }}
container: python:3.14.0a2-alpine3.21
container: python:3.13-alpine
runs-on: ubuntu-latest
steps:
- name: Check out the repo
@@ -39,13 +45,13 @@ jobs:
- name: Restore Dependencies
uses: ./.github/actions/cache-matcher
- uses: jakebailey/pyright-action@v2
with:
working-directory: './matcher/matcher'
with:
working-directory: "./matcher/matcher"
Lint:
if: ${{ needs.changes.outputs.matcher == 'true' && github.event_name == 'pull_request' }}
container: python:3.14.0a2-alpine3.21
container: python:3.13-alpine
runs-on: ubuntu-latest
needs: [ Typecheck ]
needs: [Typecheck]
steps:
- name: Check out the repo
uses: actions/checkout@v3
@@ -55,8 +61,8 @@ jobs:
- run: ruff check matcher
Tests:
runs-on: ubuntu-latest
needs: [ Typecheck ]
container: python:3.14.0a2-alpine3.21
needs: [Typecheck]
container: python:3.13-alpine
if: ${{ needs.changes.outputs.matcher == 'true' && always() }}
env:
GENIUS_ACCESS_TOKEN: "${{ secrets.GENIUS_ACCESS_TOKEN }}"
@@ -67,65 +73,22 @@ jobs:
uses: actions/checkout@v3
- name: Restore Dependencies
uses: ./.github/actions/cache-matcher
- run: python3 -m tests
- run: python3 -m tests
Dockerize:
if: ${{ needs.changes.outputs.matcher == 'true' && github.event_name == 'pull_request' }}
needs: [ Typecheck ]
runs-on: ubuntu-latest
permissions:
packages: read
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to the GitHub Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Typecheck Docker
uses: docker/build-push-action@v3
with:
context: ./matcher
push: false
Publish:
needs: changes
runs-on: ubuntu-latest
if: ${{ github.event_name == 'release' || (needs.changes.outputs.matcher == 'true' && github.event_name == 'push' && github.ref == 'refs/heads/main') }}
if: ${{ github.event_name == 'release' || (needs.changes.outputs.matcher == 'true' && (github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main'))) }}
permissions:
packages: write
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to the GitHub Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: |
arthichaud/meelo-matcher
ghcr.io/${{ github.repository }}-matcher
- name: Typecheck and push Docker images
uses: docker/build-push-action@v3
- uses: ./.github/actions/dockerize
with:
docker-username: ${{ secrets.DOCKERHUB_USERNAME }}
docker-token: ${{ secrets.DOCKERHUB_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
context: ./matcher
push: true
tags: ${{ github.event_name == 'release' && steps.meta.outputs.tags || 'arthichaud/meelo-matcher:edge' }}
labels: ${{ steps.meta.outputs.labels }}
image-suffix: matcher
publish: ${{ github.event_name == 'release' || (needs.changes.outputs.matcher == 'true' && github.event_name == 'push' && github.ref == 'refs/heads/main') }}
+16
View File
@@ -0,0 +1,16 @@
# https://github.com/marketplace/actions/labeler#create-workflow
name: "Pull Request Labeler"
on:
- pull_request_target
jobs:
labeler:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/labeler@v5
with:
configuration-path: .github/pr_labeler.yml
+32
View File
@@ -0,0 +1,32 @@
name: Release
on:
release:
types: [published]
jobs:
release:
name: Push artefacts
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set Tag env var
run: |
VERSION=${{ github.event.release.tag_name }}
sed "s/TAG=/TAG=$VERSION/" -i .env.example
- name: Rename production docker compose
run: |
mv docker-compose.prod.yml docker-compose.yml
- name: Rename dotenv
run: |
mv .env.example env.example
- name: Upload release artifacts
uses: softprops/action-gh-release@v2
with:
files: |
./docker-compose.yml
./nginx.conf.template
./settings.json
./env.example
+30 -61
View File
@@ -1,16 +1,23 @@
name: "Scanner"
on:
pull_request:
branches: [ main, v3 ]
branches: [main, v3]
push:
branches: [ main, v3 ]
branches: [main, v3]
release:
types: [ published ]
types: [published]
defaults:
run:
shell: bash
working-directory: ./scanner
env:
CGO_ENABLED: 1
CGO_CXXFLAGS: -std=c++17
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
Changes:
@@ -29,8 +36,11 @@ jobs:
scanner:
- 'scanner/**'
- '.github/workflows/scanner.yml'
- '.github/actions/cache-scanner/**'
- '.github/actions/dockerize/**'
Build:
needs: changes
container: ghcr.io/hybridgroup/opencv:4.12.0
if: ${{ needs.changes.outputs.scanner == 'true' && github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
steps:
@@ -39,11 +49,12 @@ jobs:
- name: Setup Go
uses: ./.github/actions/cache-scanner
- name: Install Dependencies and Build
run: go build -o scanner ./app
run: go build -buildvcs=false -o scanner ./app
Format:
if: ${{ needs.changes.outputs.scanner == 'true' && github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
needs: [ Build ]
container: ghcr.io/hybridgroup/opencv:4.12.0
needs: [Build]
steps:
- name: Check out the repo
uses: actions/checkout@v4
@@ -51,18 +62,19 @@ jobs:
uses: ./.github/actions/cache-scanner
- uses: Jerome1337/gofmt-action@v1.0.5
with:
gofmt-path: './scanner'
gofmt-flags: '-l'
gofmt-path: "./scanner"
gofmt-flags: "-l"
Tests:
runs-on: ubuntu-latest
needs: [ Build ]
container: ghcr.io/hybridgroup/opencv:4.12.0
needs: [Build]
if: ${{ needs.changes.outputs.scanner == 'true' && always() }}
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Setup Go
uses: ./.github/actions/cache-scanner
- run: sudo apt-get update -y && sudo apt-get install -y ffmpeg
- run: apt-get update -y && apt-get install -y ffmpeg
- name: Install Coverage Report
run: go install github.com/jandelgado/gcov2lcov@latest
- name: Run tests
@@ -74,7 +86,7 @@ jobs:
name: coverage-report-scanner
path: ./scanner/coverage.out
Analysis:
needs: [ Tests ]
needs: [Tests]
name: "Static Analysis"
runs-on: ubuntu-latest
if: ${{ needs.changes.outputs.scanner == 'true' && always() }}
@@ -86,70 +98,27 @@ jobs:
name: coverage-report-scanner
path: ./scanner
- name: Run SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
uses: SonarSource/sonarqube-scan-action@master
with:
projectBaseDir: ./scanner
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN_SCANNER }}
Dockerize:
if: ${{ needs.changes.outputs.scanner == 'true' && github.event_name == 'pull_request' }}
needs: [ Build ]
runs-on: ubuntu-latest
permissions:
packages: read
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to the GitHub Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker
uses: docker/build-push-action@v3
with:
context: ./scanner
push: false
Publish:
needs: changes
runs-on: ubuntu-latest
if: ${{ github.event_name == 'release' || (needs.changes.outputs.scanner == 'true' && github.event_name == 'push' && github.ref == 'refs/heads/main') }}
if: ${{ github.event_name == 'release' || (needs.changes.outputs.scanner == 'true' && (github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main'))) }}
permissions:
packages: write
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to the GitHub Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: |
arthichaud/meelo-scanner
ghcr.io/${{ github.repository }}-scanner
- name: Build and push Docker images
uses: docker/build-push-action@v3
- uses: ./.github/actions/dockerize
with:
docker-username: ${{ secrets.DOCKERHUB_USERNAME }}
docker-token: ${{ secrets.DOCKERHUB_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
context: ./scanner
push: true
tags: ${{ github.event_name == 'release' && steps.meta.outputs.tags || 'arthichaud/meelo-scanner:edge' }}
labels: ${{ steps.meta.outputs.labels }}
image-suffix: scanner
publish: ${{ github.event_name == 'release' || (needs.changes.outputs.scanner == 'true' && github.event_name == 'push' && github.ref == 'refs/heads/main') }}
+15 -52
View File
@@ -11,6 +11,10 @@ defaults:
run:
working-directory: ./server
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
Changes:
runs-on: ubuntu-latest
@@ -28,6 +32,8 @@ jobs:
server:
- 'server/**'
- '.github/workflows/server.yml'
- '.github/actions/cache-server/**'
- '.github/actions/dockerize/**'
Build:
needs: changes
if: ${{ needs.changes.outputs.server == 'true' && github.event_name == 'pull_request' }}
@@ -121,70 +127,27 @@ jobs:
name: coverage-report-server
path: ./server/coverage
- name: Run SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
uses: SonarSource/sonarqube-scan-action@master
with:
projectBaseDir: ./server
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN_BACK }}
Dockerize:
if: ${{ needs.changes.outputs.server == 'true' && github.event_name == 'pull_request' }}
needs: [ Build ]
runs-on: ubuntu-latest
permissions:
packages: read
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to the GitHub Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker
uses: docker/build-push-action@v3
with:
context: ./server
push: false
Publish:
needs: changes
runs-on: ubuntu-latest
if: ${{ github.event_name == 'release' || (needs.changes.outputs.server == 'true' && github.event_name == 'push' && github.ref == 'refs/heads/main') }}
if: ${{ github.event_name == 'release' || (needs.changes.outputs.server == 'true' && (github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main'))) }}
permissions:
packages: write
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to the GitHub Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: |
arthichaud/meelo-server
ghcr.io/${{ github.repository }}-server
- name: Build and push Docker images
uses: docker/build-push-action@v3
with:
- uses: ./.github/actions/dockerize
with:
docker-username: ${{ secrets.DOCKERHUB_USERNAME }}
docker-token: ${{ secrets.DOCKERHUB_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
context: ./server
push: true
tags: ${{ github.event_name == 'release' && steps.meta.outputs.tags || 'arthichaud/meelo-server:edge' }}
labels: ${{ steps.meta.outputs.labels }}
image-suffix: server
publish: ${{ github.event_name == 'release' || (needs.changes.outputs.server == 'true' && github.event_name == 'push' && github.ref == 'refs/heads/main') }}
+63
View File
@@ -0,0 +1,63 @@
# Contributing
Hi! Thanks for considering contributing to Meelo. The project is open-source and is free, so contributions are essential to keep the project alive.
Be aware that your code contributions will be published under the [GPL-3.0 license](https://github.com/Arthi-chaud/Meelo/blob/main/LICENSE).
## Kinds of contributions
In this document we consider three kinds of contributions:
- Ideas
- Bug reports
- Feature or improvement ideas
- Code
- Documentation
- For users (e.g. wiki) or developers (e.g. the READMEs in each app directory)
## General guidelines
When contributing, please consider the following:
- If you want to suggest new feature or improvement, please open an issue first
- If you want to implement said feature, clearly state it in the issue's body
- Wait for the approval of the maintainer before starting.
- If you noticed a bug
- Either open an issue to report or ask about it
- If you are certain it is a bug and not a design choice and wish to fix it, you can open a pull request, no need to create an issue beforehand.
- If you want to contribute to the user documentation
- please first open an issue listing what you want to add/modify
- provide the new content in the issue's body.
- If you want to contribute to the developer's documentation
- No need for an issue, you can create your pull request right away.
## Code-related guidelines
### Submitting a pull request
1. Fork and clone the repository.
2. Create a new branch: `git checkout -b my-branch-name`.
3. Write code.
4. Make sure the code is formatted correctly.
5. Make sure all previous tests pass.
6. Push to your fork and submit a pull request.
### Developer documentation
Each service/app comes with a README dedicated to developers. Please read it to better understand about how the project works. **Warning**: these may be incomplete and/or outdated. If that's the case, please report it (or open a PR).
### Code formatting
All submitted code should be formatted and linted. When submitting code, please make sure your code is formatted properly (e.g. with Biome) and does not trigger any lint errors.
If you are not sure what tooling is used for a specific app, please open an issue.
### Testing
Some apps have unit tests. If it is doable, please write tests for your code. This is not mandatory but highly appreciated. If you have questions about how to set up the testing environment, please open an issue.
**All tests should pass locally**. As long as that's not the case, your contribution will not be accepted.
### CI
If you opened a pull request, the `Dockerisation` steps of the CI might fail. This is expected and should be addressed at some point. All other CI steps should pass (with one exception: the Matcher's tests might fail randomly).
+55 -29
View File
@@ -1,10 +1,15 @@
# <img alt="Icon" src="./front/public/banner.png" width="150px">
<div align="center" style="display: flex; justify-content: center; align-items: center;">
<img width="500" src="https://github.com/user-attachments/assets/8d9b147f-aad3-4cd7-898b-01e58f65dd7c" />
</div>
Welcome! Meelo is a self-hosted music server and web app. It works similarly to Plex, Jellyfin, Koel and Black Candy, but focuses on flexibility, browsing and listening experiences. Actually, Meelo is designed for music collectors. What does it mean? Scroll down to see our killer features :arrow_heading_down:
**<div align="center">[Features](#star-features) • [Setup](#-setup) • [Screenshots](#camera-screenshots) • [Wiki](https://github.com/Arthi-chaud/Meelo/wiki)</div>**
While the software wants to be a flexible as possible, it requires a little bit of setup. However, once everything is ready, you will just have to sit back, relax, and enjoy your music. :sunglasses:
<img src="https://github.com/user-attachments/assets/4a489a25-3cf7-4281-a9c1-cf9d41351e69" />
![Album Page (Top)](./assets/examples/album-top.png)
Welcome! Meelo is a self-hosted music server. It works similarly to Plex, Jellyfin, Koel and Black Candy, but focuses on flexibility, browsing and listening experiences. Actually, Meelo is designed for music collectors. What does it mean? Scroll down to see our killer features :arrow_heading_down:
While the software aims to be a flexible as possible, it requires a little bit of setup. However, once everything is ready, you will just have to sit back, relax, and enjoy your music. :sunglasses:
## :star: Features
@@ -25,62 +30,83 @@ Meelo has all the basic features you might expect from a music player/server (pl
- Songs have _tracks_
- It is pretty much the same as for albums and releases
- No duplicated songs when browsing your library!
- Song Versions
- Browse Songs [grouped by version](./assets/examples/song-groups.png)
- Songs have _versions_
- See an example [here](./assets/examples/song-groups.png)
- Album and Song types
- Finding instrumental songs or live recordings has never been this simple!
- Filter Songs that are exclusive to an album (compilations only)
- Filter Songs that are exclusive to an album (for compilation albums only)
- Supports all formats!
- Thanks to transcoding and the way we parse files, any audio and video format is virtually supported
- (Note: Transcoding is only used when the file format is not supported in the browser)
- Flexible metadata parsing
- Use either the embedded metadata or the file's name (or both!) to extract metadata
- Also works with album covers!
- Get genres, descriptions, and ratings using MusicBrainz, Genius, Wikipedia and many more providers!
- Download lyrics
- Get genres, descriptions, and ratings using MusicBrainz, Genius, Wikipedia and many other providers!
- Push Scrobbles to ListenBrainz and LastFM!
- Download (synced) lyrics or get them from embedded metadata and `.lrc` files
Keep track of the upcoming features [here](https://github.com/Arthi-chaud/Meelo/issues)
## :book: How to get started
<img src="https://github.com/user-attachments/assets/7e1fe7df-638c-42f1-978d-e1cb59f91324" />
To use Meelo, you'll need a 'clean' collection: either with embedded metadata or standard file/folder architecture. Using iTunes or Beets is a great way to start.
## 🔧 Setup
Meelo is shipped though Docker images. You might need to know a bit about Regexes.
Look over [here](https://arthi-chaud.github.io/Meelo/) for setup instructions.
:point_right: To get started, follow the dedicated [wiki](https://github.com/Arthi-chaud/Meelo/wiki)
You'll need a 'clean' collection: either with embedded metadata or standard file/folder architecture. Using iTunes or Beets is a great way to start.
> [!NOTE]
> Meelo is shipped though Docker images. You might need to know a bit about Regexes.
## :iphone: Android App
Meelo now has an Android app :tada:. However, it is still in alpha and lacks some important features.
Go [here](https://github.com/Arthi-chaud/Meelo/discussions/1130) for more information.
You can find the known issues and the list of missing/upcoming features [here](https://github.com/Arthi-chaud/Meelo/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22Front%20(Mobile)%22)
While the app is in alpha, we recommend using the web app. With its responsive design, it works perfectly on mobile devices.
## :camera: Screenshots
[![](https://github.com/user-attachments/assets/48141241-25ec-4570-a4f1-696c0cdfcaaa)](https://github.com/user-attachments/assets/48141241-25ec-4570-a4f1-696c0cdfcaaa)
:point_right: You can find more screenshots of the UI [here](./docs/screenshots/)
>[!NOTE]
> Some screenshots may be outdated and may not show the state of the latest version of the app. Some minor details may change (e.g. logo, spacing, etc.)
</details>
## :desktop_computer: Live Demo
We are actively working on a public demo, so that you can try out Meelo. Stay tuned...
## :camera: Screenshots
## 🌐 Translation
<details>
<summary>Unfold to see what the web application looks like!</summary>
Meelo uses [Weblate](https://weblate.org/en-gb/) to handle translations.
Feel free to contribute [here](https://hosted.weblate.org/engage/meelo/)!
https://github.com/user-attachments/assets/48141241-25ec-4570-a4f1-696c0cdfcaaa
[![Stats](https://hosted.weblate.org/widget/meelo/front/multi-auto.svg)](https://hosted.weblate.org/engage/meelo/)
---
![Album View](./assets/examples/album-page.png)
![Artist Page](./assets/examples/artist-page.png)
![Player Page](./assets/examples/player.png)
![Release Page](./assets/examples/releases.png)
More screenshots [here](./assets/examples/)
</details>
Any questions about this? Open a discussion [here](https://github.com/Arthi-chaud/Meelo/discussions/categories/translation)!
## :handshake: Contributing and Bugs reporting
:hammer: Would you like to contribute to Meelo? Feel free to [open a pull request](https://github.com/Arthi-chaud/Meelo/compare).
:thinking: Is there a feature you would like to have? [Open an issue](https://github.com/Arthi-chaud/Meelo/issues/new/choose), and we will be happy to discuss it!
:wrench: Need help with the setup process? Open a discussion [here](https://github.com/Arthi-chaud/Meelo/discussions/categories/need-help-with-setting-up).
:bug: Have you encountered a bug? Ugh we don't like 'em here! Report it by [opening an issue](https://github.com/Arthi-chaud/Meelo/issues/new/choose).
:hammer: Would you like to contribute to Meelo? Feel free to [open a pull request](https://github.com/Arthi-chaud/Meelo/compare).
---
Get ready to make the most out of you music collection!
[![Coverage (Back-end)](https://sonarcloud.io/api/project_badges/measure?project=arthi-chaud_Meelo-back&metric=coverage)](https://sonarcloud.io/summary/new_code?id=arthi-chaud_Meelo-back)
[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=arthi-chaud_Meelo-back&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=arthi-chaud_Meelo-back)
![Docker Pulls](https://img.shields.io/docker/pulls/arthichaud/meelo-server)
<img src="https://github.com/user-attachments/assets/4e7a161f-180a-4f95-89b1-482421018a61" />
+16 -5
View File
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"$schema": "https://biomejs.dev/schemas/2.1.3/schema.json",
"vcs": {
"enabled": false,
"clientKind": "git",
@@ -7,15 +7,25 @@
"defaultBranch": "main"
},
"files": {
"ignore": ["**/.yarn/**", "**/dist/**", "**/package.json"]
"includes": [
"**",
"!**/.yarn/**",
"!**/dist/**",
"!**/package.json",
"!**/.expo/**"
]
},
"formatter": {
"enabled": true,
"indentStyle": "tab",
"indentWidth": 4
},
"organizeImports": {
"enabled": true
"assist": {
"actions": {
"source": {
"organizeImports": "on"
}
}
},
"linter": {
"enabled": true,
@@ -35,7 +45,8 @@
"noConsole": "error"
},
"correctness": {
"noUnusedImports": "warn"
"noUnusedImports": "error",
"noUnusedVariables": "error"
}
}
},
+29 -15
View File
@@ -1,5 +1,6 @@
services:
server:
init: true
build:
context: ./server
dockerfile: Dockerfile.dev
@@ -49,7 +50,7 @@ services:
test: ["CMD-SHELL", "wget -qO- localhost:8133"]
interval: 5s
timeout: 5s
retries: 5
retries: 10
ports:
- "8133:8133"
environment:
@@ -59,7 +60,7 @@ services:
- API_KEYS=${API_KEYS}
volumes:
- ./scanner:/app
- ${DATA_DIR}:${INTERNAL_DATA_DIR}:ro
- ${DATA_DIR}:${INTERNAL_DATA_DIR}
- ${CONFIG_DIR}:${INTERNAL_CONFIG_DIR}:ro
front:
build:
@@ -73,12 +74,12 @@ services:
scanner:
condition: service_healthy
volumes:
- ./front/src:/app/src
- ./front/public:/app/public
- ./front/next.config.js:/app/next.config.js
- ./front/apps/web/:/app/apps/web/
- ./front/packages:/app/packages
- ./front/translations:/app/translations
- ./front/assets:/app/assets
- ./front/package.json:/app/package.json
- ./front/tsconfig.json:/app/tsconfig.json
- ./front/yarn.lock:/app/yarn.lock
- ./front/tsconfig.base.json:/app/tsconfig.base.json
- front_deps:/app/node_modules
environment:
# This can be commented out. see front/next.config.js
@@ -86,10 +87,17 @@ services:
- SSR_SERVER_URL=http://server:4000
- PUBLIC_SCANNER_URL=http://localhost:8133
- SSR_SCANNER_URL=http://scanner:8133
- PUBLIC_MATCHER_URL=http://localhost:6789
- SSR_MATCHER_URL=http://matcher:6789
matcher:
build:
context: ./matcher
args:
- VERSION=dev
restart: on-failure
command: fastapi dev matcher --port 6789 --host 0.0.0.0
ports:
- "6789:6789"
depends_on:
server:
condition: service_healthy
@@ -115,7 +123,14 @@ services:
- MEILI_MASTER_KEY=${MEILI_MASTER_KEY}
- MEILI_LOG_LEVEL=WARN
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--spider", "http://0.0.0.0:7700/health"]
test:
[
"CMD",
"wget",
"--no-verbose",
"--spider",
"http://0.0.0.0:7700/health",
]
timeout: 5s
retries: 5
transcoder:
@@ -126,15 +141,14 @@ services:
cpus: 1
environment:
- GOCODER_SAFE_PATH=${INTERNAL_DATA_DIR}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_SCHEMA=gocoder
- POSTGRES_SERVER=db
- PGUSER=${POSTGRES_USER}
- PGPASSWORD=${POSTGRES_PASSWORD}
- PGDATABASE=${POSTGRES_DB}
- PGHOST=db
- PGPORT=5432
volumes:
- ${DATA_DIR}:${INTERNAL_DATA_DIR}:ro
- transcoder_cache:/cache
profiles: ['', 'cpu']
depends_on:
db:
condition: service_healthy
@@ -148,9 +162,9 @@ services:
env_file:
- .env
mq:
hostname: meelo-mq
image: rabbitmq:4.0-alpine
environment:
- RABBITMQ_NODENAME=meelo
- RABBITMQ_DEFAULT_USER=${RABBITMQ_USER}
- RABBITMQ_DEFAULT_PASS=${RABBITMQ_PASSWORD}
healthcheck:
+25 -13
View File
@@ -1,7 +1,8 @@
# This compose allows to setup the whole project in the production environment
# This compose allows to setup the whole project in the production environment
services:
server:
image: ghcr.io/arthi-chaud/meelo-server:latest
init: true
image: arthichaud/meelo-server:${TAG}
expose:
- 4000
restart: on-failure
@@ -28,7 +29,7 @@ services:
timeout: 5s
retries: 5
scanner:
image: ghcr.io/arthi-chaud/meelo-scanner:latest
image: arthichaud/meelo-scanner:${TAG}
expose:
- 8133
depends_on:
@@ -48,7 +49,7 @@ services:
- ${DATA_DIR}:${INTERNAL_DATA_DIR}:ro
- ${CONFIG_DIR}:${INTERNAL_CONFIG_DIR}:ro
front:
image: ghcr.io/arthi-chaud/meelo-front:latest
image: arthichaud/meelo-front:${TAG}
expose:
- 3000
depends_on:
@@ -61,9 +62,13 @@ services:
- SSR_SERVER_URL=http://server:4000
- PUBLIC_SCANNER_URL=${PUBLIC_URL}/scanner
- SSR_SCANNER_URL=http://scanner:8133
- PUBLIC_MATCHER_URL=http://{PUBLIC_URL}/matcher
- SSR_MATCHER_URL=http://matcher:6789
matcher:
image: ghcr.io/arthi-chaud/meelo-matcher:latest
image: arthichaud/meelo-matcher:${TAG}
restart: on-failure
expose:
- 6789
depends_on:
server:
condition: service_healthy
@@ -99,7 +104,14 @@ services:
- MEILI_MASTER_KEY=${MEILI_MASTER_KEY}
- MEILI_LOG_LEVEL=WARN
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--spider", "http://0.0.0.0:7700/health"]
test:
[
"CMD",
"wget",
"--no-verbose",
"--spider",
"http://0.0.0.0:7700/health",
]
timeout: 5s
retries: 5
transcoder:
@@ -110,22 +122,21 @@ services:
- 7666
environment:
- GOCODER_SAFE_PATH=${INTERNAL_DATA_DIR}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_SCHEMA=gocoder
- POSTGRES_SERVER=db
- PGUSER=${POSTGRES_USER}
- PGPASSWORD=${POSTGRES_PASSWORD}
- PGDATABASE=${POSTGRES_DB}
- PGHOST=db
- PGPORT=5432
volumes:
- ${DATA_DIR}:${INTERNAL_DATA_DIR}:ro
- transcoder_cache:/cache
depends_on:
db:
condition: service_healthy
profiles: ['', 'cpu']
mq:
image: rabbitmq:4.0-alpine
hostname: meelo-mq
environment:
- RABBITMQ_NODENAME=meelo
- RABBITMQ_DEFAULT_USER=${RABBITMQ_USER}
- RABBITMQ_DEFAULT_PASS=${RABBITMQ_PASSWORD}
healthcheck:
@@ -150,6 +161,7 @@ services:
- FRONT_URL=http://front:3000
- SERVER_URL=http://server:4000
- SCANNER_URL=http://scanner:8133
- MATCHER_URL=http://matcher:6789
volumes:
- ./nginx.conf.template:/etc/nginx/templates/meelo.conf.template:ro
volumes:
+29 -9
View File
@@ -1,8 +1,11 @@
# This compose allows to build the whole project in a "production"-like environment
# This compose allows to build the whole project in a "production"-like environment
services:
server:
init: true
build:
context: ./server
args:
- VERSION=local
expose:
- 4000
restart: on-failure
@@ -31,6 +34,8 @@ services:
front:
build:
context: ./front
args:
- VERSION=local
expose:
- 3000
depends_on:
@@ -43,9 +48,13 @@ services:
- SSR_SERVER_URL=http://server:4000
- PUBLIC_SCANNER_URL=${PUBLIC_URL}/scanner
- SSR_SCANNER_URL=http://scanner:8133
- PUBLIC_MATCHER_URL=http://{PUBLIC_URL}/matcher
- SSR_MATCHER_URL=http://matcher:6789
scanner:
build:
context: ./scanner
args:
- VERSION=local
expose:
- 8133
depends_on:
@@ -67,7 +76,11 @@ services:
matcher:
build:
context: ./matcher
args:
- VERSION=local
restart: on-failure
expose:
- 6789
depends_on:
server:
condition: service_healthy
@@ -91,15 +104,14 @@ services:
condition: service_healthy
environment:
- GOCODER_SAFE_PATH=${INTERNAL_DATA_DIR}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_SCHEMA=gocoder
- POSTGRES_SERVER=db
- PGUSER=${POSTGRES_USER}
- PGPASSWORD=${POSTGRES_PASSWORD}
- PGDATABASE=${POSTGRES_DB}
- PGHOST=db
- PGPORT=5432
volumes:
- ${DATA_DIR}:${INTERNAL_DATA_DIR}:ro
- transcoder_cache:/cache
profiles: ['', 'cpu']
db:
image: postgres:alpine3.14
healthcheck:
@@ -123,13 +135,20 @@ services:
- MEILI_MASTER_KEY=${MEILI_MASTER_KEY}
- MEILI_LOG_LEVEL=WARN
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--spider", "http://0.0.0.0:7700/health"]
test:
[
"CMD",
"wget",
"--no-verbose",
"--spider",
"http://0.0.0.0:7700/health",
]
timeout: 5s
retries: 5
mq:
image: rabbitmq:4.0-alpine
hostname: meelo-mq
environment:
- RABBITMQ_NODENAME=meelo
- RABBITMQ_DEFAULT_USER=${RABBITMQ_USER}
- RABBITMQ_DEFAULT_PASS=${RABBITMQ_PASSWORD}
healthcheck:
@@ -154,6 +173,7 @@ services:
- FRONT_URL=http://front:3000
- SERVER_URL=http://server:4000
- SCANNER_URL=http://scanner:8133
- MATCHER_URL=http://matcher:6789
volumes:
- ./nginx.conf.template:/etc/nginx/templates/meelo.conf.template:ro
volumes:
-1
View File
@@ -1 +0,0 @@
site/
-45
View File
@@ -1,45 +0,0 @@
site_name: Meelo - User Documentation
docs_dir: './src'
repo_url: https://github.com/Arthi-chaud/Meelo
nav:
- Setup:
- TLDR: setup/tldr.md
- Requirements: setup/requirements.md
- Prepare Music Library: setup/prepare-music-library.md
- Create Configuration Folder: setup/create-conf-folder.md
- Prepare Your Settings: setup/settings.md
- Configure Providers: setup/providers.md
- Environment Variables: setup/env.md
- Docker: setup/docker.md
- Start: setup/startup.md
- Create First Library: setup/create-library.md
- Album Types: album-types.md
- Song Types: song-types.md
- Featuring Artists: featuring-artists.md
theme:
name: material
language: en
palette:
scheme: default
titles_only: true
icon:
repo: fontawesome/brands/github
font:
text: Noto Sans
logo: ./icon.png
favicon: ./favicon.ico
features:
- navigation.instant
- navigation.tracking
- navigation.tabs
- navigation.tabs.sticky
- navigation.sections
- navigation.indexes
- toc.integrate
- navigation.top
extra_css:
- stylesheets/extra.css
plugins:
- search
markdown_extensions:
- mdx_truly_sane_lists
-2
View File
@@ -1,2 +0,0 @@
mkdocs-material
mdx_truly_sane_lists
Binary file not shown.

After

Width:  |  Height:  |  Size: 794 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 776 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 815 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

File renamed without changes.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

File renamed without changes.
File renamed without changes.
File renamed without changes.
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

File renamed without changes.
File renamed without changes.
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 MiB

File renamed without changes.
File renamed without changes.
-42
View File
@@ -1,42 +0,0 @@
# Meelo's Album Types
In Music Apps, we usually call any song container an *Album*. While this is technically incorrect, many music streaming services categorizes these *Albums* in sub-categories: Albums, Singles, Live, Compilations, etc.
## Available Types
Meelo supports this categorization, and the following album types:
- Studio Recording
- What we would usually call *Albums*
- Live Recording
- Compilation
- Single
- Soundtrack
- Remix Album
- Collections that would mainly have remixes of songs by a single artist, while not being a Single
- Video Album
- Any kind of collection that would only contain video tracks
## Type recognition
Meelo tries to be as independent of user interactions as possible. Therefore, type recognition is done automatically, when a new album is registered.
For now, Type recognition is solely based on the Album's title. Keywords to recognize an album's type were inspired by the way other music streaming services would categorize an album. Therefore, if your music collection's naming convention work a similar way to Spotify, Apple Music, ..., there might not be a lot to change if you want to fully enjoy this feature. However, if you consider that one of the following ways to recognize a type is incomplete or incorrect, feel free to [open an issue](https://github.com/Arthi-chaud/Meelo/issues).
### Keywords
| Album Type | Keywords |
|---|---|
| Studio Album | See note below |
| Compilations | - `best of`<br>- `hits`<br>- `greatest hits`<br>- `singles`<br>- `collection`<br>+ See note below |
| Live Recording | - '` tour`' (Notice the leading whitespace)<br>- `live from`<br>- `live at`<br>- `unplugged`<br>- `(live)` |
| Single | - `- single`<br>- `- ep`<br>- `(remixes)` |
| Video Album | - `music videos`<br>- `the video`<br>- `dvd` |
| Remix Album | - `remix album`<br>- `mixes`<br>- `the remixes`<br>- `remixed`<br>- `remixes`<br>- `best mixes` |
| Soundtrack | - `soundtrack`<br>- `from the motion picture`<br>- `music from and inspired by` |
Notes:
- Keywords are case-insensitive.
- Any album that would not fall into the other categories will be considered to be a Compilation IF the album is a 'compilation' album (see metadata)
- Any album that would not fall into the other categories will be considered to be a Studio Recording
-1
View File
@@ -1 +0,0 @@
../../front/public/banner-black.png
-1
View File
@@ -1 +0,0 @@
../../front/public/favicon.ico
-53
View File
@@ -1,53 +0,0 @@
# Meelo's Featuring Artist Management
Meelo is now able to parse and recognise featuring artists on a song, based on:
- Its name
- Its artist field (from the metadata)
It does not leverage the album artist.
For example, a song named 'My Song (feat. Another Artist)' by 'Primary Artist & Other Artist' would be parsed to 'My Song' by artist 'Primary artist', featuring 'Other Artist' and 'Another Artist'.
Therefore, each featuring artist will be added to the database, and treated like any other artist.
Parsing works with `()`, `[]` and `-` as delimiters.
## Examples
In this section, we list examples that would be successfully parsed by Meelo
- 'My Song (feat. B)' by 'A'
- Name: 'My Song'
- Artist: 'A'
- Featuring: 'B'
- 'My Song (Remix) [feat. B]' by 'A'
- Name: 'My Song (Remix)'
- Artist: 'A'
- Featuring: 'B'
- 'My Song' by 'A & B'
- Name: 'My Song'
- Artist: 'A'
- Featuring: 'B'
- 'My Song' by 'A, B & C'
- Name: 'My Song'
- Artist: 'A'
- Featuring: 'B', 'C'
- 'My Song - With D' by 'A, B & C'
- Name: 'My Song'
- Artist: 'A'
- Featuring: 'B', 'C', 'D'
- 'My Song - Version With Drums' by 'A'
- Name: 'My Song - Version With Drums'
- Artist: 'A'
- Featuring: None
- 'My Song' by 'A Vs. B'
- Name: 'My Song'
- Artist: 'A'
- Featuring: 'B'
## Special Case: Artist is already known
By default, we use '&' to find and split a list of artist. However, this behaviour changes if an artist containing '&' already exists.
For example, if we had an *album* artist named 'A & B' (since album artist are not parsed as much as song artists), a song named 'My Song (feat. A & B)' would be parsed as 'My Song', with featuring artist 'A & B'.
-1
View File
@@ -1 +0,0 @@
../../front/public/icon.png
-3
View File
@@ -1,3 +0,0 @@
![Meelo](./banner-black.png)
# Welcome to Meelo's User Documentation
-7
View File
@@ -1,7 +0,0 @@
# Create a configuration folder
Meelo is runnable through a Docker container. But it needs a way to be parameterized and persist some data.
Therefore, it is recommended that you create a dedicated folder on your computer for Meelo.
For the sake of clarity, in this documentation, we'll refer to this folder as `Meelo`.
-8
View File
@@ -1,8 +0,0 @@
# Create your first library
After creating the admin account (from Web App),
Visit the `/settings/libraries`, and click `Create Library` to create your first library.
Once your library is created, you should run a scan to collect the related files. To do so, click the *Scan* button.
Your Meelo server is ready to be used! Visit the `/` route on your favourite browser to enjoy your favourite music :).
-5
View File
@@ -1,5 +0,0 @@
# Docker and Docker-compose
In your `Meelo` folder, copy this [docker-compose file](https://raw.githubusercontent.com/Arthi-chaud/Meelo/master/docker-compose.prod.yml) as `docker-compose.yml`. It can be used as-is.
Additionally, you should copy [nginx configuration file](https://raw.githubusercontent.com/Arthi-chaud/Meelo/master/nginx.conf.template) as is in your `Meelo` folder.
-3
View File
@@ -1,3 +0,0 @@
# Environment variables
In your `Meelo` directory, copy this [`.env.example`](https://raw.githubusercontent.com/Arthi-chaud/Meelo/master/.env.example) as `.env` and fill the variables
-18
View File
@@ -1,18 +0,0 @@
# Prepare your music library
The toughest part is to have an structured music library. The way files are organised are up to you! But here is a recommended architecture:
```tree
Library's Folder
│
Artist 1
│ │
│ └───Album 1
│ │ 01 Track 1.m4a
│ │ 02 Track 2.m4a
| | cover.jpg
│ │ ...
│ ...
```
Meelo provides two ways of collecting metadata: using embedded tags, or using the path of the file. If a metadata source does not provide a specific field, you can use the other as a fallback. (You will configure this behaviour in the next sections). But before going any further, you should decide which metadata collection method to use.
-42
View File
@@ -1,42 +0,0 @@
# Providers
## Enabling Providers
In the `settings.json`, ff the `provider` object does not contain a provider's settings, this provider will be disabled.
Providers can also be disabled explicitly by setting the Provider's settings' field `enabled` at `false`. For example:
```json
// In settings.json
{
//... Other properties
"providers": {
"provider1": {}, // Provider 1 is enabled
// Provider 2 was not specified, it is therefore disabled
"provider3": {
"enabled": true // Provider 3 is enabled
},
"provider4": {
"enabled": false, // Provider 4 is disabled
"apiKey": "" // Other property of provider 4
}
}
}
```
If a provider is disabled, no call to its service will be done.
## Providers' properties and features
| Provider Name | Key | Additional Properties | Artist Description | Artist Illustration | Album Description | Album Rating | Song Description | Song Lyrics | Release Identification | Album Genres |
|---------------|---------------|--------------------------------------------------------------------------------------------|--------------------|---------------------|-------------------|--------------|------------------|-------------|------------------------|--------------|
| Genius | `genius` | `apiKey`: Access Token, you can get it [here](https://genius.com/api-clients) | x | x | x | | x | x | | |
| MusicBrainz | `musicbrainz` | None | | | | | | | | x |
| AllMusic | `allmusic` | None | | | x | x | | | | |
| MetaCritic | `metacritic` | None | | | x | x | | | | |
| Wikipedia | `wikipedia` | None | x | | x | | x | | | |
| Discogs | `discogs` | `apiKey`: Access Token, you can get it [here](https://www.discogs.com/settings/developers) | x | | | | | | x | x |
**Warning**: Meelo heavily relies on Wikidata to optimize the metadata fetching. It is used if, and only if the MusicBrainz Provider is enabled. It is not possible to use one without the other.
*Note*: If MusicBrainz is enabled, we might make requests to [AcoustID](https://acoustid.org)'s API to match songs using audio fingerprints (No data will be submitted).
-8
View File
@@ -1,8 +0,0 @@
# Requirements
Meelo is easy to setup! But you'll need the following things before continuing
- Have you music library ready (see [*this step*](../prepare-music-library))
- Know how `Docker` and `docker-compose` work
- Know how to create a `.env` file
- Know how to manipulate Regular Expressions (Especially the Golang flavor)
-35
View File
@@ -1,35 +0,0 @@
# Prepare your settings
In your `Meelo` folder, download and fill [this `settings.json` file](https://raw.githubusercontent.com/Arthi-chaud/Meelo/master/settings.json)
All fields are **required**
- `trackRegex`: Probably the most important setting for Meelo. This field takes an array of strings, each string being a Regular Expression string. The RegExp will be used to find the files to import (It will be matched against the **absolute path** of the file). As mentioned before, the path of the file can be used as a metadata source. Therefore, **the Regular Expression accepts groups to find metadata**:
- `AlbumArtist`: The artist of the parent album.
- Special Case: If it equals `Compilations`, related album will be considered as compilation albums (with no main artist, like soundtrack albums)
- `Artist`: The artist of the track. If it is not present, it will use the `AlbumArtist` field.
- `Release`: Usually no present in a standard file architecture; the name of the *release*. When it is not present, the `Album` field will be used.
- `Album`: The name of the album of the material. This field will then be parsed to group related album releases.
- `Year`: The Year of the release of the material
- `Disc`: The Index of the disc the track is on.
- `Index`: The index of the track in a playlist/on a disc.
- `Track`: The name of the track
- `Genre`: The genre of the material
- `DiscogsID`: The Discogs ID of the parent release. A Discogs ID should take the form of a digit-only string. This ID can be found in the URL of the release's Discogs page or in the top-right corner of that page (displayed as `[rXXXXXXXX]`)
- *Note*: **If you do not use embedded data**, most regex groups are optional. Only `Track` and (`AlbumArtist` or `Artist`) are required for the file to be accepted by the scanner.
- `metadata`: Configuration of the **metadata extraction** system:
- `source`: The **primary metadata source** to use:
- `embedded`: Using the embedded tags
- `path`: Using the Regular Expression's groups.
- `order`: Define the missing metadata fallback behaviour. *Note*: if this method is enabled, it will consider 'cover.*' file in the same directory as the media file as an illustration candidate.
- `only`: If a field is missing from the primary metadata source, will not use the other method to get it.
- `preferred`: If a field is missing from the primary metadata source, will try to use the other method to get it.
- `useExternalProviderGenres`: Set to `true` if you want genres from external metadata providers to be used. These genres will be used alongside with embedded genres (if parsing embedded metadata is enabled)
- `providers`: Please refer to [*Providers*](../providers)
- `compilations`:
- `artists`: Optional field. If an album artist in this list, their albums will be considered to be compilation albums.
- `useID3CompTag`: If true, use iTunes' non-standard ID3 tags for compilations.
*Note*: The provided Regular Expression uses the iTunes file architecture.
*Note*: Regular Expression must use the Golang Flavour.
-16
View File
@@ -1,16 +0,0 @@
# Start Meelo
Once these three files are ready, run the following command from your `Meelo` folder:
```bash
docker-compose up
```
## Troubleshooting
Two things can prevent Meelo from starting normally:
- A bad `settings.json` file
- In that case, a message should tell you what went wrong
- Connection to database failed
- In that case, you should have a red message telling you a query failed. To fix, simply restart the server
-12
View File
@@ -1,12 +0,0 @@
# TLDR
Don't feel like reading? Here's how to setup Meelo quickly
- Download and fill [this `docker-compose` file](https://raw.githubusercontent.com/Arthi-chaud/Meelo/master/docker-compose.prod.yml)
- Download and fill [this `nginx` configuration file](https://raw.githubusercontent.com/Arthi-chaud/Meelo/master/nginx.conf.template)
- Download and fill [this `.env` file](https://raw.githubusercontent.com/Arthi-chaud/Meelo/master/.env.example)
- Download and fill [this `settings.json` file](https://raw.githubusercontent.com/Arthi-chaud/Meelo/master/settings.json)
You should be ready to go! If something goes wrong, please take the time to read the `Setup` section of this documentation.
Enjoy!
-31
View File
@@ -1,31 +0,0 @@
# Meelo's Album Types
It is possible to filter songs based on their *type*.
## Available Types
Meelo supports the following song types:
- Original
- Live
- Acoustic
- Remix
- Instrumental
- A Cappella
- Edit
- Of the original version.
- If the song is an edit of a remix, it will be categorised as 'Remix'
- Clean
- Demo
- Medley
- Non-Music (e.g. Interviews)
## Type recognition
Songs' type recognition is solely based on keywords present in their name. For example:
- 'My Song (Remix)' would be typed as 'Remix'
- 'My Song (Live)' would be recognised as a Live rendition
- 'My Song (Rough Mix)' would be defined as 'Demo'
- 'My Song (Instrumental Mix)' would be an 'Instrumental'
- 'My Song (Instrumental Break Down Mix)' would be a 'Remix'
-3
View File
@@ -1,3 +0,0 @@
:root {
--md-primary-fg-color: #2F2C2B;
}
+1 -1
View File
@@ -1,2 +1,2 @@
node_modules/
.next/
apps/web/.next/
-83
View File
@@ -1,83 +0,0 @@
{
"extends": [
"next/core-web-vitals",
"plugin:prettier/recommended",
"plugin:react/recommended",
"../.eslintrc.json"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": ["./tsconfig.json"]
},
"plugins": ["react", "@typescript-eslint", "unused-imports", "import"],
"ignorePatterns": ["node_modules/", "jest.config.js", "__tests__"],
"rules": {
"react/prop-types": "off",
"react/react-in-jsx-scope": "off",
"react/sort-prop-types": "warn",
"react/jsx-props-no-multi-spaces": "warn",
"react/jsx-max-props-per-line": [
"error",
{ "maximum": 3, "when": "multiline" }
],
"react/jsx-key": "error",
"react/jsx-indent-props": ["warn", "tab"],
"react/jsx-curly-spacing": [
"warn",
{ "when": "never", "children": false }
],
"react/jsx-indent": ["warn", "tab"],
"unused-imports/no-unused-imports": "error",
"no-restricted-imports": [
"error",
{
"paths": [
{
"name": "react-query",
"importNames": [
"useQueryClient",
"useQuery",
"useInfiniteQuery",
"useQueries"
],
"message": "Use wrapper functions provided by src/api/query"
},
{
"name": "@mui/material",
"importNames": ["Link"],
"message": "Use Link from 'next/router"
},
{
"name": "@mui/material",
"importNames": ["Modal"],
"message": "Use 'confirm' from 'material-ui-confirm'"
},
{
"name": "@mui/material",
"importNames": ["ThemeProvider"],
"message": "Use wrapper from theme/provider"
},
{
"name": "format-duration",
"message": "Use wrapper"
},
{
"name": "@mui/material",
"importNames": ["Fade"],
"message": "Use wrapper from 'components/fade'"
},
{
"name": "@mui/icons-material",
"message": "Use icons in 'src/components/icons'"
}
],
"patterns": [
{
"group": ["@mui/icons-material/*"],
"message": "Use icons in 'src/components/icons'"
}
]
}
]
}
}
+1 -1
View File
@@ -9,7 +9,7 @@
/coverage
# next.js
/.next/
.next/
/out/
# production
@@ -0,0 +1,414 @@
diff --git a/cpp/core/HybridFunction.hpp b/cpp/core/HybridFunction.hpp
index efcea05a248540806cfefd04efb44920ed941228..d8604ffb8cf4c4d6ed42d8238f8012a9d6ca0bd7 100644
--- a/cpp/core/HybridFunction.hpp
+++ b/cpp/core/HybridFunction.hpp
@@ -8,8 +8,7 @@
#pragma once
namespace margelo::nitro {
-template <typename T, typename Enable>
-struct JSIConverter;
+template <typename T, typename Enable> struct JSIConverter;
} // namespace margelo::nitro
#include "CountTrailingOptionals.hpp"
@@ -23,13 +22,17 @@ struct JSIConverter;
#include <string>
#include <type_traits>
+#ifdef ANDROID
+#include <fbjni/fbjni.h>
+#endif
+
namespace margelo::nitro {
using namespace facebook;
/**
- * Represents the kind of a function - it can be either a normal function ("METHOD"),
- * or a property ("GETTER" + "SETTER")
+ * Represents the kind of a function - it can be either a normal function
+ * ("METHOD"), or a property ("GETTER" + "SETTER")
*/
enum class FunctionKind { METHOD, GETTER, SETTER };
@@ -44,76 +47,108 @@ private:
public:
// getters
- inline const std::string& getName() const {
- return _name;
- }
- inline size_t getParamCount() const {
- return _paramCount;
- }
- inline const jsi::HostFunctionType& getHostFunction() const {
+ inline const std::string &getName() const { return _name; }
+ inline size_t getParamCount() const { return _paramCount; }
+ inline const jsi::HostFunctionType &getHostFunction() const {
return _function;
}
public:
// functions
- inline jsi::Function toJSFunction(jsi::Runtime& runtime) const {
- return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, _name), static_cast<unsigned int>(_paramCount),
- _function);
+ inline jsi::Function toJSFunction(jsi::Runtime &runtime) const {
+ return jsi::Function::createFromHostFunction(
+ runtime, jsi::PropNameID::forUtf8(runtime, _name),
+ static_cast<unsigned int>(_paramCount), _function);
}
private:
- HybridFunction(jsi::HostFunctionType&& function, size_t paramCount, const std::string& name)
+ HybridFunction(jsi::HostFunctionType &&function, size_t paramCount,
+ const std::string &name)
: _function(std::move(function)), _paramCount(paramCount), _name(name) {}
public:
/**
* Create a new `HybridFunction` that can be called from JS.
* This performs proper JSI -> C++ conversion using `JSIConverter<T>`,
- * and assumes that the object this is called on has a proper `this` configured.
- * The object's `this` needs to be a `NativeState`.
+ * and assumes that the object this is called on has a proper `this`
+ * configured. The object's `this` needs to be a `NativeState`.
*/
template <typename THybrid, typename ReturnType, typename... Args>
- static inline HybridFunction createHybridFunction(const std::string& name, ReturnType (THybrid::*method)(Args...), FunctionKind kind) {
- jsi::HostFunctionType hostFunction = [name, method, kind](/* JS Runtime */ jsi::Runtime& runtime,
- /* HybridObject */ const jsi::Value& thisValue,
- /* JS arguments */ const jsi::Value* args,
- /* argument size */ size_t count) -> jsi::Value {
- // 1. Get actual `HybridObject` instance from `thisValue` (it's stored as `NativeState`)
- std::shared_ptr<THybrid> hybridInstance = getHybridObjectNativeState<THybrid>(runtime, thisValue, kind, name);
-
- // 2. Make sure the given arguments match, either with a static size, or with potentially optional arguments size.
+ static inline HybridFunction
+ createHybridFunction(const std::string &name,
+ ReturnType (THybrid::*method)(Args...),
+ FunctionKind kind) {
+ jsi::HostFunctionType hostFunction =
+ [name, method, kind](/* JS Runtime */ jsi::Runtime &runtime,
+ /* HybridObject */ const jsi::Value &thisValue,
+ /* JS arguments */ const jsi::Value *args,
+ /* argument size */ size_t count) -> jsi::Value {
+ // 1. Get actual `HybridObject` instance from `thisValue` (it's stored as
+ // `NativeState`)
+ std::shared_ptr<THybrid> hybridInstance =
+ getHybridObjectNativeState<THybrid>(runtime, thisValue, kind, name);
+
+ // 2. Make sure the given arguments match, either with a static size, or
+ // with potentially optional arguments size.
constexpr size_t optionalArgsCount = trailing_optionals_count_v<Args...>;
constexpr size_t maxArgsCount = sizeof...(Args);
constexpr size_t minArgsCount = maxArgsCount - optionalArgsCount;
bool isWithinArgsRange = (count >= minArgsCount && count <= maxArgsCount);
if (!isWithinArgsRange) [[unlikely]] {
// invalid amount of arguments passed!
- std::string funcName = getHybridFuncFullName<THybrid>(kind, name, hybridInstance.get());
+ std::string funcName =
+ getHybridFuncFullName<THybrid>(kind, name, hybridInstance.get());
if constexpr (minArgsCount == maxArgsCount) {
- // min and max args length is the same, so we don't have any optional parameters. fixed count
- throw jsi::JSError(runtime, "`" + funcName + "` expected " + std::to_string(maxArgsCount) + " arguments, but received " +
+ // min and max args length is the same, so we don't have any optional
+ // parameters. fixed count
+ throw jsi::JSError(runtime, "`" + funcName + "` expected " +
+ std::to_string(maxArgsCount) +
+ " arguments, but received " +
std::to_string(count) + "!");
} else {
- // min and max args length are different, so we have optional parameters - variable length arguments.
- throw jsi::JSError(runtime, "`" + funcName + "` expected between " + std::to_string(minArgsCount) + " and " +
- std::to_string(maxArgsCount) + " arguments, but received " + std::to_string(count) + "!");
+ // min and max args length are different, so we have optional
+ // parameters - variable length arguments.
+ throw jsi::JSError(runtime, "`" + funcName + "` expected between " +
+ std::to_string(minArgsCount) +
+ " and " +
+ std::to_string(maxArgsCount) +
+ " arguments, but received " +
+ std::to_string(count) + "!");
}
}
try {
- // 3. Actually call the method with JSI values as arguments and return a JSI value again.
- // Internally, this method converts the JSI values to C++ values using `JSIConverter<T>`.
- return callMethod(hybridInstance.get(), method, runtime, args, count, std::index_sequence_for<Args...>{});
- } catch (const std::exception& exception) {
- // Some exception was thrown - add method name information and re-throw as `JSError`.
- std::string funcName = getHybridFuncFullName<THybrid>(kind, name, hybridInstance.get());
+ // 3. Actually call the method with JSI values as arguments and return a
+ // JSI value again.
+ // Internally, this method converts the JSI values to C++ values
+ // using `JSIConverter<T>`.
+ return callMethod(hybridInstance.get(), method, runtime, args, count,
+ std::index_sequence_for<Args...>{});
+ } catch (const std::exception &exception) {
+ // Some exception was thrown - add method name information and re-throw
+ // as `JSError`.
+ std::string funcName =
+ getHybridFuncFullName<THybrid>(kind, name, hybridInstance.get());
+ std::string message = exception.what();
+ throw jsi::JSError(runtime, funcName + ": " + message);
+#ifdef ANDROID
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wexceptions"
+ } catch (const jni::JniException &exception) {
+ std::string funcName =
+ getHybridFuncFullName<THybrid>(kind, name, hybridInstance.get());
std::string message = exception.what();
throw jsi::JSError(runtime, funcName + ": " + message);
+#pragma clang diagnostic pop
+#endif
} catch (...) {
- // Some unknown exception was thrown - add method name information and re-throw as `JSError`.
- std::string funcName = getHybridFuncFullName<THybrid>(kind, name, hybridInstance.get());
+ // Some unknown exception was thrown - add method name information and
+ // re-throw as `JSError`.
+ std::string funcName =
+ getHybridFuncFullName<THybrid>(kind, name, hybridInstance.get());
std::string errorName = TypeInfo::getCurrentExceptionName();
- throw jsi::JSError(runtime, "`" + funcName + "` threw an unknown " + errorName + " error.");
+ throw jsi::JSError(runtime, "`" + funcName + "` threw an unknown " +
+ errorName + " error.");
}
};
@@ -122,46 +157,64 @@ public:
/**
* Create a new `HybridFunction` that can be called from JS.
- * Unlike `createHybridFunction(...)`, this method does **not** perform any argument parsing or size checking.
- * It is a raw-, untyped JSI method, and the user is expected to manually handle arguments and return values.
+ * Unlike `createHybridFunction(...)`, this method does **not** perform any
+ * argument parsing or size checking. It is a raw-, untyped JSI method, and
+ * the user is expected to manually handle arguments and return values.
*/
template <typename Derived>
- static inline HybridFunction createRawHybridFunction(const std::string& name, size_t expectedArgumentsCount,
- jsi::Value (Derived::*method)(jsi::Runtime& runtime, const jsi::Value& thisArg,
- const jsi::Value* args, size_t count)) {
- jsi::HostFunctionType hostFunction = [name, method](/* JS Runtime */ jsi::Runtime& runtime,
- /* HybridObject */ const jsi::Value& thisValue,
- /* JS arguments */ const jsi::Value* args,
- /* argument size */ size_t count) -> jsi::Value {
- // 1. Get actual `HybridObject` instance from `thisValue` (it's stored as `NativeState`)
- std::shared_ptr<Derived> hybridInstance = getHybridObjectNativeState<Derived>(runtime, thisValue, FunctionKind::METHOD, name);
-
- // 2. Call the raw JSI method using raw JSI Values. Exceptions are also expected to be handled by the user.
- Derived* pointer = hybridInstance.get();
+ static inline HybridFunction createRawHybridFunction(
+ const std::string &name, size_t expectedArgumentsCount,
+ jsi::Value (Derived::*method)(jsi::Runtime &runtime,
+ const jsi::Value &thisArg,
+ const jsi::Value *args, size_t count)) {
+ jsi::HostFunctionType hostFunction =
+ [name, method](/* JS Runtime */ jsi::Runtime &runtime,
+ /* HybridObject */ const jsi::Value &thisValue,
+ /* JS arguments */ const jsi::Value *args,
+ /* argument size */ size_t count) -> jsi::Value {
+ // 1. Get actual `HybridObject` instance from `thisValue` (it's stored as
+ // `NativeState`)
+ std::shared_ptr<Derived> hybridInstance =
+ getHybridObjectNativeState<Derived>(runtime, thisValue,
+ FunctionKind::METHOD, name);
+
+ // 2. Call the raw JSI method using raw JSI Values. Exceptions are also
+ // expected to be handled by the user.
+ Derived *pointer = hybridInstance.get();
return (pointer->*method)(runtime, thisValue, args, count);
};
- return HybridFunction(std::move(hostFunction), expectedArgumentsCount, name);
+ return HybridFunction(std::move(hostFunction), expectedArgumentsCount,
+ name);
}
private:
/**
- * Calls the given method on the given instance with the given `jsi::Value` arguments by converting them to the desired target types.
- * The given method's return value will be converted to a `jsi::Value` again.
+ * Calls the given method on the given instance with the given `jsi::Value`
+ * arguments by converting them to the desired target types. The given
+ * method's return value will be converted to a `jsi::Value` again.
*/
- template <typename Derived, typename ReturnType, typename... Args, size_t... Is>
- static inline jsi::Value callMethod(Derived* obj, ReturnType (Derived::*method)(Args...), jsi::Runtime& runtime, const jsi::Value* args,
- size_t argsSize, std::index_sequence<Is...>) {
+ template <typename Derived, typename ReturnType, typename... Args,
+ size_t... Is>
+ static inline jsi::Value
+ callMethod(Derived *obj, ReturnType (Derived::*method)(Args...),
+ jsi::Runtime &runtime, const jsi::Value *args, size_t argsSize,
+ std::index_sequence<Is...>) {
static const jsi::Value defaultValue;
if constexpr (std::is_void_v<ReturnType>) {
// It's a void method.
- (obj->*method)(JSIConverter<std::decay_t<Args>>::fromJSI(runtime, Is < argsSize ? args[Is] : defaultValue)...);
+ (obj->*method)(JSIConverter<std::decay_t<Args>>::fromJSI(
+ runtime, Is < argsSize ? args[Is] : defaultValue)...);
return jsi::Value::undefined();
} else {
- // It's returning some C++ type, we need to convert that to a JSI value now.
- ReturnType result = (obj->*method)(JSIConverter<std::decay_t<Args>>::fromJSI(runtime, Is < argsSize ? args[Is] : defaultValue)...);
- return JSIConverter<ReturnType>::toJSI(runtime, std::forward<ReturnType>(result));
+ // It's returning some C++ type, we need to convert that to a JSI value
+ // now.
+ ReturnType result =
+ (obj->*method)(JSIConverter<std::decay_t<Args>>::fromJSI(
+ runtime, Is < argsSize ? args[Is] : defaultValue)...);
+ return JSIConverter<ReturnType>::toJSI(runtime,
+ std::forward<ReturnType>(result));
}
}
@@ -170,20 +223,24 @@ private:
* Get the `NativeState` of the given `value`.
*/
template <typename THybrid>
- static inline std::shared_ptr<THybrid> getHybridObjectNativeState(jsi::Runtime& runtime, const jsi::Value& value,
- [[maybe_unused]] FunctionKind funcKind,
- [[maybe_unused]] const std::string& funcName) {
+ static inline std::shared_ptr<THybrid>
+ getHybridObjectNativeState(jsi::Runtime &runtime, const jsi::Value &value,
+ [[maybe_unused]] FunctionKind funcKind,
+ [[maybe_unused]] const std::string &funcName) {
// 1. Convert jsi::Value to jsi::Object
#ifdef NITRO_DEBUG
if (!value.isObject()) [[unlikely]] {
- throw jsi::JSError(runtime, "Cannot " + getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
- " - `this` is not bound! Suggestions:\n"
- "- Did you accidentally destructure the `HybridObject`? (`const { " +
- funcName +
- " } = ...`)\n"
- "- Did you call `dispose()` on the `HybridObject` before?"
- "- Did you accidentally call `" +
- funcName + "` on the prototype directly?");
+ throw jsi::JSError(
+ runtime,
+ "Cannot " + getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
+ " - `this` is not bound! Suggestions:\n"
+ "- Did you accidentally destructure the `HybridObject`? (`const "
+ "{ " +
+ funcName +
+ " } = ...`)\n"
+ "- Did you call `dispose()` on the `HybridObject` before?"
+ "- Did you accidentally call `" +
+ funcName + "` on the prototype directly?");
}
#endif
jsi::Object object = value.getObject(runtime);
@@ -191,33 +248,42 @@ private:
// 2. Check if it even has any kind of `NativeState`
#ifdef NITRO_DEBUG
if (!object.hasNativeState(runtime)) [[unlikely]] {
- throw jsi::JSError(runtime, "Cannot " + getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
- " - `this` does not have a NativeState! Suggestions:\n"
- "- Did you accidentally destructure the `HybridObject`? (`const { " +
- funcName +
- " } = ...`)\n"
- "- Did you call `dispose()` on the `HybridObject` before?"
- "- Did you accidentally call `" +
- funcName + "` on the prototype directly?");
+ throw jsi::JSError(
+ runtime,
+ "Cannot " + getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
+ " - `this` does not have a NativeState! Suggestions:\n"
+ "- Did you accidentally destructure the `HybridObject`? (`const "
+ "{ " +
+ funcName +
+ " } = ...`)\n"
+ "- Did you call `dispose()` on the `HybridObject` before?"
+ "- Did you accidentally call `" +
+ funcName + "` on the prototype directly?");
}
#endif
// 3. Get `NativeState` from the jsi::Object and check if it is non-null
- std::shared_ptr<jsi::NativeState> nativeState = object.getNativeState(runtime);
+ std::shared_ptr<jsi::NativeState> nativeState =
+ object.getNativeState(runtime);
#ifdef NITRO_DEBUG
if (nativeState == nullptr) [[unlikely]] {
- throw jsi::JSError(runtime, "Cannot " + getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
- " - `this`'s `NativeState` is `null`, "
- "did you accidentally call `dispose()` on this object?");
+ throw jsi::JSError(
+ runtime, "Cannot " +
+ getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
+ " - `this`'s `NativeState` is `null`, "
+ "did you accidentally call `dispose()` on this object?");
}
#endif
// 4. Try casting it to our desired target type.
- std::shared_ptr<THybrid> hybridInstance = std::dynamic_pointer_cast<THybrid>(nativeState);
+ std::shared_ptr<THybrid> hybridInstance =
+ std::dynamic_pointer_cast<THybrid>(nativeState);
#ifdef NITRO_DEBUG
if (hybridInstance == nullptr) [[unlikely]] {
- throw jsi::JSError(runtime, "Cannot " + getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
- " - `this` has a NativeState, but it's the wrong type!");
+ throw jsi::JSError(
+ runtime, "Cannot " +
+ getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
+ " - `this` has a NativeState, but it's the wrong type!");
}
#endif
return hybridInstance;
@@ -225,28 +291,33 @@ private:
private:
template <typename THybrid>
- static inline std::string getHybridFuncFullName(FunctionKind kind, const std::string& registrationName,
- THybrid* hybridInstance = nullptr) {
- std::string typeName = hybridInstance != nullptr ? hybridInstance->getName() : TypeInfo::getFriendlyTypename<THybrid>(true);
+ static inline std::string
+ getHybridFuncFullName(FunctionKind kind, const std::string &registrationName,
+ THybrid *hybridInstance = nullptr) {
+ std::string typeName = hybridInstance != nullptr
+ ? hybridInstance->getName()
+ : TypeInfo::getFriendlyTypename<THybrid>(true);
switch (kind) {
- case FunctionKind::METHOD:
- return typeName + "." + registrationName + "(...)";
- case FunctionKind::GETTER:
- case FunctionKind::SETTER:
- return typeName + "." + registrationName;
+ case FunctionKind::METHOD:
+ return typeName + "." + registrationName + "(...)";
+ case FunctionKind::GETTER:
+ case FunctionKind::SETTER:
+ return typeName + "." + registrationName;
}
}
template <typename THybrid>
- static inline std::string getHybridFuncDebugInfo(FunctionKind kind, const std::string& registrationName,
- THybrid* hybridInstance = nullptr) {
- auto funcName = getHybridFuncFullName<THybrid>(kind, registrationName, hybridInstance);
+ static inline std::string
+ getHybridFuncDebugInfo(FunctionKind kind, const std::string &registrationName,
+ THybrid *hybridInstance = nullptr) {
+ auto funcName =
+ getHybridFuncFullName<THybrid>(kind, registrationName, hybridInstance);
switch (kind) {
- case FunctionKind::METHOD:
- return "call hybrid function `" + funcName + "`";
- case FunctionKind::GETTER:
- return "get hybrid property `" + funcName + "`";
- case FunctionKind::SETTER:
- return "set hybrid property `" + funcName + "`";
+ case FunctionKind::METHOD:
+ return "call hybrid function `" + funcName + "`";
+ case FunctionKind::GETTER:
+ return "get hybrid property `" + funcName + "`";
+ case FunctionKind::SETTER:
+ return "set hybrid property `" + funcName + "`";
}
}
};
+48 -17
View File
@@ -1,36 +1,67 @@
ARG VERSION=unknown
# Source : https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile
FROM node:18-alpine3.18 AS base
FROM base AS builder
ARG VERSION
ENV NEXT_PUBLIC_VERSION=$VERSION
ENV VERSION=$VERSION
WORKDIR /app
COPY ./*.json ./.yarnrc.yml ./*.lock ./
# Yarn stuff
COPY ./.yarnrc.yml ./*.lock ./
COPY ./.yarn ./.yarn
RUN yarn workspaces focus
COPY ./next.config.js .
## To Provide static assets at build time
COPY ./public ./public
COPY ./src ./src
ENV NEXT_TELEMETRY_DISABLED 1
RUN yarn build
# Package.json + tsconfig
## Root
COPY package.json tsconfig.base.json ./
## Apps
COPY apps/web/*.json apps/web/
## Libs
COPY packages/*.json packages/
COPY packages/api/*.json packages/api/
COPY packages/models/*.json packages/models/
COPY packages/state/*.json packages/state/
COPY packages/ui/*.json packages/ui/
COPY packages/utils/*.json packages/utils/
FROM base as runner
# Install Libraries
RUN yarn workspaces focus -A
# NextJS
COPY ./apps/web/ apps/web/
COPY ./packages/ packages/
COPY ./translations/ translations/
COPY ./assets/* apps/web/public/
ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production
RUN yarn build:web
######
FROM base AS runner
ARG VERSION
WORKDIR /app
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1
ENV NEXT_PUBLIC_VERSION=$VERSION
ENV VERSION=$VERSION
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/standalone/apps/web ./
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/standalone/node_modules ./node_modules
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/public ./public
USER nextjs
EXPOSE 3000
ENV PORT 3000
ENV HOSTNAME "0.0.0.0"
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD node server.js
CMD ["node", "server.js"]
+14 -5
View File
@@ -1,12 +1,21 @@
FROM node:18-alpine3.18
WORKDIR /app
COPY ./*.json ./
COPY ./*.lock ./
COPY ./.yarnrc.yml ./
COPY ./.yarn /app/.yarn
ENV VERSION=dev
ENV NEXT_PUBLIC_VERSION=dev
COPY ./.yarnrc.yml ./*.lock ./
COPY ./.yarn ./.yarn
COPY package.json tsconfig.base.json ./
COPY apps/web/*.json apps/web/
COPY packages/*.json packages/
COPY packages/api/*.json packages/api/
COPY packages/models/*.json packages/models/
COPY packages/state/*.json packages/state/
COPY packages/utils/*.json packages/utils/
RUN yarn install
ENV PORT 3000
ENV HOSTNAME "0.0.0.0"
CMD yarn && yarn run start:dev
CMD yarn && cd apps/web && yarn run start:dev
-28
View File
@@ -1,28 +0,0 @@
import userReducer, {
setAccessToken,
setUserProfile,
unsetAccessToken,
} from "../../src/state/userSlice";
describe("User Slice", () => {
let state: ReturnType<typeof userReducer> | undefined = undefined;
it("Should set the access token", () => {
state = userReducer({}, setAccessToken("1234"));
expect(state).toStrictEqual({ accessToken: "1234" });
});
it("Should set the user profile", () => {
const user = { id: 1, name: "user", admin: true, enabled: true };
state = userReducer(state, setUserProfile(user));
expect(state).toStrictEqual({ accessToken: "1234", user });
});
it("Should unset the access token, and user profile", () => {
state = userReducer(state, unsetAccessToken());
expect(state.accessToken).toBeUndefined();
});
});
+42
View File
@@ -0,0 +1,42 @@
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
# dependencies
node_modules/
# Expo
.expo/
dist/
web-build/
expo-env.d.ts
# Native
.kotlin/
*.orig.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
# Metro
.metro-health-check*
# debug
npm-debug.*
yarn-debug.*
yarn-error.*
# macOS
.DS_Store
*.pem
# local env files
.env*.local
# typescript
*.tsbuildinfo
app-example
android
*.apk
ios
+85
View File
@@ -0,0 +1,85 @@
import type { ExpoConfig } from "expo/config";
const IS_DEV = process.env.APP_VARIANT === "development";
module.exports = (_: ExpoConfig): ExpoConfig => ({
name: IS_DEV ? "Meelo (Dev)" : "Meelo",
slug: "meelo",
version: "1.0.0",
icon: "./assets/icon-black.png",
scheme: "mobile",
userInterfaceStyle: "automatic",
newArchEnabled: true,
android: {
icon: "./assets/icon-black.png",
edgeToEdgeEnabled: true,
package: IS_DEV ? "dev.arthichaud.meelo.dev" : "dev.artichaud.meelo",
permissions: [
"android.permission.FOREGROUND_SERVICE",
"android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK",
],
},
ios: {
bundleIdentifier: "com.arthichaud.meelo",
infoPlist: {
UIViewControllerBasedStatusBarAppearance: true,
},
},
plugins: [
"expo-router",
[
"expo-screen-orientation",
{
initialOrientation: "DEFAULT",
},
],
[
"expo-splash-screen",
{
image: "./assets/icon-black.png",
resizeMode: "contain",
imageWidth: 200,
backgroundColor: "#ffffff",
dark: {
image: "./assets/icon-white.png",
backgroundColor: "#242120",
},
},
],
[
"expo-build-properties",
{
android: {
usesCleartextTraffic: true,
},
},
],
[
"expo-asset",
{
assets: [
"./assets/banner1_white.png",
"./assets/banner1_black.png",
],
},
],
"./plugins/withMediaServicePlugin.ts",
[
// https://docs.thewidlarzgroup.com/react-native-video/docs/v7/configuration/expo-plugin
"react-native-video",
{
enableBackgroundAudio: true,
//TODO Picture in picture
},
],
],
experiments: {
typedRoutes: true,
},
extra: {
router: {},
eas: {
projectId: "b8d8dcdb-a905-4674-9c62-db4256e0d94f",
},
},
});
+1
View File
@@ -0,0 +1 @@
../../../assets/banner1-black.png
+1
View File
@@ -0,0 +1 @@
../../../assets/banner1-white.png
+1
View File
@@ -0,0 +1 @@
../../../assets/icon-black.png
+1
View File
@@ -0,0 +1 @@
../../../assets/icon-white.png
+23
View File
@@ -0,0 +1,23 @@
module.exports = (api) => {
api.cache(true);
return {
presets: [
[
"babel-preset-expo",
{
// Needed for jotai in dev (?)
unstable_transformImportMeta: true,
},
],
],
plugins: [
[
"react-native-unistyles/plugin",
{
root: "src",
},
],
"react-native-worklets/plugin", // Always keep last
],
};
};
+31
View File
@@ -0,0 +1,31 @@
{
"cli": {
"version": ">= 10.2.2"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"env": {
"APP_VARIANT": "development"
}
},
"preview": {
"distribution": "internal",
"env": {
"APP_VARIANT": "preview"
}
},
"production": {
"android": {
"buildType": "apk"
},
"env": {
"APP_VARIANT": "production"
}
}
},
"submit": {
"production": {}
}
}
+2
View File
@@ -0,0 +1,2 @@
import "expo-router/entry";
import "./src/theme";
+43
View File
@@ -0,0 +1,43 @@
const { getDefaultConfig } = require("expo/metro-config");
const {
wrapWithReanimatedMetroConfig,
} = require("react-native-reanimated/metro-config");
/** @type {import('expo/metro-config').MetroConfig} */
const defaultConfig = getDefaultConfig(__dirname);
const path = require("node:path");
const ALIASES = {
"@wandersonalwes/iconsax-react": "iconsax-react-nativejs",
};
const projectRoot = __dirname;
function addMonorepoSupport(config) {
const workspaceRoot = path.resolve(projectRoot, "../..");
return {
...config,
watchFolders: [...config.watchFolders, workspaceRoot],
};
}
module.exports = wrapWithReanimatedMetroConfig(
addMonorepoSupport({
...defaultConfig,
resolver: {
...defaultConfig.resolver,
resolveRequest: (context, moduleName, platform) => {
// Ensure you call the default resolver.
return context.resolveRequest(
context,
ALIASES[moduleName] ?? moduleName,
platform,
);
},
requireCycleIgnorePatterns: [
...defaultConfig.resolver.requireCycleIgnorePatterns,
/.*/,
],
},
}),
);
+80
View File
@@ -0,0 +1,80 @@
{
"name": "mobile",
"main": "index.ts",
"version": "1.0.0",
"private": true,
"sideEffects": false,
"scripts": {
"start": "APP_VARIANT=development expo start",
"android": "APP_VARIANT=development expo run:android",
"android:prebuild": "APP_VARIANT=development expo prebuild -p android",
"ios": "expo run:ios"
},
"dependencies": {
"@d11/react-native-fast-image": "^8.12.0",
"@expo-google-fonts/rubik": "^0.3.0",
"@expo/config-plugins": "^54.0.2",
"@gorhom/bottom-sheet": "^5.2.6",
"@react-native-community/checkbox": "^0.5.20",
"@shopify/flash-list": "2.0.2",
"@shopify/react-native-skia": "2.2.12",
"@tanstack/react-query": "^5.77.0",
"api": "workspace:^",
"expo": "^54.0.0",
"expo-asset": "~12.0.9",
"expo-blur": "~15.0.7",
"expo-build-properties": "~1.0.9",
"expo-constants": "~18.0.9",
"expo-dev-client": "~6.0.15",
"expo-device": "~8.0.9",
"expo-font": "~14.0.9",
"expo-haptics": "~15.0.7",
"expo-linear-gradient": "^15.0.7",
"expo-linking": "~8.0.8",
"expo-router": "~6.0.12",
"expo-screen-orientation": "~9.0.7",
"expo-splash-screen": "~31.0.10",
"expo-status-bar": "~3.0.8",
"expo-system-ui": "~6.0.7",
"expo-web-browser": "~15.0.8",
"i18next": "^23.7.16",
"iconsax-react-nativejs": "^0.0.8",
"infinite-controls": "workspace:^",
"intl-pluralrules": "^2.0.1",
"jotai": "^2.12.5",
"lodash": "^4.17.21",
"models": "workspace:^",
"react": "19.1.0",
"react-hook-form": "^7.58.0",
"react-i18next": "^12.2.2",
"react-native": "0.81.4",
"react-native-awesome-slider": "^2.9.0",
"react-native-blurhash": "patch:react-native-blurhash@npm%3A2.1.1#~/.yarn/patches/react-native-blurhash-npm-2.1.1-baca87b7fa.patch",
"react-native-draggable-flatlist": "^4.0.3",
"react-native-edge-to-edge": "^1.6.1",
"react-native-gesture-handler": "~2.28.0",
"react-native-keyboard-controller": "1.18.5",
"react-native-mmkv": "4.0.0-beta.12",
"react-native-nitro-modules": "0.30.1",
"react-native-reanimated": "~4.1.1",
"react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.16.0",
"react-native-svg": "15.12.1",
"react-native-toast-message": "^2.3.3",
"react-native-unistyles": "3.0.15",
"react-native-uuid": "^2.0.3",
"react-native-video": "^7.0.0-alpha.7",
"react-native-worklets": "^0.5.1",
"state": "workspace:^",
"type-fest": "^4.33.0",
"ui": "workspace:^",
"utils": "workspace:^"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@types/lodash": "^4",
"@types/react": "~19.1.10",
"typescript": "~5.9.2"
},
"packageManager": "yarn@4.4.0"
}
@@ -0,0 +1,33 @@
import type { ConfigPlugin } from "@expo/config-plugins";
import { getMainApplicationOrThrow } from "@expo/config-plugins/build/android/Manifest";
import { withAndroidManifest } from "@expo/config-plugins/build/plugins/android-plugins";
const withMediaServicePlugin: ConfigPlugin = (oldConfig) => {
return withAndroidManifest(oldConfig, (config) => {
const mainApplication = getMainApplicationOrThrow(config.modResults);
mainApplication.service ??= [];
mainApplication.service.push({
$: {
"android:name":
"com.twg.video.core.services.playback.VideoPlaybackService",
"android:exported": "false",
"android:foregroundServiceType": "mediaPlayback",
},
"intent-filter": [
{
action: [
{
$: {
"android:name":
"androidx.media3.session.MediaSessionService",
},
},
],
},
],
});
return config;
});
};
export default withMediaServicePlugin;
@@ -0,0 +1,119 @@
import { BottomSheetFlatList, useBottomSheetModal } from "@gorhom/bottom-sheet";
import { useMutation } from "@tanstack/react-query";
import { useCallback } from "react";
import { useTranslation } from "react-i18next";
import { StyleSheet } from "react-native-unistyles";
import type { AddToPlaylistPayload } from "@/api";
import { getPlaylists } from "@/api/queries";
import { AddIcon, PlaylistIcon } from "@/ui/icons";
import { generateArray } from "@/utils/gen-list";
import { useQuery, useQueryClient } from "~/api";
import { useModal } from "~/components/bottom-modal-sheet";
import type { ContextMenuItem } from "~/components/context-menu";
import { ListItem } from "~/components/item/list-item";
import { PlaylistItem } from "~/components/item/resource/playlist";
import { Divider } from "~/primitives/divider";
import { Text } from "~/primitives/text";
import { showErrorToast, showSuccessToast } from "~/primitives/toast";
import { useCreatePlaylistFormModal } from "./playlist/create-update";
export const useAddToPlaylistAction = (
payload?: AddToPlaylistPayload,
): ContextMenuItem => {
const content = useCallback(() => {
if (!payload) {
return null;
}
return <ChoosePlaylistModal payload={payload} />;
}, [payload]);
const { openModal } = useModal({
content,
onDismiss: () => {},
});
return {
label: "actions.addToPlaylist.label",
icon: PlaylistIcon,
onPress: openModal,
nestedModal: true,
};
};
const ChoosePlaylistModal = ({
payload,
}: {
payload: AddToPlaylistPayload;
}) => {
const { t } = useTranslation();
const { dismiss } = useBottomSheetModal();
const addToPlaylist = useMutation({
mutationFn: (playlistId: number) =>
queryClient.api
.addToPlaylist(payload, playlistId)
.then(() => {
showSuccessToast({
text: t("toasts.playlist.addedToPlaylist"),
});
dismiss();
queryClient.client.invalidateQueries({
queryKey: ["playlists"],
});
})
.catch((e) =>
showErrorToast({
text: t(e.message ?? e.error ?? JSON.stringify(e)),
}),
),
});
const { openFormModal } = useCreatePlaylistFormModal(undefined, (p) =>
addToPlaylist.mutate(p.id),
);
const { data: playlists } = useQuery(() => {
const q = getPlaylists({ changeable: true }, { sortBy: "name" }, [
"illustration",
]);
return {
key: [...q.key, "full"],
exec: (api) => () => q.exec(api)({ pageSize: 100 }),
};
});
const queryClient = useQueryClient();
return (
<BottomSheetFlatList
style={styles.root}
contentContainerStyle={styles.list}
ListHeaderComponent={
<>
<Text
content={t("actions.addToPlaylist.modalTitle")}
variant="h4"
/>
<ListItem
title={t("actions.new")}
illustration={null}
illustrationProps={{ fallbackIcon: AddIcon }}
subtitle={null}
onPress={openFormModal}
/>
<Divider h />
</>
}
data={playlists?.items ?? generateArray(3)}
renderItem={({ item: playlist }) => (
<PlaylistItem
playlist={playlist}
onPress={() =>
playlist && addToPlaylist.mutate(playlist.id)
}
/>
)}
/>
);
};
const styles = StyleSheet.create((theme, rt) => ({
root: {
height: (2 * rt.screen.height) / 3,
width: "100%",
},
list: { paddingBottom: theme.gap(2) },
}));
+137
View File
@@ -0,0 +1,137 @@
import type { Href } from "expo-router";
import type { QueryClient } from "@/api/hook";
import { getReleaseTracklist } from "@/api/queries";
import { transformPage } from "@/api/query";
import {
playAfterAtom,
playFromInfiniteQuery,
playNextAtom,
playTrackAtom,
type TrackState,
} from "@/state/player";
import { store } from "@/state/store";
import {
AlbumIcon,
ArtistIcon,
EditIcon,
type Icon as IconType,
InfoIcon,
LyricsIcon,
PlayAfterIcon,
PlayIcon,
PlayNextIcon,
RelatedTracksIcon,
SongIcon,
VideoIcon,
} from "@/ui/icons";
export type Action = {
icon: IconType;
label: TranslationKey;
onPress?: () => void;
href?: Href;
disabled?: boolean;
};
export const GoToArtist = (artistId: string | number): Action => ({
label: "actions.goToArtist",
icon: ArtistIcon,
href: `/artists/${artistId}`,
});
export const GoToArtistAlbums = (artistId: string | number): Action => ({
label: "actions.artist.seeAlbums",
icon: AlbumIcon,
href: `/albums?artist=${artistId}`,
});
export const GoToArtistSongs = (artistId: string | number): Action => ({
label: "actions.artist.seeSongs",
icon: SongIcon,
href: `/songs?artist=${artistId}`,
});
export const GoToRelease = (releaseId: string | number): Action => ({
label: "actions.album.goToAlbum",
icon: AlbumIcon,
href: `/releases/${releaseId}`,
});
export const ChangeType = (label: TranslationKey, onPress: () => void) => ({
label,
onPress,
nestedModal: true,
icon: EditIcon,
});
export const Play = (track: TrackState): Action => ({
label: "actions.playback.play",
icon: PlayIcon,
onPress: () => store.set(playTrackAtom, track),
});
export const PlayNext = (track: TrackState): Action => ({
label: "actions.playback.playNext",
icon: PlayNextIcon,
onPress: () => store.set(playNextAtom, track),
});
export const PlayAfter = (track: TrackState): Action => ({
label: "actions.playback.playAfter",
icon: PlayAfterIcon,
onPress: () => store.set(playAfterAtom, track),
});
export const PlayReleaseAction = (
releaseId: string | number,
queryClient: QueryClient,
) =>
({
icon: PlayIcon,
label: "actions.playback.play",
onPress: () => {
const query = getReleaseTracklist(releaseId, false, [
"artist",
"featuring",
"illustration",
]);
store.set(
playFromInfiniteQuery,
transformPage(query, ({ song, video, ...track }) => ({
artist: (song ?? video)!.artist,
track,
featuring: song?.featuring ?? [],
id: track.id,
})),
queryClient,
);
},
}) satisfies Action;
export const GoToLyrics = (songId: string | number): Action => ({
label: "actions.song.seeLyrics",
href: `/songs/${songId}?tab=lyrics`,
icon: LyricsIcon,
});
export const GoToSongInfo = (songId: string | number): Action => ({
label: "actions.song.seeSongInfo",
href: `/songs/${songId}?tab=infos`,
icon: InfoIcon,
});
export const GoToSongVideos = (songId: string | number): Action => ({
label: "actions.song.seeRelatedVideos",
href: `/videos?song=${songId}`,
icon: VideoIcon,
});
export const GoToSongVersions = (songId: string | number): Action => ({
label: "actions.song.seeOtherVersions",
href: `/songs?versionsOf=${songId}`,
icon: SongIcon,
});
export const GoToRelatedTracks = (songId: string | number): Action => ({
label: "actions.song.seeRelatedTracks",
href: `/tracks?song=${songId}`,
icon: RelatedTracksIcon,
});
+88
View File
@@ -0,0 +1,88 @@
import { useMutation } from "@tanstack/react-query";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import type Album from "@/models/album";
import type Release from "@/models/release";
import type Song from "@/models/song";
import type Track from "@/models/track";
import { MasterIcon } from "@/ui/icons";
import { useQueryClient } from "~/api";
import { showSuccessToast } from "~/primitives/toast";
export const useSetReleaseAsMaster = (
release: Release | undefined,
album: Album | undefined,
) => {
const { t } = useTranslation();
const queryClient = useQueryClient();
const setAsMaster = useMutation({
mutationFn: () =>
queryClient.api.updateAlbum(release!.albumId, {
masterReleaseId: release?.id,
}),
onSuccess: () => {
showSuccessToast({ text: t("toasts.releaseSetAsMaster") });
queryClient.client.invalidateQueries({ queryKey: ["releases"] });
queryClient.client.invalidateQueries({ queryKey: ["albums"] });
queryClient.client.invalidateQueries({ queryKey: [release?.id] });
queryClient.client.invalidateQueries({ queryKey: [release?.slug] });
queryClient.client.invalidateQueries({
queryKey: [album?.id],
});
queryClient.client.invalidateQueries({
queryKey: [album?.slug],
});
},
});
return useMemo(
() =>
SetAsMaster(
() => setAsMaster.mutateAsync(),
!release || !album || release.id === album.masterId,
),
[setAsMaster, release],
);
};
export const useSetSongTrackAsMaster = (
track: Track | undefined,
song: Song | undefined,
) => {
const { t } = useTranslation();
const queryClient = useQueryClient();
const setAsMaster = useMutation({
mutationFn: () =>
queryClient.api.updateSong(song!.id, {
masterTrackId: track?.id,
}),
onSuccess: () => {
showSuccessToast({ text: t("toasts.trackSetAsMaster") });
queryClient.client.invalidateQueries({ queryKey: ["tracks"] });
queryClient.client.invalidateQueries({ queryKey: ["songs"] });
queryClient.client.invalidateQueries({ queryKey: ["releases"] });
queryClient.client.invalidateQueries({ queryKey: [song?.id] });
queryClient.client.invalidateQueries({ queryKey: [song?.slug] });
queryClient.client.invalidateQueries({
queryKey: [track?.id],
});
queryClient.client.invalidateQueries({
queryKey: [track?.releaseId],
});
},
});
return useMemo(
() =>
SetAsMaster(
() => setAsMaster.mutateAsync(),
!track || !song || track.id === song.masterId,
),
[setAsMaster, track],
);
};
const SetAsMaster = (onPress: () => void, disabled: boolean) => ({
onPress,
disabled,
label: "actions.setAsMaster" as const,
icon: MasterIcon,
});
@@ -0,0 +1,32 @@
import { useCallback } from "react";
import type Playlist from "@/models/playlist";
import { AddIcon } from "@/ui/icons";
import { useModal } from "~/components/bottom-modal-sheet";
import { CreateUpdatePlaylistForm } from "~/components/playlist/create-update";
import type { Action } from "..";
export const useUpdatePlaylistFormModal = (
playlist?: Playlist,
afterSave?: (playlist: Playlist) => void,
) => {
const content = useCallback(() => {
return (
<CreateUpdatePlaylistForm
existingPlaylist={playlist}
afterSave={afterSave}
/>
);
}, []);
const { openModal } = useModal({
content,
onDismiss: () => {},
});
return { openFormModal: openModal };
};
export const useCreatePlaylistFormModal = useUpdatePlaylistFormModal;
export const CreatePlaylist = (onPress: () => void): Action => ({
label: "actions.new",
icon: AddIcon,
onPress,
});
@@ -0,0 +1,50 @@
import { useMutation } from "@tanstack/react-query";
import { usePathname, useRouter } from "expo-router";
import { DeleteIcon } from "@/ui/icons";
import { useQueryClient } from "~/api";
import type { Action } from "..";
export const useDeletePlaylistAction = (
playlistId: number | undefined,
): Action => {
const queryClient = useQueryClient();
const path = usePathname();
const router = useRouter();
const deletePlaylist = useMutation({
mutationFn: async () =>
playlistId &&
queryClient.api.deletePlaylist(playlistId).then(() => {
queryClient.client.invalidateQueries({
queryKey: ["playlists"],
});
if (path.startsWith(`/playlists/`)) router.dismiss();
}),
});
return {
label: "actions.delete",
icon: DeleteIcon,
onPress: () => deletePlaylist.mutate(),
};
};
export const useDeletePlaylistEntryAction = (
entryId: number | undefined,
): Action => {
const queryClient = useQueryClient();
const deletePlaylistEntry = useMutation({
mutationFn: async () =>
entryId &&
queryClient.api.deletePlaylistEntry(entryId).then(() => {
queryClient.client.invalidateQueries({
queryKey: ["playlists"],
});
}),
});
return {
label: "actions.deleteFromPlaylist",
icon: DeleteIcon,
onPress: () => deletePlaylistEntry.mutate(),
};
};
+55
View File
@@ -0,0 +1,55 @@
import { useCallback } from "react";
import { Share } from "react-native";
import { ShareIcon } from "@/ui/icons";
import type { Action } from "~/actions";
import { useAPI } from "~/api";
export const useShareCallback = () => {
const api = useAPI();
return useCallback(
(route: string) =>
Share.share({ url: `${api.urls.api.replace("/api", "")}${route}` }),
[api],
);
};
export const ShareAction = (callback: () => void): Action => ({
label: "actions.share",
icon: ShareIcon,
onPress: callback,
});
export const useShareAlbumAction = (albumId: string | number | undefined) => {
const c = useShareCallback();
if (!albumId) {
return undefined;
}
return ShareAction(() => c(`/albums/${albumId}`));
};
export const useSharePlaylistAction = (
playlistId: string | number | undefined,
) => {
const c = useShareCallback();
if (!playlistId) {
return undefined;
}
return ShareAction(() => c(`/playlists/${playlistId}`));
};
export const useShareArtistAction = (artistId: string | number | undefined) => {
const c = useShareCallback();
if (!artistId) {
return undefined;
}
return ShareAction(() => c(`/artists/${artistId}`));
};
export const useShareSongAction = (songId: string | number | undefined) => {
const c = useShareCallback();
if (!songId) {
return undefined;
}
return ShareAction(() => c(`/songs/${songId}`));
};
@@ -0,0 +1,24 @@
import { useCallback } from "react";
import { InfoIcon } from "@/ui/icons";
import { useModal } from "~/components/bottom-modal-sheet";
import type { ContextMenuItem } from "~/components/context-menu";
import { TrackInfo } from "~/components/track-info";
export const useTrackInfoModal = (trackId: number | null | undefined) => {
const content = useCallback(() => {
if (!trackId) return null;
return <TrackInfo trackId={trackId} />;
}, [trackId]);
const { openModal } = useModal({
content,
onDismiss: () => {},
});
return { openTrackInfoModal: openModal };
};
export const SeeTrackInfo = (openModal: () => void): ContextMenuItem => ({
label: "actions.track.seeTrackInfo",
icon: InfoIcon,
nestedModal: true,
onPress: openModal,
});
Loaded 100 of 734 files, more files were not shown because too many files have changed in this diff. Show more