Status updating working

This commit is contained in:
Fergal Moran
2023-07-09 21:15:19 +01:00
parent 2afb202613
commit d3d9d60a36
23 changed files with 196 additions and 529 deletions

View File

@@ -31,6 +31,7 @@
"commander": "^11.0.0",
"dotenv": "^16.3.1",
"drizzle-orm": "^0.27.0",
"encoding": "^0.1.13",
"http-status-codes": "^2.2.0",
"import-local": "^3.1.0",
"install": "^0.13.0",

81
pnpm-lock.yaml generated
View File

@@ -19,7 +19,7 @@ dependencies:
version: 1.1.4(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@sentry/nextjs':
specifier: ^7.57.0
version: 7.57.0(next@13.4.10-canary.0)(react@18.2.0)
version: 7.57.0(encoding@0.1.13)(next@13.4.10-canary.0)(react@18.2.0)
'@sindresorhus/slugify':
specifier: ^2.2.1
version: 2.2.1
@@ -43,7 +43,7 @@ dependencies:
version: 10.33.0
argon2:
specifier: ^0.30.3
version: 0.30.3
version: 0.30.3(encoding@0.1.13)
axios:
specifier: ^1.4.0
version: 1.4.0
@@ -59,6 +59,9 @@ dependencies:
drizzle-orm:
specifier: ^0.27.0
version: 0.27.0(@opentelemetry/api@1.4.1)(@types/pg@8.10.2)(pg@8.11.1)(postgres@3.3.5)
encoding:
specifier: ^0.1.13
version: 0.1.13
http-status-codes:
specifier: ^2.2.0
version: 2.2.0
@@ -82,7 +85,7 @@ dependencies:
version: 3.3.5
quirrel:
specifier: ^1.14.1
version: 1.14.1(@types/ioredis-mock@8.2.2)
version: 1.14.1(@types/ioredis-mock@8.2.2)(encoding@0.1.13)
react:
specifier: 18.2.0
version: 18.2.0
@@ -114,7 +117,7 @@ devDependencies:
version: 3.2.3
'@azure/storage-blob':
specifier: ^12.14.0
version: 12.14.0
version: 12.14.0(encoding@0.1.13)
'@faker-js/faker':
specifier: ^8.0.2
version: 8.0.2
@@ -231,7 +234,7 @@ devDependencies:
version: 0.3.0(@ianvs/prettier-plugin-sort-imports@4.0.2)(prettier@2.8.8)
pusher:
specifier: ^5.1.3
version: 5.1.3
version: 5.1.3(encoding@0.1.13)
pusher-js:
specifier: ^8.2.0
version: 8.2.0
@@ -327,7 +330,7 @@ packages:
- supports-color
dev: true
/@azure/core-http@3.0.2:
/@azure/core-http@3.0.2(encoding@0.1.13):
resolution: {integrity: sha512-o1wR9JrmoM0xEAa0Ue7Sp8j+uJvmqYaGoHOCT5qaVYmvgmnZDC0OvQimPA/JR3u77Sz6D1y3Xmk1y69cDU9q9A==}
engines: {node: '>=14.0.0'}
dependencies:
@@ -339,7 +342,7 @@ packages:
'@types/node-fetch': 2.6.4
'@types/tunnel': 0.0.3
form-data: 4.0.0
node-fetch: 2.6.12
node-fetch: 2.6.12(encoding@0.1.13)
process: 0.11.10
tslib: 2.6.0
tunnel: 0.0.6
@@ -458,12 +461,12 @@ packages:
uuid: 8.3.2
dev: true
/@azure/storage-blob@12.14.0:
/@azure/storage-blob@12.14.0(encoding@0.1.13):
resolution: {integrity: sha512-g8GNUDpMisGXzBeD+sKphhH5yLwesB4JkHr1U6be/X3F+cAMcyGLPD1P89g2M7wbEtUJWoikry1rlr83nNRBzg==}
engines: {node: '>=14.0.0'}
dependencies:
'@azure/abort-controller': 1.1.0
'@azure/core-http': 3.0.2
'@azure/core-http': 3.0.2(encoding@0.1.13)
'@azure/core-lro': 2.5.3
'@azure/core-paging': 1.5.0
'@azure/core-tracing': 1.0.0-preview.13
@@ -1770,14 +1773,14 @@ packages:
engines: {node: '>=8'}
dev: false
/@mapbox/node-pre-gyp@1.0.10:
/@mapbox/node-pre-gyp@1.0.10(encoding@0.1.13):
resolution: {integrity: sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==}
hasBin: true
dependencies:
detect-libc: 2.0.1
https-proxy-agent: 5.0.1
make-dir: 3.1.0
node-fetch: 2.6.12
node-fetch: 2.6.12(encoding@0.1.13)
nopt: 5.0.0
npmlog: 5.0.1
rimraf: 3.0.2
@@ -2681,7 +2684,7 @@ packages:
tslib: 2.6.0
dev: false
/@sentry/cli@1.75.2:
/@sentry/cli@1.75.2(encoding@0.1.13):
resolution: {integrity: sha512-CG0CKH4VCKWzEaegouWfCLQt9SFN+AieFESCatJ7zSuJmzF05ywpMusjxqRul6lMwfUhRKjGKOzcRJ1jLsfTBw==}
engines: {node: '>= 8'}
hasBin: true
@@ -2689,7 +2692,7 @@ packages:
dependencies:
https-proxy-agent: 5.0.1
mkdirp: 0.5.6
node-fetch: 2.6.12
node-fetch: 2.6.12(encoding@0.1.13)
progress: 2.0.3
proxy-from-env: 1.1.0
which: 2.0.2
@@ -2717,7 +2720,7 @@ packages:
tslib: 2.6.0
dev: false
/@sentry/nextjs@7.57.0(next@13.4.10-canary.0)(react@18.2.0):
/@sentry/nextjs@7.57.0(encoding@0.1.13)(next@13.4.10-canary.0)(react@18.2.0):
resolution: {integrity: sha512-TH7Hhs833j1k2rM5K3AqiQ7+bxrTzANZazBLEK1YVec02PpnqflVuBHSdFxT6dG7ypxOpMkN36BN5INY5HHT0Q==}
engines: {node: '>=8'}
peerDependencies:
@@ -2735,7 +2738,7 @@ packages:
'@sentry/react': 7.57.0(react@18.2.0)
'@sentry/types': 7.57.0
'@sentry/utils': 7.57.0
'@sentry/webpack-plugin': 1.20.0
'@sentry/webpack-plugin': 1.20.0(encoding@0.1.13)
chalk: 3.0.0
next: 13.4.10-canary.0(@babel/core@7.22.6)(@opentelemetry/api@1.4.1)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
@@ -2806,11 +2809,11 @@ packages:
tslib: 2.6.0
dev: false
/@sentry/webpack-plugin@1.20.0:
/@sentry/webpack-plugin@1.20.0(encoding@0.1.13):
resolution: {integrity: sha512-Ssj1mJVFsfU6vMCOM2d+h+KQR7QHSfeIP16t4l20Uq/neqWXZUQ2yvQfe4S3BjdbJXz/X4Rw8Hfy1Sd0ocunYw==}
engines: {node: '>= 8'}
dependencies:
'@sentry/cli': 1.75.2
'@sentry/cli': 1.75.2(encoding@0.1.13)
webpack-sources: 3.2.3
transitivePeerDependencies:
- encoding
@@ -3321,7 +3324,7 @@ packages:
dependencies:
async-retry: 1.3.3
debug: 4.3.4
node-fetch: 2.6.7
node-fetch: 2.6.7(encoding@0.1.13)
transitivePeerDependencies:
- supports-color
dev: false
@@ -3512,12 +3515,12 @@ packages:
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
dev: true
/argon2@0.30.3:
/argon2@0.30.3(encoding@0.1.13):
resolution: {integrity: sha512-DoH/kv8c9127ueJSBxAVJXinW9+EuPA3EMUxoV2sAY1qDE5H9BjTyVF/aD2XyHqbqUWabgBkIfcP3ZZuGhbJdg==}
engines: {node: '>=14.0.0'}
requiresBuild: true
dependencies:
'@mapbox/node-pre-gyp': 1.0.10
'@mapbox/node-pre-gyp': 1.0.10(encoding@0.1.13)
'@phc/format': 1.0.0
node-addon-api: 5.1.0
transitivePeerDependencies:
@@ -4213,10 +4216,10 @@ packages:
luxon: 3.3.0
dev: false
/cross-fetch@3.1.8:
/cross-fetch@3.1.8(encoding@0.1.13):
resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==}
dependencies:
node-fetch: 2.6.12
node-fetch: 2.6.12(encoding@0.1.13)
transitivePeerDependencies:
- encoding
dev: false
@@ -4730,6 +4733,11 @@ packages:
engines: {node: '>= 0.8'}
dev: false
/encoding@0.1.13:
resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
dependencies:
iconv-lite: 0.6.3
/enhanced-resolve@5.15.0:
resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==}
engines: {node: '>=10.13.0'}
@@ -5926,6 +5934,12 @@ packages:
safer-buffer: 2.1.2
dev: false
/iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
dependencies:
safer-buffer: 2.1.2
/ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
dev: false
@@ -7324,7 +7338,7 @@ packages:
resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==}
dev: false
/node-fetch@2.6.12:
/node-fetch@2.6.12(encoding@0.1.13):
resolution: {integrity: sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==}
engines: {node: 4.x || >=6.0.0}
peerDependencies:
@@ -7333,9 +7347,10 @@ packages:
encoding:
optional: true
dependencies:
encoding: 0.1.13
whatwg-url: 5.0.0
/node-fetch@2.6.7:
/node-fetch@2.6.7(encoding@0.1.13):
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
engines: {node: 4.x || >=6.0.0}
peerDependencies:
@@ -7344,6 +7359,7 @@ packages:
encoding:
optional: true
dependencies:
encoding: 0.1.13
whatwg-url: 5.0.0
dev: false
@@ -7870,10 +7886,10 @@ packages:
pathe: 1.1.1
dev: true
/plausible-telemetry@0.1.0:
/plausible-telemetry@0.1.0(encoding@0.1.13):
resolution: {integrity: sha512-wz3RTCMXGV54ilCkWRcI1gbiko8j/qH/A6402ScFUM5XY2rnqAX/Prc+i/lvHwLORL2ZLdCWvngvOpXCmmkRHQ==}
dependencies:
cross-fetch: 3.1.8
cross-fetch: 3.1.8(encoding@0.1.13)
transitivePeerDependencies:
- encoding
dev: false
@@ -8196,14 +8212,14 @@ packages:
tweetnacl: 1.0.3
dev: true
/pusher@5.1.3:
/pusher@5.1.3(encoding@0.1.13):
resolution: {integrity: sha512-Bmy5guFxQsbYSFLF3CM7GA2qE1zDJYn51PnNme9QlSjGguvkqUg4nj31PbgiLVDFK2sJvxPfx4JrB2HLgM3kaw==}
engines: {node: '>= 4.0.0'}
dependencies:
'@types/node-fetch': 2.6.4
abort-controller: 3.0.0
is-base64: 1.1.0
node-fetch: 2.6.12
node-fetch: 2.6.12(encoding@0.1.13)
tweetnacl: 1.0.3
tweetnacl-util: 0.15.1
transitivePeerDependencies:
@@ -8231,7 +8247,7 @@ packages:
resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==}
dev: false
/quirrel@1.14.1(@types/ioredis-mock@8.2.2):
/quirrel@1.14.1(@types/ioredis-mock@8.2.2)(encoding@0.1.13):
resolution: {integrity: sha512-ROrge19oMruhRx68DGN3wxGCIc6MweeYDRgVM5IrAYqG0jzeAiS1GiFMA7GQyrbNgJ8bOtfMfINYVxYJt97HuQ==}
engines: {node: 14 || 15 || 16 || 17 || 18 || 19 || 20}
hasBin: true
@@ -8257,7 +8273,7 @@ packages:
conditional-type-checks: 1.0.6
connect: 3.7.0
cron-parser: 4.8.1
cross-fetch: 3.1.8
cross-fetch: 3.1.8(encoding@0.1.13)
cross-spawn: 7.0.3
dd-trace: 3.25.0
easy-table: 1.2.0
@@ -8272,12 +8288,12 @@ packages:
js-yaml: 4.1.0
jsonwebtoken: 9.0.0
ms: 2.1.3
node-fetch: 2.6.7
node-fetch: 2.6.7(encoding@0.1.13)
open: 8.4.0
opentracing: 0.14.7
parse-gitignore: 1.0.1
pino: 8.14.1
plausible-telemetry: 0.1.0
plausible-telemetry: 0.1.0(encoding@0.1.13)
posthog-node: 2.6.0
secure-e2ee: 0.4.0
secure-webhooks: 0.3.0
@@ -8728,7 +8744,6 @@ packages:
/safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
dev: false
/sax@1.2.4:
resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==}

