Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 11 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ https://github.com/user-attachments/assets/5a97453a-db8a-4689-b2d0-ab1f2009fcb0

## Installation

### [AUR](https://aur.archlinux.org/packages/zpotify) [deprecated]
### [AUR](https://aur.archlinux.org/packages/zpotify)

Using your favorite AUR helper or manually:

Expand All @@ -17,13 +17,12 @@ makepkg -si

### Building

Requires zig 0.15.1.
Requires glib-2.0, libjpeg and chafa for image support.
Requires `zig 0.15.2`.

```
git clone https://github.com/ratakor/zpotify
cd zpotify
zig build --release=fast -Dimage-support=true
zig build --release=fast
```

Shell completions are auto-generated with `zpotify completion <shell>`!
Expand All @@ -36,8 +35,8 @@ Requires Spotify premium.
Usage: zpotify [--]<command> [options]

Commands:
get Get data from Spotify
print Display current track info in a specific format
search Search a track, playlist, album, or artist with a TUI
play Play a track, playlist, album, or artist from your library
pause Toggle pause state
prev Skip to previous track
Expand Down Expand Up @@ -97,24 +96,18 @@ Benchmark 2 (43 runs): ./run zpotify
- [baton](https://github.com/joshuathompson/baton)
- [zig-spoon](https://git.sr.ht/~leon_plickat/zig-spoon)
- [chafa](https://github.com/hpjansson/chafa)
- [spotify-player](https://github.com/aome510/spotify-player)

## TODO

- fix `play {album,track}` & `search track` & `queue`
- probably an issue in std.compress.flate.Decompress although it may be fixed with zig master
- rework `play` play command
- maybe add a command (`list`) to display all user items and a command to
play a certain ID so the user can glue it however he wants instead of being
forced to use a dmenu-like program
- also merge `queue` command with that new command(?)
- bash completion
- fix `FIXME`s in zig-spoon (it's about std.Io.GenericWriter)
- or switch to [libvaxis](https://github.com/rockorager/libvaxis) which also allows to change license
- add a tui like `search` for `queue` to edit the queue
- add `zpotify status` which is basically `cmd.print.format(stdout, default_format)`
- add `zpotify play liked` cmd which play liked songs
- how is it different from `zpotify play track`?
- add a way to save track / album to a playlist
- embed [librespot](https://github.com/librespot-org/librespot) or add a wrapper like [librespot-cfg](https://gist.github.com/Ratakor/7dab4b17311a5c60d3b36ad34a02388a)
- need to create bindings
- use [build.crab](https://github.com/akarpovskii/build.crab) for build integration
- librespot alternative?: [cspot](https://github.com/feelfreelinux/cspot)
- `save` command which takes a tack ID and a playlist ID (or names or whatever)
- add man page with [zzdoc](https://github.com/rockorager/zzdoc)
- convert these TODOs into github issues
- redo the perf analysis
- add test coverage
35 changes: 3 additions & 32 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ pub fn build(b: *std.Build) void {
const pie = b.option(bool, "pie", "Build a Position Independent Executable");
const strip = b.option(bool, "strip", "Strip executable");
const use_llvm = b.option(bool, "use-llvm", "Use Zig's llvm code backend");
const image_support = b.option(bool, "image-support", "Build with image support (requires chafa and libjpeg)") orelse false;

const resolved_version = getVersion(b);

Expand All @@ -34,12 +33,10 @@ pub fn build(b: *std.Build) void {
options.addOption([]const u8, "program_name", program_name);
options.addOption(std.SemanticVersion, "version", resolved_version);
options.addOption([]const u8, "version_string", b.fmt("{f}", .{resolved_version}));
options.addOption(bool, "image_support", image_support);
break :blk options.createModule();
};

// zig build release
// -Dimage-support=false -Dstrip=true --release=fast
var release_artifacts: [release_targets.len]*std.Build.Step.Compile = undefined;
for (release_targets, &release_artifacts) |target_query, *artifact| {
const release_target = b.resolveTargetQuery(target_query);
Expand All @@ -48,22 +45,17 @@ pub fn build(b: *std.Build) void {
.target = target,
.optimize = optimize,
}).module("axe");
const spoon_module = b.dependency("spoon", .{
.target = target,
.optimize = optimize,
}).module("spoon");

const exe_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = release_target,
.optimize = optimize,
.optimize = .ReleaseFast,
.single_threaded = single_threaded,
.pic = pie,
.strip = strip,
.strip = true,
.imports = &.{
.{ .name = "build_options", .module = build_options },
.{ .name = "axe", .module = axe_module },
.{ .name = "spoon", .module = spoon_module },
},
});

Expand All @@ -73,23 +65,13 @@ pub fn build(b: *std.Build) void {
.use_llvm = use_llvm,
.use_lld = use_llvm,
});
if (image_support) {
artifact.*.linkLibC();
artifact.*.linkSystemLibrary("glib-2.0");
artifact.*.linkSystemLibrary("chafa");
artifact.*.linkSystemLibrary("libjpeg");
}
}
release(b, &release_artifacts, resolved_version);

const axe_module = b.dependency("axe", .{
.target = target,
.optimize = optimize,
}).module("axe");
const spoon_module = b.dependency("spoon", .{
.target = target,
.optimize = optimize,
}).module("spoon");

const exe_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
Expand All @@ -101,7 +83,6 @@ pub fn build(b: *std.Build) void {
.imports = &.{
.{ .name = "build_options", .module = build_options },
.{ .name = "axe", .module = axe_module },
.{ .name = "spoon", .module = spoon_module },
},
});

Expand All @@ -112,12 +93,6 @@ pub fn build(b: *std.Build) void {
.use_llvm = use_llvm,
.use_lld = use_llvm,
});
if (image_support) {
exe.linkLibC();
exe.linkSystemLibrary("glib-2.0");
exe.linkSystemLibrary("chafa");
exe.linkSystemLibrary("libjpeg");
}
b.installArtifact(exe);

// zib build run
Expand All @@ -137,11 +112,7 @@ pub fn build(b: *std.Build) void {

// zig build fmt
const fmt_step = b.step("fmt", "Format all source files");
fmt_step.dependOn(&b.addFmt(.{ .paths = &.{
"build.zig",
"src",
"vendor/zig-spoon",
} }).step);
fmt_step.dependOn(&b.addFmt(.{ .paths = &.{ "build.zig", "src" } }).step);
}

/// Returns `MAJOR.MINOR.PATCH-dev` when `git describe` failed.
Expand Down
5 changes: 1 addition & 4 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@
.name = .zpotify,
.version = "0.4.0-dev",
.fingerprint = 0x7c117f9b8cb9acba,
.minimum_zig_version = "0.15.1",
.minimum_zig_version = "0.15.2",
.dependencies = .{
.spoon = .{
.path = "vendor/zig-spoon",
},
.axe = .{
.url = "git+https://github.com/Ratakor/axe#4df48b5fd1bd8496dc3a9ed49790649b7c4e8fee",
.hash = "axe-0.7.0-M2JU5cyPAADJQcWDZE7xNmnuQtAgwmPLyCMjiLhOptrC",
Expand Down
6 changes: 3 additions & 3 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 0 additions & 13 deletions nix/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@
stdenv,
callPackage,
installShellFiles,
pkg-config,
zig,
glib,
chafa,
libjpeg,
image-support ? true,
}:
let
fs = lib.fileset;
Expand All @@ -35,21 +30,13 @@ stdenv.mkDerivation (finalAttrs: {
"${finalAttrs.deps}"
# "--release=fast"
"-Dversion-string=${finalAttrs.version}"
"-Dimage-support=${lib.boolToString image-support}"
];

nativeBuildInputs = [
installShellFiles
pkg-config
zig.hook
];

buildInputs = lib.optionals image-support [
glib # chafa dependency
chafa
libjpeg
];

postInstall = ''
installShellCompletion --cmd zpotify \
--bash <($out/bin/zpotify completion bash) \
Expand Down
14 changes: 0 additions & 14 deletions nix/shell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@
gnutar,
xz,
p7zip,
pkg-config,
glib,
chafa,
libjpeg,
}:
mkShellNoCC {
packages = [
Expand All @@ -22,14 +18,4 @@ mkShellNoCC {
xz
p7zip
];

nativeBuildInputs = [
pkg-config
];

buildInputs = [
glib
chafa
libjpeg
];
}
18 changes: 13 additions & 5 deletions src/Client.zig
Original file line number Diff line number Diff line change
Expand Up @@ -176,19 +176,27 @@ pub fn sendRequestOwned(
);

// usually compressed with gzip
var decompress_buffer: [std.compress.flate.max_window_len]u8 = undefined;
// so this needs to be at least:
// - 8 * std.compress.flate.max_window_len for `play track`
// - 32 * std.compress.flate.max_window_len for `play album`
var decompress_buffer: [32 * std.compress.flate.max_window_len]u8 = undefined;
var transfer_buffer: [64]u8 = undefined;
var decompress: std.http.Decompress = undefined;
const reader = response.readerDecompressing(&transfer_buffer, &decompress, &decompress_buffer);
// const reader = response.reader(&transfer_buffer);

// debug raw response
if (false) {
var stdout = std.fs.File.stdout().writer(&.{});
const response_writer = &stdout.interface;
_ = reader.streamRemaining(response_writer) catch |err| switch (err) {
var stdout_writer = std.fs.File.stdout().writer(&.{});
const stdout = &stdout_writer.interface;
const written = reader.streamRemaining(stdout) catch |err| switch (err) {
error.ReadFailed => return response.bodyErr().?,
else => |e| return e,
};
std.log.debug("written: {}", .{written});
if (written > 0) {
std.process.exit(0);
}
}

var json_reader: std.json.Reader = .init(fba.allocator(), reader);
Expand All @@ -199,7 +207,7 @@ pub fn sendRequestOwned(
if (T != void) {
return std.json.parseFromTokenSourceLeaky(T, arena_allocator, &json_reader, .{
.allocate = .alloc_always,
.ignore_unknown_fields = true,
.ignore_unknown_fields = true, // TODO: set to false: all fields should be mapped into struct
});
}
},
Expand Down
2 changes: 1 addition & 1 deletion src/cmd.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
// - src/cmd/completion.zig (if needing specific completion)
// - README.md (Usage)

pub const get = @import("cmd/get.zig");
pub const print = @import("cmd/print.zig");
pub const search = @import("cmd/search.zig");
pub const play = @import("cmd/play.zig");
pub const pause = @import("cmd/pause.zig");
pub const prev = @import("cmd/prev.zig");
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/completion.zig
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ const zsh_completion = blk: {
\\ format_options=($format_options)
\\ _arguments -s "*:format:(($format_options))"
\\ ;;
\\ play|search)
\\ play)
\\ _arguments -s "2:query_type:(track playlist album artist)"
\\ ;;
\\ repeat)
Expand Down
Loading