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
41 changes: 41 additions & 0 deletions .github/workflows/registry.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: "Build Store Registry"

on:
push:
branches: [master]
paths:
- "store/stores.json"
schedule:
# Daily, stars and download counts go stale otherwise
- cron: "17 4 * * *"
workflow_dispatch:

permissions:
contents: write

concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false

jobs:
Generate:
name: Generate registry.json
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Install Node.js 📥
uses: actions/setup-node@v4
with:
node-version: latest

- name: Generate registry 🛠️
run: node store/generate.mjs
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "store: refresh registry metrics"
file_pattern: store/registry.json
33 changes: 33 additions & 0 deletions .github/workflows/validateStores.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: "Validate Stores"

on:
pull_request:
paths:
- "store/**"
push:
branches: [master, dev]
paths:
- "store/**"
# registry.json is generated, validating it on every bot commit is noise
- "!store/registry.json"
workflow_dispatch:

# Read only on purpose, this runs on pull requests from forks
permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
Validate:
name: Validate
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Validate store registry 🔍
run: ./store/validate.sh

2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ environment.systemPackages = with pkgs; [
Proper developer documentation etc is planned after the inital beta release of Luna.
If you are a developer or want to try making your own plugin, please hop in discord and ask we are more than happy to assist with getting started.

Got a plugin store to share? See [store/README.md](./store/README.md), it is a one entry pull request and no client release.

### Client Dev

To develop for the luna client follow these steps:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "luna",
"version": "1.16.6-beta",
"version": "1.17.1-beta",
"description": "A client mod for the Tidal music app for plugins",
"author": {
"name": "Inrixia",
Expand Down
8 changes: 5 additions & 3 deletions plugins/lib/src/classes/MediaItem/MediaItem.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { asyncDebounce, memoize, memoizeArgless, registerEmitter, type AddReceiver, type Emit } from "@inrixia/helpers";

import { listenerError } from "../../helpers/listenerError";
import type { IRecording, ITrack } from "musicbrainz-api";

import { ftch, ReactiveStore, type LunaUnload, type LunaUnloads, type Tracer } from "@luna/core";
Expand Down Expand Up @@ -117,7 +119,7 @@ export class MediaItem extends ContentBase {
if (item?.productId === undefined) return MediaItem.trace.warn("player/PRELOAD_ITEM intercepted without productId!", item);
const mediaItem = await this.fromId(item.productId, item.productType);
if (mediaItem === undefined) return;
emit(mediaItem, mediaItem.trace.err.withContext("preloadItem.runListeners"));
emit(mediaItem, listenerError(mediaItem.trace, "preloadItem.runListeners"));
}),
);
/** Triggered on "playbackControls/MEDIA_PRODUCT_TRANSITION"*/
Expand All @@ -134,7 +136,7 @@ export class MediaItem extends ContentBase {
this._supportsSpatialAudio = playbackContext.actualAudioMode !== "STEREO";
}

await emit(mediaItem, mediaItem.trace.err.withContext("mediaProductTransition.runListeners"));
await emit(mediaItem, listenerError(mediaItem.trace, "mediaProductTransition.runListeners"));
}),
),
);
Expand All @@ -150,7 +152,7 @@ export class MediaItem extends ContentBase {
async ({ mediaProduct: { productId, productType } }: redux.InterceptPayload<"playbackControls/PREFILL_MEDIA_PRODUCT_TRANSITION">) => {
const mediaItem = await this.fromId(productId, productType);
if (mediaItem === undefined) return;
await emit(mediaItem, mediaItem.trace.err.withContext("prefillMPT.runListeners"));
await emit(mediaItem, listenerError(mediaItem.trace, "prefillMPT.runListeners"));
},
),
),
Expand Down
1 change: 1 addition & 0 deletions plugins/lib/src/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from "./downloadObject";
export * from "./getCredentials";
export * from "./getPlaybackInfo";
export * from "./getPlaybackInfo.dasha.native";
export * from "./listenerError";
export * from "./observable";
export * from "./parseDate";
export * from "./safeTimeout";
22 changes: 22 additions & 0 deletions plugins/lib/src/helpers/listenerError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { LunaPlugin, type Tracer } from "@luna/core";

/**
* Build an onError handler for an event emitter.
*
* registerEmitter hands the same onError to every listener and never says which one threw, so a
* plugin's broken listener was recorded against whichever module owns the emitter. The stack still
* names the bundle that threw, so attribute by that and fall back to the emitter when it does not.
*/
export const listenerError =
(trace: Tracer, context: string) =>
(err: unknown): void => {
const message = (<Error>err)?.message ?? String(err);
const culprit = LunaPlugin.fromStack((<Error>err)?.stack);
if (culprit !== undefined) {
// A runtime fault in someone's listener, not a failure to load
culprit.reportRuntimeError(`${context}: ${message}`);
console.error(`[${culprit.name}] ${context}:`, err);
return;
}
trace.err.withContext(context)(err);
};
85 changes: 0 additions & 85 deletions plugins/ui/src/SettingsPage/PluginStoreTab/InstallFromUrl.tsx

This file was deleted.

Loading
Loading