stardance has been extended another month! the new deadline is october 31 :)

You are browsing as a guest. Sign up (or log in) to start making projects!

techs-sus

@techs-sus

Joined June 1st, 2026

  • 44Devlogs
  • 1Projects
  • 1Ships
  • 20Votes
Open comments for this post

36m logged

I fixed the binary in Nix, it works now, also updated the asciinema cast

it was missing some reachability metadata and required LD_LIBRARY_PATH to be set to the jdk’s libraries.

0
0
21
Open comments for this post

43m 8s logged

Tiny change, I switched from scrimage to ImageIO. It works much better and should be less error prone as scrimage would vendor in webp binaries, which is not cross platform.

0
0
15
Open comments for this post

2h 42m 39s logged

Squashed more bugs, updated NativeMetadataMain.kt so that reachability metadata includes image i/o. This makes the Graal native image actually work when having to convert from webp to png. This will be a non issue when I can find an image library that is written in pure Kotlin.

The last shipwright to review my project tried syncing a mix, which is infinite. So I added an error for that and made sure no one will run into unexplainable errors again.

0
0
16
Open comments for this post

1h 7m 36s logged

I added GraalVM native image support. The actual implementation is boring but this makes the program’s cold starts faster and makes it feel more native. Not sure if this helps with battery life, but it might. This definitely helps with memory usage, the program only uses ~100mb syncing my 400 track playlist.

I restricted the native image to Nix because the releases are supposed to always work on all platforms.

0
0
27
Open comments for this post

2h 30m 15s logged

I redid the cli structure to group all the playlist commands under one common playlist parent command. I also implemented a clean up command to help people save disk space because SSDs are expensive right now.

For example:
music-syncer-kotlin -p path init -> music-syncer-kotlin playlist -p path init

This was a necessary step so I could implement the clean up command, which deletes audio and thumbnail files that are not used by any playlists in a container. A container is the folder holding all the databases.

Here’s everything I did, stolen from my commit message:

feature: add clean-container command
- refactor: add hasTrackInDatabase to the Playlist class
- refactor: move knownFinal{Audio,Thumbnail}Extension out of class and
	make it const
- refactor: nest all playlist commands as one subcommand
- refactor: make cli help text more helpful
- refactor: always print something to the console to let the user know
	that everything went well
- refactor: do not use PlaylistHolder, use
	currentContext.registerJvmCloseable to clean up playlist
- fix: when thumbnail is newly created, retag should silently fail
0
0
15
Open comments for this post

2h 46m 3s logged

I watched a Kotlin talk about Flows and I was inspired to rewrite my program using them.

This made my program more efficient, idiomatic and also helped me fix the annoying progress bar refresh bug (multiple old bars shown). It also let me actually properly sort by position across all 4 status types (added, removed, moved, errored).

I also made the downloading functions use one underlying common function to clean up the codebase. I also removed sqlx4k because it wasn’t actually parallel and was causing issues with init.

Here, in the screenshot I am showing the new elegant code for the logging of track statuses. It used to be many individual async calls, but flows allow me to group them up and sort them.

0
0
18
Open comments for this post

33m 3s logged

I made position diffing aware of insertions and deletions, so that output is less bloated with useless move info such as 0->1, 1->2, etc.

I also did some optimization by using database transactions wherever possible. I can’t do it in the main sync body because it is bounded to its own coroutine scope.

0
0
11
Open comments for this post

21m 35s logged

I made it so that thumbnails should now be even higher quality, it does take more storage but the difference is quite noticeable on some music players.

Also small hotfix, if you accidentally pass in another unrelated SQLite database it will now not run migrations on it before erroring.

0
0
13
Open comments for this post

38m 55s logged

To help users migrate from the old Rust implementation, I made it so that a sync will re-tag the track if the track’s thumbnail does not exist on disk.

The Kotlin implementation always gets higher resolution images, so this allows for much higher resolution track covers.

0
0
34
Open comments for this post

1h 19m 21s logged

In my attempt to make the streamExtractor lazily loaded, I caused a deadlock. I have fixed the deadlock and this fixed version is at v0.2.7.

The fix was pretty simple, it was to not use CoroutineStart.Lazy because a lazy coroutine may never run, meaning the parent coroutineScope will hang forever.

I also switched the backend driver to sqlx4k as it provides a connection pool but more importantly makes the suspend code flow cleaner.

I wont switch to Exposed (the official SQL driver) because it doesn’t have the same parallel capabilities as sql4k.

0
0
27
Open comments for this post

1h 45m 35s logged

I forgot to add error handling from the original Rust project, so I did that.

I also fixed the parallelism by using a Semaphore as limitedParallelism still wakes all the coroutines. Using a Semaphore only allows N permits to be handed out at once. This means that only N tasks would be running at once, given that each task takes one permit. I set N to 8 as that should not cause any throttling but still allow multiple tracks to be downloaded at once.

I also implemented better lazy fetching than the old Rust implementation. Now only pieces that must be fetched are fetched, and if they don’t need to be fetched, a StreamExtractor is never fetched. This causes a huge speed up, allowing my ~400 track playlist to be incrementally synced in 8 seconds.

Other notable quality of life features (taken from the git log):

- refactor: make output use terminal.theme.*
- refactor: make output more human readable by using title instead of youtube_playlist_id
- fix: reorder items in subtasks so that the text doesn't move everything else
0
0
13
Open comments for this post

46m logged

Shortly after I shipped, I found a better library for progress bars and displaying to the terminal in general.

I also made sure that the exists locally log line would be printed out in order.

I decided to use it, and I am really happy with the result!!!!

0
0
43
Ship

I made a Kotlin CLI program that synchronizes a YouTube Music playlist to a SQLite database locally.

I made this because I want to be able to play my music freely even when YouTube Music or my network is down.

The main challenge was finding a library that would let me get the data I needed (artist, thumbnail, audio tracks, etc.) from YouTube Music, and I even had to rewrite the entire program in Kotlin from Rust because the library I was using couldn’t download audio properly.

  • 27 devlogs
  • 45h
  • 18.46x multiplier
  • 833 Stardust
Try project → See source code →
Open comments for this post

1h 13m 29s logged

Preparing to ship the project, so here’s a list of everything I did:

  • added back an edited readme
  • added back the apache 2.0 license
  • fixed the github actions auto release
  • recorded an asciinema cast and put it in the readme
  • made the cli say music-player-kotlin instead of cli
  • …other small changes to make sure its ready for release
0
0
19
Open comments for this post

25m 43s logged

I fixed position tracking, so that moved tracks will output a line telling you they have moved.

I attempted to fix a deadlock that was happening, and fixed some database queries targetting the wrong table.

0
0
7
Loading more…

Followers

Loading…