Skip to content
Closed
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
43 changes: 43 additions & 0 deletions .github/workflows/format-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: CI / Format Check

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]

permissions:
contents: read

jobs:
check:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '20'
cache: 'npm'

- name: Install Node dependencies
run: npm ci

- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
with:
components: rustfmt, clippy

- name: Cache Rust
uses: Swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae # v2

- name: Install system dependencies (Linux)
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libasound2-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev

- name: Run checks (ESLint, Prettier, Clippy, Rustfmt, Knip)
run: npm run check
3 changes: 2 additions & 1 deletion src-tauri/src/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ fn configure_alsa_hwparams(
}

#[cfg(target_os = "linux")]
#[allow(clippy::too_many_arguments)]
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
fn spawn_alsa_writer(
device: &str,
initial_format: &PcmFormat,
Expand Down Expand Up @@ -1557,6 +1557,7 @@ impl AudioPlayer {
// ── Appsink pipeline builder ───────────────────────────────────────────

#[cfg(target_os = "linux")]
#[allow(clippy::too_many_arguments)]
fn build_appsink_pipeline(
uri: &str,
exclusive: bool,
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/commands/library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,7 @@ pub async fn get_favorite_artists(
}

#[tauri::command(rename_all = "camelCase")]
#[allow(clippy::too_many_arguments)]
pub async fn get_playlist_folders(
state: State<'_, AppState>,
app_handle: tauri::AppHandle,
Expand Down
4 changes: 4 additions & 0 deletions src-tauri/src/discord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ impl DiscordHandle {
DiscordCommand::Connect => {
want_connected = true;
try_connect(&mut client, &mut connected);
#[allow(clippy::collapsible_if)]
if connected && is_playing && !current_title.is_empty() {
if set_activity(
&mut client,
Expand Down Expand Up @@ -127,6 +128,7 @@ impl DiscordHandle {

if want_connected {
try_connect(&mut client, &mut connected);
#[allow(clippy::collapsible_if)]
if connected {
if set_activity(
&mut client,
Expand Down Expand Up @@ -188,6 +190,7 @@ impl DiscordHandle {
DiscordCommand::Seeked { position_secs } => {
if is_playing {
play_start_epoch = now_epoch_secs() - position_secs as i64;
#[allow(clippy::collapsible_if)]
if want_connected && connected {
if set_activity(
&mut client,
Expand Down Expand Up @@ -234,6 +237,7 @@ fn now_epoch_secs() -> i64 {
.as_secs() as i64
}

#[allow(clippy::too_many_arguments)]
fn set_activity(
client: &mut DiscordIpcClient,
title: &str,
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ pub enum ProxyType {
Socks5,
}

#[allow(clippy::derivable_impls)]
impl Default for ProxyType {
fn default() -> Self {
Self::Http
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/tidal_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2494,6 +2494,7 @@ impl TidalClient {

/// Fetch playlist folders from v2/my-collection/playlists/folders.
/// Returns raw JSON so we can capture the full response shape.
#[allow(clippy::too_many_arguments)]
pub async fn get_playlist_folders(
&mut self,
folder_id: &str,
Expand Down
3 changes: 1 addition & 2 deletions src-tauri/src/tray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,7 @@ pub fn setup(app: &tauri::App) {
// In Flatpak/Snap sandbox, ksni can't own a well-known D-Bus name
// (would need --own-name with a dynamic PID-based name).
// disable_dbus_name makes it register via the unique connection name instead.
let is_sandboxed =
std::env::var("FLATPAK_ID").is_ok() || std::env::var("SNAP").is_ok();
let is_sandboxed = std::env::var("FLATPAK_ID").is_ok() || std::env::var("SNAP").is_ok();
match tray.disable_dbus_name(is_sandboxed).spawn().await {
Ok(handle) => {
app_handle.manage(TrayHandle(handle));
Expand Down
42 changes: 35 additions & 7 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,41 @@ textarea {
--cols: 2;
}

@container (min-width: 5.88em) { .card-scroll-item { --cols: 2; } }
@container (min-width: 8.02em) { .card-scroll-item { --cols: 3; } }
@container (min-width: 10.16em) { .card-scroll-item { --cols: 4; } }
@container (min-width: 12.30em) { .card-scroll-item { --cols: 5; } }
@container (min-width: 14.44em) { .card-scroll-item { --cols: 6; } }
@container (min-width: 16.58em) { .card-scroll-item { --cols: 7; } }
@container (min-width: 18.24em) { .card-scroll-item { --cols: 8; } }
@container (min-width: 5.88em) {
.card-scroll-item {
--cols: 2;
}
}
@container (min-width: 8.02em) {
.card-scroll-item {
--cols: 3;
}
}
@container (min-width: 10.16em) {
.card-scroll-item {
--cols: 4;
}
}
@container (min-width: 12.30em) {
.card-scroll-item {
--cols: 5;
}
}
@container (min-width: 14.44em) {
.card-scroll-item {
--cols: 6;
}
}
@container (min-width: 16.58em) {
.card-scroll-item {
--cols: 7;
}
}
@container (min-width: 18.24em) {
.card-scroll-item {
--cols: 8;
}
}

/* Toast slide-in */
@keyframes toastIn {
Expand Down
85 changes: 44 additions & 41 deletions src/api/tidal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,35 +124,37 @@
entry.accessOrder = ++accessCounter;
return Promise.resolve(entry.data as T);
}
return fetcher().catch((err) => {
checkNetworkError(err);
throw err;
}).then((data) => {
// Remove stale entry if present
if (store.has(hk)) removeEntry(hk);
const size = estimateSize(data);
evictIfNeeded(size);
const newEntry: CacheEntry = {
data,
ts: Date.now(),
ttl,
tags,
accessOrder: ++accessCounter,
estimatedSize: size,
};
store.set(hk, newEntry);
keyMap.set(hk, key);
currentBytes += size;
for (const tag of tags) {
let set = tagIndex.get(tag);
if (!set) {
set = new Set();
tagIndex.set(tag, set);
return fetcher()
.catch((err) => {
checkNetworkError(err);
throw err;
})
.then((data) => {
// Remove stale entry if present
if (store.has(hk)) removeEntry(hk);
const size = estimateSize(data);
evictIfNeeded(size);
const newEntry: CacheEntry = {
data,
ts: Date.now(),
ttl,
tags,
accessOrder: ++accessCounter,
estimatedSize: size,
};
store.set(hk, newEntry);
keyMap.set(hk, key);
currentBytes += size;
for (const tag of tags) {
let set = tagIndex.get(tag);
if (!set) {
set = new Set();
tagIndex.set(tag, set);
}
set.add(hk);
}
set.add(hk);
}
return data;
});
return data;
});
}

/** Remove all cache entries matching a tag (fast path) or key prefix (fallback). */
Expand Down Expand Up @@ -304,7 +306,7 @@
async () => {
try {
return await invoke<SearchResults>("search_tidal", { query, limit });
} catch (error: any) {

Check warning on line 309 in src/api/tidal.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type
console.error("Failed to search:", error);
throw error;
}
Expand Down Expand Up @@ -376,7 +378,7 @@
async () => {
try {
return await invoke<AlbumDetail>("get_album_detail", { albumId });
} catch (error: any) {

Check warning on line 381 in src/api/tidal.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type
console.error("Failed to get album detail:", error);
throw error;
}
Expand Down Expand Up @@ -410,7 +412,7 @@
offset,
limit,
});
} catch (error: any) {

Check warning on line 415 in src/api/tidal.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type
console.error("Failed to get album tracks:", error);
throw error;
}
Expand Down Expand Up @@ -471,7 +473,7 @@
`artist-page:${artistId}`,
["artist"],
async () => {
const raw = await invoke<any>("get_artist_page", { artistId });

Check warning on line 476 in src/api/tidal.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type
return parseArtistPageResponse(raw);
},
TTL.MEDIUM,
Expand All @@ -479,7 +481,7 @@
}

/** Parse raw artist page response — detects v2 (json.item) vs v1 (json.rows) */
function parseArtistPageResponse(json: any): ArtistPageData {

Check warning on line 484 in src/api/tidal.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type
if (json?.item) return parseArtistPageV2(json);
return parseArtistPageV1(json);
}
Expand All @@ -492,7 +494,7 @@
MIX: "MIX_LIST",
};

function parseArtistPageV2(json: any): ArtistPageData {

Check warning on line 497 in src/api/tidal.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type
const result: ArtistPageData = {
artistName: json.item?.data?.name || "",
picture: json.item?.data?.picture,
Expand All @@ -506,14 +508,14 @@
if (!Array.isArray(modules)) return result;

for (const mod of modules) {
const rawItems: any[] = mod.items || [];

Check warning on line 511 in src/api/tidal.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type
if (rawItems.length === 0) continue;

const firstType = rawItems[0]?.type as string;
if (firstType === "VIDEO" || firstType === "TRACK_CREDITS") continue;

const sectionType = V2_TYPE_TO_SECTION[firstType] || firstType;
const items = rawItems.map((i: any) => i.data || i);

Check warning on line 518 in src/api/tidal.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type

if (sectionType === "TRACK_LIST" && result.topTracks.length === 0) {
result.topTracks = items;
Expand All @@ -530,7 +532,7 @@
return result;
}

function parseArtistPageV1(json: any): ArtistPageData {

Check warning on line 535 in src/api/tidal.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type
const result: ArtistPageData = {
artistName: "",
topTracks: [],
Expand Down Expand Up @@ -595,7 +597,7 @@
export async function getArtistViewAll(
artistId: number,
viewAllPath: string,
): Promise<any[]> {

Check warning on line 600 in src/api/tidal.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type
return cached(
`artist-view-all:${artistId}:${viewAllPath}`,
["artist"],
Expand Down Expand Up @@ -724,9 +726,7 @@
tracks: Track[];
}

export async function getMixItems(
mixId: string,
): Promise<MixPageResult> {
export async function getMixItems(mixId: string): Promise<MixPageResult> {
return cached(
`mix-page:${mixId}`,
["mix-page"],
Expand Down Expand Up @@ -795,7 +795,9 @@
);
}

export async function getPlaylistDetails(playlistId: string): Promise<Playlist> {
export async function getPlaylistDetails(
playlistId: string,
): Promise<Playlist> {
return invoke<Playlist>("get_playlist_details", { playlistId });
}

Expand Down Expand Up @@ -927,9 +929,10 @@
parent: item.parent,
addedAt: item.addedAt,
lastModifiedAt: item.lastModifiedAt,
totalNumberOfItems: typeof folderData.totalNumberOfItems === "number"
? folderData.totalNumberOfItems
: undefined,
totalNumberOfItems:
typeof folderData.totalNumberOfItems === "number"
? folderData.totalNumberOfItems
: undefined,
},
};
}
Expand All @@ -953,9 +956,11 @@
};
}

export function normalizePlaylistFolders(
response: PlaylistFoldersResponse,
): { items: PlaylistOrFolder[]; totalNumberOfItems: number; cursor: string | null } {
export function normalizePlaylistFolders(response: PlaylistFoldersResponse): {
items: PlaylistOrFolder[];
totalNumberOfItems: number;
cursor: string | null;
} {
return {
items: response.items.map(normalizeFolderItem),
totalNumberOfItems: response.totalNumberOfItems,
Expand All @@ -982,9 +987,7 @@
return invoke("rename_playlist_folder", { folderTrn, name });
}

export async function deletePlaylistFolder(
folderTrn: string,
): Promise<void> {
export async function deletePlaylistFolder(folderTrn: string): Promise<void> {
return invoke("delete_playlist_folder", { folderTrn });
}

Expand Down
5 changes: 4 additions & 1 deletion src/atoms/favorites.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ const makeSortAtom = (key: string, defaultOrder: string = "DATE") =>
export const albumSortAtom = makeSortAtom("sone.albumSort.v1");
export const artistSortAtom = makeSortAtom("sone.artistSort.v1");
export const mixSortAtom = makeSortAtom("sone.mixSort.v1");
export const playlistSortAtom = makeSortAtom("sone.playlistSort.v1", "DATE_UPDATED");
export const playlistSortAtom = makeSortAtom(
"sone.playlistSort.v1",
"DATE_UPDATED",
);
4 changes: 3 additions & 1 deletion src/atoms/playlists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ export const movedPlaylistsAtom = atom<Map<string, string>>(new Map());
/** Optimistic count adjustments per folder: folderId -> delta (+1 or -1) */
export const folderCountAdjustmentsAtom = atom<Map<string, number>>(new Map());
/** Optimistic playlist additions per folder: folderId -> PlaylistOrFolder[] */
export const addedToFolderAtom = atom<Map<string, PlaylistOrFolder[]>>(new Map());
export const addedToFolderAtom = atom<Map<string, PlaylistOrFolder[]>>(
new Map(),
);
/** Optimistic folder renames: folderId -> newName */
export const renamedFoldersAtom = atom<Map<string, string>>(new Map());
/** Shared cache of all folders for MoveToFolderMenu */
Expand Down
11 changes: 7 additions & 4 deletions src/components/AddToPlaylistMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -508,10 +508,13 @@ export default function AddToPlaylistMenu({
setAddedToFolder((prev) => {
const next = new Map(prev);
const list = next.get("root") ?? [];
next.set("root", [...list, {
kind: "playlist" as const,
data: { ...playlist, numberOfTracks: trackIds.length },
}]);
next.set("root", [
...list,
{
kind: "playlist" as const,
data: { ...playlist, numberOfTracks: trackIds.length },
},
]);
return next;
});
setShowCreateModal(false);
Expand Down
Loading
Loading