View File

@@ -8,9 +8,9 @@ echo Creating new db
createdb -h localhost -U postgres mixyboos
#Don't remove migrations now that we have an actual live deployment
rm -rf $PROJECT_ROOT/drizzle/*
rm -rf $PROJECT_ROOT/src/db/migrations/*
npx drizzle-kit generate:pg --schema=./src/db/schema.ts
npx drizzle-kit generate:pg
curl --location 'https://mixyboos.dev.fergl.ie:3000/api/trpc/auth.signUp?batch=1' \

View File

@@ -0,0 +1,28 @@
import { processMixQueue } from "@/app/api/queues/upload/mix/route";
import { StatusCodes } from "http-status-codes";
import { type NextRequest } from "next/server";
import path from "path";
import os from "os";
import fs from "fs";
const extensions = ["mp3", "wav", "aiff", "mp4", "ogg"];
export async function POST(req: NextRequest) {
const { mixId } = (await req.json()) as { mixId: string };
let fileName = "";
let exists = false;
for (const extension of extensions) {
fileName = path.join(os.tmpdir(), `${mixId}.${extension}`);
if (fs.existsSync(fileName)) {
exists = true;
break;
}
}
if (exists) {
await processMixQueue.enqueue({ filePath: fileName, mixId }, { delay: 1 });
return StatusCodes.OK;
}
return StatusCodes.NO_CONTENT;
}

View File

@@ -4,6 +4,10 @@ import os from "os";
import path from "path";
import { uploadFolder } from "@/lib/services/azure/serverUploader";
import { Queue } from "quirrel/next-app";
import { db } from "@/server/db";
import { mixes } from "@/db/schema";
import { eq } from "drizzle-orm";
import * as Sentry from "@sentry/nextjs";
export const processMixQueue = Queue(
"api/queues/upload/mix", // 👈 the route it's reachable on
@@ -12,7 +16,6 @@ export const processMixQueue = Queue(
const outputDir = `${os.tmpdir()}/${mixId}`;
fs.mkdirSync(outputDir);
const command = `ffmpeg `;
// prettier-ignore
const process = spawn("ffmpeg", [
"-i",
@@ -40,11 +43,17 @@ export const processMixQueue = Queue(
}
uploadFolder(outputDir, "audio", path.join("mixes", mixId))
.then((r) => {
//probably need to tag the mix in some way here?
.then(async (r) => {
if (r) {
await db
.update(mixes)
.set({ isProcessed: true })
.where(eq(mixes.id, mixId));
}
})
.catch((err) => {
console.log("route", "error uploading output folder", err);
Sentry.captureException(err);
});
});
}

View File

@@ -2,6 +2,9 @@ import { writeFile } from "fs/promises";
import { getFileExtension } from "@/lib/utils/fileUtils";
import { NextResponse, type NextRequest } from "next/server";
import { processMixQueue } from "../queues/upload/mix/route";
import * as Sentry from "@sentry/nextjs";
import path from "path";
import os from "os";
export async function POST(req: NextRequest) {
const data = await req.formData();
@@ -15,9 +18,19 @@ export async function POST(req: NextRequest) {
const bytes = await file.arrayBuffer();
const buffer = Buffer.from(bytes);
const path = `/tmp/${id}.${getFileExtension(file.name)}`;
await writeFile(path, buffer);
console.log(`open ${path} to see the uploaded file`);
await processMixQueue.enqueue({ filePath: path, mixId: id }, { delay: 1 });
const outputFile = path.join(
os.tmpdir(),
`${id}.${getFileExtension(file.name)}`
);
await writeFile(outputFile, buffer);
console.log(`open ${outputFile} to see the uploaded file`);
try {
await processMixQueue.enqueue(
{ filePath: outputFile, mixId: id },
{ delay: 1 }
);
} catch (err) {
Sentry.captureException(err);
}
return NextResponse.json({ success: true });
}

View File

@@ -43,24 +43,6 @@ const UserPage: React.FC<UserPageProps> = ({ user, mixes }) => {
</div>
</div>
</div>
// <div>
// <div className="relative">
// <section className="h-[350px]">
// <Image
// alt="User header"
// src={user.headerImage}
// layout="fill"
// objectFit="cover"
// />
// </section>
// <div className="absolute left-5 top-[8em] mx-5 max-w-md rounded-sm bg-muted px-4 shadow-xl">
// <UserBioComponent user={user} />
// </div>
// </div>
// <div className="ml-72">
// <MixListComponent mixes={mixes} />
// </div>
// </div>
);
};

View File

@@ -50,6 +50,8 @@ const MainPlayer = ({ mix }: IMainPlayerProps) => {
mix.user?.profileImage ||
"/img/streaming-placeholder.jpg"
}
width={16}
height={16}
alt="Mix"
/>
)}

View File

@@ -7,6 +7,8 @@ import useAudioStore, {
PlayState,
} from "@/lib/services/stores/audio/audioStore";
import { Icons } from "@/components/icons";
import { mixService } from "@/lib/services/audio";
import * as Sentry from "@sentry/nextjs";
interface IPlayPauseButtonProps {
mix: MixModel;
@@ -30,16 +32,17 @@ const PlayPauseButton = ({
return (
<div
className={classnames(classes)}
onClick={() => {
onClick={async () => {
if (
playState === PlayState.stopped ||
(mix.id !== nowPlaying?.id && !nowPlayingUrl)
) {
// mixService.getMixAudioUrl(mix).then((url) => {
// setNowPlaying(mix);
// setNowPlayingUrl(url);
// onPlayStart();
// });
const url = await mixService.getMixAudioUrl(mix.id);
if (url) {
setNowPlaying(mix);
setNowPlayingUrl(url);
onPlayStart();
}
} else {
togglePlayState();
}
@@ -47,7 +50,7 @@ const PlayPauseButton = ({
>
<div
className={
"cursor-pointer text-gray-500 transition duration-200 hover:text-gray-800 dark:text-gray-200 dark:hover:text-gray-700"
"cursor-pointer text-foreground transition duration-200 hover:text-muted-foreground"
}
>
{nowPlaying?.id === mix.id && playState === PlayState.playing ? (

View File

@@ -25,8 +25,9 @@ ALTER TABLE "live_show_tags" ADD CONSTRAINT "live_show_tags_live_show_id_tag_id"
CREATE TABLE IF NOT EXISTS "mixes" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"slug" varchar(50) NOT NULL,
"full_name" text,
"title" varchar(50) NOT NULL,
"description" varchar(2048),
"audioUrl" text,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL,
"user_id" uuid NOT NULL,
@@ -52,7 +53,8 @@ CREATE TABLE IF NOT EXISTS "users" (
"username" text NOT NULL,
"email" text NOT NULL,
"emailVerified" timestamp,
"name" varchar(2048),
"name" text,
"bio" varchar(2048),
"profileImage" text,
"headerImage" text,
"password" varchar(1024),

View File

@@ -1,2 +0,0 @@
ALTER TABLE "users" ALTER COLUMN "name" SET DATA TYPE text;--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "bio" varchar(2048);

View File

@@ -1,7 +1,7 @@
{
"version": "5",
"dialect": "pg",
"id": "3aa3a733-36d0-4095-a8a5-f3436102791a",
"id": "5d3b8fec-377f-4959-a07d-00309b6c1da5",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"live_shows": {
@@ -151,11 +151,11 @@
"primaryKey": false,
"notNull": true
},
"full_name": {
"name": "full_name",
"type": "text",
"title": {
"name": "title",
"type": "varchar(50)",
"primaryKey": false,
"notNull": false
"notNull": true
},
"description": {
"name": "description",
@@ -163,6 +163,12 @@
"primaryKey": false,
"notNull": false
},
"audioUrl": {
"name": "audioUrl",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
@@ -340,6 +346,12 @@
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"bio": {
"name": "bio",
"type": "varchar(2048)",
"primaryKey": false,
"notNull": false

View File

@@ -1,414 +0,0 @@
{
"version": "5",
"dialect": "pg",
"id": "35e487a5-8038-45d5-9ecc-9096c10317b5",
"prevId": "3aa3a733-36d0-4095-a8a5-f3436102791a",
"tables": {
"live_shows": {
"name": "live_shows",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"slug": {
"name": "slug",
"type": "varchar(50)",
"primaryKey": false,
"notNull": true
},
"full_name": {
"name": "full_name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"description": {
"name": "description",
"type": "varchar(256)",
"primaryKey": false,
"notNull": false
},
"status": {
"name": "status",
"type": "LiveShowStatus",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"live_shows_user_id_users_id_fk": {
"name": "live_shows_user_id_users_id_fk",
"tableFrom": "live_shows",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {}
},
"live_show_tags": {
"name": "live_show_tags",
"schema": "",
"columns": {
"live_show_id": {
"name": "live_show_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"tag_id": {
"name": "tag_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"live_show_tags_live_show_id_mixes_id_fk": {
"name": "live_show_tags_live_show_id_mixes_id_fk",
"tableFrom": "live_show_tags",
"tableTo": "mixes",
"columnsFrom": [
"live_show_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"live_show_tags_tag_id_tags_id_fk": {
"name": "live_show_tags_tag_id_tags_id_fk",
"tableFrom": "live_show_tags",
"tableTo": "tags",
"columnsFrom": [
"tag_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"live_show_tags_live_show_id_tag_id": {
"name": "live_show_tags_live_show_id_tag_id",
"columns": [
"live_show_id",
"tag_id"
]
}
}
},
"mixes": {
"name": "mixes",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"slug": {
"name": "slug",
"type": "varchar(50)",
"primaryKey": false,
"notNull": true
},
"full_name": {
"name": "full_name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"description": {
"name": "description",
"type": "varchar(2048)",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"is_processed": {
"name": "is_processed",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"default": false
}
},
"indexes": {},
"foreignKeys": {
"mixes_user_id_users_id_fk": {
"name": "mixes_user_id_users_id_fk",
"tableFrom": "mixes",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {}
},
"mix_tags": {
"name": "mix_tags",
"schema": "",
"columns": {
"mix_id": {
"name": "mix_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"tag_id": {
"name": "tag_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"mix_tags_mix_id_mixes_id_fk": {
"name": "mix_tags_mix_id_mixes_id_fk",
"tableFrom": "mix_tags",
"tableTo": "mixes",
"columnsFrom": [
"mix_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"mix_tags_tag_id_tags_id_fk": {
"name": "mix_tags_tag_id_tags_id_fk",
"tableFrom": "mix_tags",
"tableTo": "tags",
"columnsFrom": [
"tag_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"mix_tags_mix_id_tag_id": {
"name": "mix_tags_mix_id_tag_id",
"columns": [
"mix_id",
"tag_id"
]
}
}
},
"tags": {
"name": "tags",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {
"title_idx": {
"name": "title_idx",
"columns": [
"title"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {}
},
"users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"username": {
"name": "username",
"type": "text",
"primaryKey": false,
"notNull": true
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"emailVerified": {
"name": "emailVerified",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"bio": {
"name": "bio",
"type": "varchar(2048)",
"primaryKey": false,
"notNull": false
},
"profileImage": {
"name": "profileImage",
"type": "text",
"primaryKey": false,
"notNull": false
},
"headerImage": {
"name": "headerImage",
"type": "text",
"primaryKey": false,
"notNull": false
},
"password": {
"name": "password",
"type": "varchar(1024)",
"primaryKey": false,
"notNull": false
},
"stream_key": {
"name": "stream_key",
"type": "varchar(64)",
"primaryKey": false,
"notNull": false
},
"urls": {
"name": "urls",
"type": "text[]",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"username_idx": {
"name": "username_idx",
"columns": [
"username"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {}
}
},
"enums": {
"LiveShowStatus": {
"name": "LiveShowStatus",
"values": {
"SETUP": "SETUP",
"AWAITING": "AWAITING",
"STREAMING": "STREAMING",
"FINISHED": "FINISHED"
}
}
},
"schemas": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
}
}

View File

@@ -5,15 +5,8 @@
{
"idx": 0,
"version": "5",
"when": 1688336894767,
"tag": "0000_mysterious_sentinels",
"breakpoints": true
},
{
"idx": 1,
"version": "5",
"when": 1688505145441,
"tag": "0001_outgoing_juggernaut",
"when": 1688762411769,
"tag": "0000_clever_thunderbolt",
"breakpoints": true
}
]

View File

@@ -35,9 +35,9 @@ export const tags = pgTable(
export const mixes = pgTable("mixes", {
id: uuid("id").primaryKey().defaultRandom(),
slug: varchar("slug", { length: 50 }).notNull(),
title: varchar("full_name", { length: 50 }).notNull(),
title: varchar("title", { length: 50 }).notNull(),
description: varchar("description", { length: 2048 }),
audioUrl: text("full_name"),
audioUrl: text("audioUrl"),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at").notNull().defaultNow(),

View File

@@ -44,8 +44,8 @@ const NavLink = ({ href, title, Icon }: NavLinkProps) => {
<Link
href={href}
className={cn(
"text-sm font-medium lowercase text-primary-foreground hover:opacity-60 transition-opacity",
path !== href && "text-primary-foreground"
"text-sm font-medium lowercase text-muted transition-opacity hover:opacity-60",
path !== href && "text-foreground"
)}
>
<div className="inline-flex items-center">

View File

@@ -60,7 +60,6 @@ const MixCreateComponent = () => {
)}
{uploadState === UploadState.new && (
<div className="mx-auto my-8 w-3/5">
{" "}
<FileUpload
mixId={mixId}
onError={(e) => {
@@ -86,7 +85,6 @@ const MixCreateComponent = () => {
onMixCreated={(mix) => {
setCreateState(mix ? CreateState.done : CreateState.error);
if (mix) {
debugger;
router.push(`/${mix.user.username}/${mix.slug}`);
}
}}

View File

@@ -81,11 +81,12 @@ const MixCreateDetailsComponent: React.FC<MixCreateDetailsComponentProps> = ({
console.log("MixCreateDetailsComponent", "onSubmit", values);
try {
const result = await createMix.mutateAsync({
id: mix.id,
title: values.title,
description: values.description,
tags: [],
});
onMixCreated(result as MixModel);
onMixCreated(result);
} catch (err) {
Sentry.captureException(err);
}

View File

@@ -0,0 +1,3 @@
import mixService from "./mix-service";
export { mixService };

View File

@@ -0,0 +1,18 @@
import { db } from "@/server/db";
const mixService = {
getMixAudioUrl: async (mixId: string) => {
// const mixQuery = await db.query.mixes.findFirst({
// where: (mixes, { eq }) => eq(mixes.id, mixId),
// });
// if (!mixQuery) {
// throw new Error("Unable to find mix");
// }
// return `https://argle.bargle/${mixQuery.audioUrl}`;
},
};
export default mixService;

View File

@@ -0,0 +1,8 @@
const mapImage = (img: string | null, fallback: string) => {
return img
? img.startsWith("http")
? img
: `https://mixyboos.twic.pics/${img}?twic=v1/resize=256`
: fallback;
};
export default mapImage;

View File

@@ -1,6 +1,7 @@
import { type User as DbUser } from "@/db/schema";
import { type UserModel } from "@/lib/models";
import { type User as AuthUser } from "next-auth";
import mapImage from "./imageMapper";
const mapAuthUserToUserModel = (
user: AuthUser | undefined | null
@@ -12,12 +13,8 @@ const mapAuthUserToUserModel = (
name: user.name,
email: user.email,
bio: user.bio,
profileImage: user.profileImage
? `https://mixyboos.twic.pics/${user.profileImage}?twic=v1/resize=256`
: "/img/default-avatar.png",
headerImage: user.headerImage
? `https://mixyboos.twic.pics/${user.headerImage}?twic=v1/resize=1200x400`
: "/img/default-header.png",
profileImage: mapImage(user.profileImage, "/img/default-avatar.png"),
headerImage: mapImage(user.headerImage, "/img/default-header.png"),
urls: [],
}
: undefined;
@@ -28,12 +25,8 @@ const mapDbAuthUserToUserModel = (user: DbUser): UserModel => ({
name: user.name || user.username,
email: user.email,
bio: user.bio,
profileImage: user.profileImage
? `https://mixyboos.twic.pics/${user.profileImage}?twic=v1/resize=256`
: "/img/default-avatar.png",
headerImage: user.headerImage
? `https://mixyboos.twic.pics/${user.headerImage}?twic=v1/resize=1200x400`
: "/img/default-header.png",
profileImage: mapImage(user.profileImage, "/img/default-avatar.png"),
headerImage: mapImage(user.profileImage, "/img/default-header.png"),
urls: user.urls,
});

View File

@@ -62,12 +62,13 @@ export const mixRouter = createTRPCRouter({
createMix: protectedProcedure
.input(
z.object({
id: z.string(),
title: z.string(),
description: z.string(),
tags: z.array(z.string()).optional(),
})
)
.mutation(async ({ input: { title, description, tags }, ctx }) => {
.mutation(async ({ input: { id, title, description, tags }, ctx }) => {
const userResult = await db
.selectDistinct()
.from(users)
@@ -89,18 +90,19 @@ export const mixRouter = createTRPCRouter({
.where(eq(mixes.slug, slug));
} while (checkSlugResult[0]?.count !== 0);
const newMixQuery = await db
const result = await db
.insert(mixes)
.values({ title, slug, description, userId: ctx.session.id })
.values({ id, title, slug, description, userId: ctx.session.id })
.returning();
const newMix = await db.query.mixes.findFirst({
where: eq(mixes.id, newMixQuery.id),
with: { user: true },
});
if (newMix && newMix[0]) {
const mix = newMix[0];
return mapMixToMixModel(mix);
if (result && result[0]) {
return mapMixToMixModel({
user: user,
...result[0],
});
}
throw new trpc.TRPCError({
message: "Unable to save mix",
code: "INTERNAL_SERVER_ERROR",
});
}),
});