Merge branch 'dev'

This commit is contained in:
thororen1234 2025-09-16 11:54:57 -04:00
commit 91131e46c5
No known key found for this signature in database
5 changed files with 92 additions and 17 deletions

View file

@ -162,6 +162,7 @@ export const globPlugins = kind => ({
const fileName = file.name;
if (fileName.startsWith("_") || fileName.startsWith(".")) continue;
if (fileName === "index.ts") continue;
if (/\.(zip|rar|7z|tar|gz|bz2)/.test(fileName)) continue;
const target = getPluginTarget(fileName);

View file

@ -135,7 +135,7 @@ function CloudTab() {
<Forms.FormText variant="text-md/normal" className={Margins.bottom20}>
Equicord comes with a cloud integration allowing settings to be synced across apps and devices.
<br />
We use Vencord's <Link href="https://github.com/Vencord/Backend">cloud source code</Link> to provide our cloud instance which has a 32MB backup cap.
We use Vencord's <Link href="https://github.com/Vencord/Backend">cloud source code</Link> to provide our cloud instance which has a 60MB backup cap.
<br />
Our <Link href="https://vencord.dev/cloud/privacy">privacy policy</Link> allows you to see what information we store, how we use it, and data retention.
<br />

View file

@ -19,6 +19,11 @@ interface ActivityAssets {
small_text?: string;
}
interface ActivityButton {
label: string;
url: string;
}
interface Activity {
state: string;
details?: string;
@ -26,6 +31,7 @@ interface Activity {
start?: number;
};
assets?: ActivityAssets;
buttons?: Array<string>;
name: string;
application_id: string;
metadata?: {
@ -50,8 +56,6 @@ interface MediaData {
position?: number;
}
const settings = definePluginSettings({
serverUrl: {
description: "Jellyfin server URL (e.g., https://jellyfin.example.com)",
@ -78,6 +82,11 @@ const settings = definePluginSettings({
description: "Custom Rich Presence name (only used if 'Custom' is selected).\nOptions: {name}, {series}, {season}, {episode}, {artist}, {album}, {year}",
type: OptionType.STRING,
},
showTMDBButton: {
description: "Show TheMovieDB button in Rich Presence",
type: OptionType.BOOLEAN,
default: true,
},
overrideRichPresenceType: {
description: "Override the rich presence type",
type: OptionType.SELECT,
@ -123,6 +132,27 @@ function setActivity(activity: Activity | null) {
});
}
async function fetchTmdbData(query: string) {
try {
const res = await fetch(`https://api.vmohammad.dev/tmdb/search/multi?q=${encodeURIComponent(query)}`);
if (!res.ok) throw `${res.status} ${res.statusText}`;
const data = await res.json();
if (data.results && data.results.length > 0) {
const topResult = data.results[0];
return {
url: `https://www.themoviedb.org/${topResult.media_type}/${topResult.id}`,
posterPath: topResult.poster_path
? `https://image.tmdb.org/t/p/original${topResult.poster_path}`
: null
};
}
return null;
} catch (e) {
console.error("Failed to fetch TMDb data:", e);
return null;
}
}
export default definePlugin({
name: "JellyfinRichPresence",
description: "Rich presence for Jellyfin media server",
@ -274,15 +304,29 @@ export default definePlugin({
break;
}
const largeImage = mediaData.imageUrl;
let tmdbData: { url: string; posterPath?: string | null } | null = null;
if (settings.store.showTMDBButton) {
tmdbData = await fetchTmdbData(mediaData.seriesName || mediaData.name);
}
const assets: ActivityAssets = {
large_image: largeImage ? await getApplicationAsset(largeImage) : await getApplicationAsset("jellyfin"),
large_text: mediaData.album || mediaData.seriesName || undefined,
large_image: tmdbData?.posterPath ? await getApplicationAsset(tmdbData.posterPath) : mediaData.imageUrl ? await getApplicationAsset(mediaData.imageUrl) : undefined,
large_text: mediaData.seriesName || mediaData.album || undefined,
};
const buttons: ActivityButton[] = [];
if (settings.store.showTMDBButton) {
const result = await fetchTmdbData(mediaData.seriesName || mediaData.name);
if (result?.url) tmdbData = { url: result.url };
buttons.push({
label: "View on TheMovieDB",
url: `${tmdbData?.url}`
});
}
const getDetails = () => {
if (mediaData.type === "Episode" && mediaData.seriesName) {
return mediaData.name;
return mediaData.seriesName;
}
return mediaData.name;
};
@ -291,7 +335,7 @@ export default definePlugin({
if (mediaData.type === "Episode" && mediaData.seriesName) {
const season = mediaData.seasonNumber ? `S${mediaData.seasonNumber}` : "";
const episode = mediaData.episodeNumber ? `E${mediaData.episodeNumber}` : "";
return `${mediaData.seriesName} ${season}${episode}`.trim();
return `${mediaData.name} (${season} - ${episode})`.trim();
}
return mediaData.artist || (mediaData.year ? `(${mediaData.year})` : undefined);
};
@ -309,8 +353,12 @@ export default definePlugin({
assets,
timestamps,
buttons: buttons.length ? buttons.map(v => v.label) : undefined,
metadata: {
button_urls: buttons.map(v => v.url),
},
type: richPresenceType,
flags: 1,
flags: 1
};
}
});

View file

@ -57,7 +57,7 @@ function splitTextWithIndicators(text: string): ReactNode[] {
const nodes: ReactNode[] = [];
let lastIndex = 0;
let count = 0;
const regex = /\/([a-z]{1,20})(?=\s|$|[^\w])/gi;
const regex = /(?:^|\s)\/([^/\s]+)(?:$|\s|\p{P})/giu;
let match: RegExpExecArray | null;
while ((match = regex.exec(text)) && count < settings.store.maxIndicators) {

View file

@ -17,16 +17,15 @@
*/
import { app } from "electron";
import { copyFileSync, existsSync, readFileSync, writeFileSync } from "fs";
import { join } from "path";
export const DATA_DIR = process.env.EQUICORD_USER_DATA_DIR ?? process.env.VENCORD_USER_DATA_DIR ?? (
const suffix = IS_DEV ? "dev" : "";
export const DATA_DIR = process.env.EQUICORD_USER_DATA_DIR ?? (
process.env.DISCORD_USER_DATA_DIR
? IS_VESKTOP
? join(process.env.DISCORD_USER_DATA_DIR, "..", "VencordData")
: join(process.env.DISCORD_USER_DATA_DIR, "..", "EquicordData")
: IS_VESKTOP
? join(app.getPath("userData"), "..", "Vencord")
: join(app.getPath("userData"), "..", "Equicord")
? join(process.env.DISCORD_USER_DATA_DIR, "..", "EquicordData", suffix)
: join(app.getPath("userData"), "..", "Equicord", suffix)
);
export const SETTINGS_DIR = join(DATA_DIR, "settings");
@ -34,6 +33,7 @@ export const THEMES_DIR = join(DATA_DIR, "themes");
export const QUICKCSS_PATH = join(SETTINGS_DIR, "quickCss.css");
export const SETTINGS_FILE = join(SETTINGS_DIR, "settings.json");
export const NATIVE_SETTINGS_FILE = join(SETTINGS_DIR, "native-settings.json");
export const DEV_MIGRATED = join(SETTINGS_DIR, "migration");
export const ALLOWED_PROTOCOLS = [
"https:",
"http:",
@ -45,3 +45,29 @@ export const ALLOWED_PROTOCOLS = [
];
export const IS_VANILLA = /* @__PURE__ */ process.argv.includes("--vanilla");
if (IS_DEV) {
const prodDir = join(DATA_DIR, "..");
const settings = join(prodDir, "settings", "settings.json");
const quickCss = join(prodDir, "settings", "quickCss.css");
let migrated = false;
if (existsSync(DEV_MIGRATED)) {
const content = readFileSync(DEV_MIGRATED, "utf-8");
migrated = content.includes("migrated");
}
if (!migrated) {
setTimeout(() => {
try {
if (existsSync(settings)) copyFileSync(settings, SETTINGS_FILE);
if (existsSync(quickCss)) copyFileSync(quickCss, QUICKCSS_PATH);
writeFileSync(DEV_MIGRATED, "migrated");
app.relaunch();
app.exit(0);
} catch (err) {
console.error("[Equicord] Failed to copy prod data:", err);
}
}, 5000);
}
}