diff --git a/apps/api/lexicons/playlist/insertDirectory.json b/apps/api/lexicons/playlist/insertDirectory.json index fe53ce40..9b3c466f 100644 --- a/apps/api/lexicons/playlist/insertDirectory.json +++ b/apps/api/lexicons/playlist/insertDirectory.json @@ -8,7 +8,8 @@ "parameters": { "type": "params", "required": [ - "uri" + "uri", + "directory" ], "properties": { "uri": { @@ -19,6 +20,10 @@ "directory": { "type": "string", "description": "The directory (id) to insert into the playlist" + }, + "position": { + "type": "integer", + "description": "The position in the playlist to insert the directory at, if not specified, the directory will be appended" } } } diff --git a/apps/api/lexicons/playlist/insertFiles.json b/apps/api/lexicons/playlist/insertFiles.json index a95603a7..b2669a62 100644 --- a/apps/api/lexicons/playlist/insertFiles.json +++ b/apps/api/lexicons/playlist/insertFiles.json @@ -8,7 +8,8 @@ "parameters": { "type": "params", "required": [ - "uri" + "uri", + "files" ], "properties": { "uri": { @@ -22,6 +23,10 @@ "type": "string", "description": "List of file (id) to insert into the playlist" } + }, + "position": { + "type": "integer", + "description": "The position in the playlist to insert the files at, if not specified, files will be appended" } } } diff --git a/apps/api/lexicons/playlist/startPlaylist.json b/apps/api/lexicons/playlist/startPlaylist.json index 47cdc4f8..354e7c2d 100644 --- a/apps/api/lexicons/playlist/startPlaylist.json +++ b/apps/api/lexicons/playlist/startPlaylist.json @@ -15,6 +15,14 @@ "type": "string", "description": "The URI of the playlist to start", "format": "at-uri" + }, + "shuffle": { + "type": "boolean", + "description": "Whether to shuffle the playlist when starting it" + }, + "position": { + "type": "integer", + "description": "The position in the playlist to start from, if not specified, starts from the beginning" } } } diff --git a/apps/api/pkl/defs/playlist/insertDirectory.pkl b/apps/api/pkl/defs/playlist/insertDirectory.pkl index 2e0462d2..c7db0afa 100644 --- a/apps/api/pkl/defs/playlist/insertDirectory.pkl +++ b/apps/api/pkl/defs/playlist/insertDirectory.pkl @@ -8,7 +8,7 @@ defs = new Mapping { description = "Insert a directory into a playlist" parameters { type = "params" - required = List("uri") + required = List("uri", "directory") properties { ["uri"] = new StringType { type = "string" @@ -19,6 +19,10 @@ defs = new Mapping { type = "string" description = "The directory (id) to insert into the playlist" } + ["position"] = new IntegerType { + type = "integer" + description = "The position in the playlist to insert the directory at, if not specified, the directory will be appended" + } } } } diff --git a/apps/api/pkl/defs/playlist/insertFiles.pkl b/apps/api/pkl/defs/playlist/insertFiles.pkl index 2b59bc7e..a73dee3c 100644 --- a/apps/api/pkl/defs/playlist/insertFiles.pkl +++ b/apps/api/pkl/defs/playlist/insertFiles.pkl @@ -8,7 +8,7 @@ defs = new Mapping { description = "Insert files into a playlist" parameters { type = "params" - required = List("uri") + required = List("uri", "files") properties { ["uri"] = new StringType { type = "string" @@ -22,6 +22,10 @@ defs = new Mapping { description = "List of file (id) to insert into the playlist" } } + ["position"] = new IntegerType { + type = "integer" + description = "The position in the playlist to insert the files at, if not specified, files will be appended" + } } } } diff --git a/apps/api/pkl/defs/playlist/startPlaylist.pkl b/apps/api/pkl/defs/playlist/startPlaylist.pkl index 514444d9..43c281d8 100644 --- a/apps/api/pkl/defs/playlist/startPlaylist.pkl +++ b/apps/api/pkl/defs/playlist/startPlaylist.pkl @@ -15,6 +15,14 @@ defs = new Mapping { description = "The URI of the playlist to start" format = "at-uri" } + ["shuffle"] = new BooleanType { + type = "boolean" + description = "Whether to shuffle the playlist when starting it" + } + ["position"] = new IntegerType { + type = "integer" + description = "The position in the playlist to start from, if not specified, starts from the beginning" + } } } } diff --git a/apps/api/pkl/schema/lexicon.pkl b/apps/api/pkl/schema/lexicon.pkl index 99700538..51814790 100644 --- a/apps/api/pkl/schema/lexicon.pkl +++ b/apps/api/pkl/schema/lexicon.pkl @@ -17,6 +17,10 @@ class IntegerType extends BaseType { minimum: Int? } +class BooleanType extends BaseType { + type: "boolean" +} + class Blob extends BaseType { type: "blob" accept: List @@ -31,7 +35,7 @@ class Array extends BaseType { class Params { type: "params" required: List? - properties: Mapping + properties: Mapping } class ObjectType extends BaseType { diff --git a/apps/api/src/lexicon/lexicons.ts b/apps/api/src/lexicon/lexicons.ts index d2b3fa2a..1bd69447 100644 --- a/apps/api/src/lexicon/lexicons.ts +++ b/apps/api/src/lexicon/lexicons.ts @@ -2344,7 +2344,7 @@ export const schemaDict = { description: 'Insert a directory into a playlist', parameters: { type: 'params', - required: ['uri'], + required: ['uri', 'directory'], properties: { uri: { type: 'string', @@ -2355,6 +2355,11 @@ export const schemaDict = { type: 'string', description: 'The directory (id) to insert into the playlist', }, + position: { + type: 'integer', + description: + 'The position in the playlist to insert the directory at, if not specified, the directory will be appended', + }, }, }, }, @@ -2369,7 +2374,7 @@ export const schemaDict = { description: 'Insert files into a playlist', parameters: { type: 'params', - required: ['uri'], + required: ['uri', 'files'], properties: { uri: { type: 'string', @@ -2383,6 +2388,11 @@ export const schemaDict = { description: 'List of file (id) to insert into the playlist', }, }, + position: { + type: 'integer', + description: + 'The position in the playlist to insert the files at, if not specified, files will be appended', + }, }, }, }, @@ -2515,6 +2525,15 @@ export const schemaDict = { description: 'The URI of the playlist to start', format: 'at-uri', }, + shuffle: { + type: 'boolean', + description: 'Whether to shuffle the playlist when starting it', + }, + position: { + type: 'integer', + description: + 'The position in the playlist to start from, if not specified, starts from the beginning', + }, }, }, }, diff --git a/apps/api/src/lexicon/types/app/rocksky/playlist/insertDirectory.ts b/apps/api/src/lexicon/types/app/rocksky/playlist/insertDirectory.ts index b1cca96a..7e99d000 100644 --- a/apps/api/src/lexicon/types/app/rocksky/playlist/insertDirectory.ts +++ b/apps/api/src/lexicon/types/app/rocksky/playlist/insertDirectory.ts @@ -12,7 +12,9 @@ export interface QueryParams { /** The URI of the playlist to start */ uri: string /** The directory (id) to insert into the playlist */ - directory?: string + directory: string + /** The position in the playlist to insert the directory at, if not specified, the directory will be appended */ + position?: number } export type InputSchema = undefined diff --git a/apps/api/src/lexicon/types/app/rocksky/playlist/insertFiles.ts b/apps/api/src/lexicon/types/app/rocksky/playlist/insertFiles.ts index 352dfab9..12b9112f 100644 --- a/apps/api/src/lexicon/types/app/rocksky/playlist/insertFiles.ts +++ b/apps/api/src/lexicon/types/app/rocksky/playlist/insertFiles.ts @@ -11,7 +11,9 @@ import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server' export interface QueryParams { /** The URI of the playlist to start */ uri: string - files?: string[] + files: string[] + /** The position in the playlist to insert the files at, if not specified, files will be appended */ + position?: number } export type InputSchema = undefined diff --git a/apps/api/src/lexicon/types/app/rocksky/playlist/startPlaylist.ts b/apps/api/src/lexicon/types/app/rocksky/playlist/startPlaylist.ts index eb37cb6f..8b783c8c 100644 --- a/apps/api/src/lexicon/types/app/rocksky/playlist/startPlaylist.ts +++ b/apps/api/src/lexicon/types/app/rocksky/playlist/startPlaylist.ts @@ -11,6 +11,10 @@ import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server' export interface QueryParams { /** The URI of the playlist to start */ uri: string + /** Whether to shuffle the playlist when starting it */ + shuffle?: boolean + /** The position in the playlist to start from, if not specified, starts from the beginning */ + position?: number } export type InputSchema = undefined diff --git a/apps/api/src/schema/index.ts b/apps/api/src/schema/index.ts index 9e2ce696..cc01eef7 100644 --- a/apps/api/src/schema/index.ts +++ b/apps/api/src/schema/index.ts @@ -16,6 +16,7 @@ import lovedTracks from "./loved-tracks"; import playlistTracks from "./playlist-tracks"; import playlists from "./playlists"; import profileShouts from "./profile-shouts"; +import queueTracks from "./queue-tracks"; import scrobbles from "./scrobbles"; import shoutLikes from "./shout-likes"; import shoutReports from "./shout-reports"; @@ -62,4 +63,5 @@ export default { googleDrivePaths, dropbox, googleDrive, + queueTracks, }; diff --git a/apps/api/src/schema/queue-tracks.ts b/apps/api/src/schema/queue-tracks.ts new file mode 100644 index 00000000..3a8fc3be --- /dev/null +++ b/apps/api/src/schema/queue-tracks.ts @@ -0,0 +1,24 @@ +import { InferInsertModel, InferSelectModel } from "drizzle-orm"; +import { integer, pgTable, text, timestamp } from "drizzle-orm/pg-core"; +import tracks from "./tracks"; +import users from "./users"; + +const queueTracks = pgTable("queue_tracks", { + id: text("xata_id").primaryKey(), + userId: text("user_id") + .notNull() + .references(() => users.id), + trackId: text("track_id") + .notNull() + .references(() => tracks.id), + position: integer("position").notNull(), + fileUri: text("file_uri").notNull(), + version: integer("xata_version").default(0).notNull(), + createdAt: timestamp("xata_createdat").defaultNow().notNull(), + updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), +}); + +export type SelectQueueTrack = InferSelectModel; +export type InsertQueueTrack = InferInsertModel; + +export default queueTracks; diff --git a/apps/api/src/xrpc/app/rocksky/player/addItemsToQueue.ts b/apps/api/src/xrpc/app/rocksky/player/addItemsToQueue.ts new file mode 100644 index 00000000..99000c07 --- /dev/null +++ b/apps/api/src/xrpc/app/rocksky/player/addItemsToQueue.ts @@ -0,0 +1,57 @@ +import { HandlerAuth } from "@atproto/xrpc-server"; +import { Context } from "context"; +import { Effect, pipe } from "effect"; +import { Server } from "lexicon"; +import { QueryParams } from "lexicon/types/app/rocksky/player/addItemsToQueue"; +import tables from "schema"; + +export default function (server: Server, ctx: Context) { + const addItemsToQueue = (params, auth: HandlerAuth) => + pipe( + { + params, + ctx, + did: auth.credentials?.did, + }, + handleAddItemsToQueue, + Effect.flatMap(presentation), + Effect.retry({ times: 3 }), + Effect.timeout("10 seconds"), + Effect.catchAll((err) => { + console.error(err); + return Effect.succeed({}); + }) + ); + server.app.rocksky.player.addItemsToQueue({ + auth: ctx.authVerifier, + handler: async ({ params, auth }) => { + const result = await Effect.runPromise(addItemsToQueue(params, auth)); + }, + }); +} + +const handleAddItemsToQueue = ({ + params, + ctx, + did, +}: { + params: QueryParams; + ctx: Context; + did?: string; +}) => + Effect.tryPromise({ + try: async () => { + await ctx.db.transaction(async (tx) => { + await tx.select().from(tables.queueTracks).execute(); + // Logic to add items to the queue would go here + }); + }, + catch: (err) => { + console.error(err); + return {}; + }, + }); + +const presentation = (): Effect.Effect<{}, never> => { + return Effect.sync(() => ({})); +}; diff --git a/apps/api/src/xrpc/app/rocksky/player/getPlaybackQueue.ts b/apps/api/src/xrpc/app/rocksky/player/getPlaybackQueue.ts new file mode 100644 index 00000000..407ac280 --- /dev/null +++ b/apps/api/src/xrpc/app/rocksky/player/getPlaybackQueue.ts @@ -0,0 +1,59 @@ +import { HandlerAuth } from "@atproto/xrpc-server"; +import { Context } from "context"; +import { Effect, pipe } from "effect"; +import { Server } from "lexicon"; +import { QueryParams } from "lexicon/types/app/rocksky/player/getPlaybackQueue"; +import tables from "schema"; + +export default function (server: Server, ctx: Context) { + const getPlaybackQueue = (params, auth: HandlerAuth) => + pipe( + { + params, + ctx, + did: auth.credentials?.did, + }, + retrieve, + Effect.flatMap(presentation), + Effect.retry({ times: 3 }), + Effect.timeout("10 seconds"), + Effect.catchAll((err) => { + console.error(err); + return Effect.succeed({}); + }) + ); + server.app.rocksky.player.getPlaybackQueue({ + auth: ctx.authVerifier, + handler: async ({ params, auth }) => { + const result = await Effect.runPromise(getPlaybackQueue(params, auth)); + return { + encoding: "application/json", + body: {}, + }; + }, + }); +} + +const retrieve = ({ + params, + ctx, + did, +}: { + params: QueryParams; + ctx: Context; + did?: string; +}) => + Effect.tryPromise({ + try: async () => { + await ctx.db.select().from(tables.queueTracks).execute(); + // Logic to retrieve the playback queue would go here + }, + catch: (err) => { + console.error(err); + return {}; + }, + }); + +const presentation = (): Effect.Effect<{}, never> => { + return Effect.sync(() => ({})); +}; diff --git a/apps/api/src/xrpc/app/rocksky/player/playDirectory.ts b/apps/api/src/xrpc/app/rocksky/player/playDirectory.ts new file mode 100644 index 00000000..b7f70780 --- /dev/null +++ b/apps/api/src/xrpc/app/rocksky/player/playDirectory.ts @@ -0,0 +1,57 @@ +import { HandlerAuth } from "@atproto/xrpc-server"; +import { Context } from "context"; +import { Effect, pipe } from "effect"; +import { Server } from "lexicon"; +import { QueryParams } from "lexicon/types/app/rocksky/player/playFile"; +import tables from "schema"; + +export default function (server: Server, ctx: Context) { + const playDirectory = (params, auth: HandlerAuth) => + pipe( + { + params, + ctx, + did: auth.credentials?.did, + }, + handlePlayDirectory, + Effect.flatMap(presentation), + Effect.retry({ times: 3 }), + Effect.timeout("10 seconds"), + Effect.catchAll((err) => { + console.error(err); + return Effect.succeed({}); + }) + ); + server.app.rocksky.player.playDirectory({ + auth: ctx.authVerifier, + handler: async ({ params, auth }) => { + const result = await Effect.runPromise(playDirectory(params, auth)); + }, + }); +} + +const handlePlayDirectory = ({ + params, + ctx, + did, +}: { + params: QueryParams; + ctx: Context; + did?: string; +}) => + Effect.tryPromise({ + try: async () => { + await ctx.db.transaction(async (tx) => { + await tx.select().from(tables.queueTracks).execute(); + // Logic to play the directory would go here + }); + }, + catch: (err) => { + console.error(err); + return {}; + }, + }); + +const presentation = (): Effect.Effect<{}, never> => { + return Effect.sync(() => ({})); +}; diff --git a/apps/api/src/xrpc/app/rocksky/player/playFile.ts b/apps/api/src/xrpc/app/rocksky/player/playFile.ts new file mode 100644 index 00000000..c4d03f31 --- /dev/null +++ b/apps/api/src/xrpc/app/rocksky/player/playFile.ts @@ -0,0 +1,57 @@ +import { HandlerAuth } from "@atproto/xrpc-server"; +import { Context } from "context"; +import { Effect, pipe } from "effect"; +import { Server } from "lexicon"; +import { QueryParams } from "lexicon/types/app/rocksky/player/playFile"; +import tables from "schema"; + +export default function (server: Server, ctx: Context) { + const playFile = (params, auth: HandlerAuth) => + pipe( + { + params, + ctx, + did: auth.credentials?.did, + }, + handlePlayFile, + Effect.flatMap(presentation), + Effect.retry({ times: 3 }), + Effect.timeout("10 seconds"), + Effect.catchAll((err) => { + console.error(err); + return Effect.succeed({}); + }) + ); + server.app.rocksky.player.playFile({ + auth: ctx.authVerifier, + handler: async ({ params, auth }) => { + const result = await Effect.runPromise(playFile(params, auth)); + }, + }); +} + +const handlePlayFile = ({ + params, + ctx, + did, +}: { + params: QueryParams; + ctx: Context; + did?: string; +}) => + Effect.tryPromise({ + try: async () => { + await ctx.db.transaction(async (tx) => { + await tx.select().from(tables.queueTracks).execute(); + // Logic to play the file would go here + }); + }, + catch: (err) => { + console.error(err); + return {}; + }, + }); + +const presentation = (): Effect.Effect<{}, never> => { + return Effect.sync(() => ({})); +}; diff --git a/apps/api/src/xrpc/app/rocksky/playlist/createPlaylist.ts b/apps/api/src/xrpc/app/rocksky/playlist/createPlaylist.ts new file mode 100644 index 00000000..9d048ef3 --- /dev/null +++ b/apps/api/src/xrpc/app/rocksky/playlist/createPlaylist.ts @@ -0,0 +1,54 @@ +import { HandlerAuth } from "@atproto/xrpc-server"; +import { Context } from "context"; +import { Effect, pipe } from "effect"; +import { Server } from "lexicon"; +import { QueryParams } from "lexicon/types/app/rocksky/playlist/createPlaylist"; +import tables from "schema"; + +export default function (server: Server, ctx: Context) { + const createPlaylist = (params, auth: HandlerAuth) => + pipe( + { + params, + ctx, + did: auth.credentials?.did, + }, + create, + Effect.flatMap(presentation), + Effect.retry({ times: 3 }), + Effect.timeout("10 seconds"), + Effect.catchAll((err) => { + console.error(err); + return Effect.succeed({}); + }) + ); + server.app.rocksky.playlist.createPlaylist({ + auth: ctx.authVerifier, + handler: async ({ params, auth }) => { + const result = await Effect.runPromise(createPlaylist(params, auth)); + }, + }); +} + +const create = ({ + params, + ctx, + did, +}: { + params: QueryParams; + ctx: Context; + did?: string; +}) => + Effect.tryPromise({ + try: async () => { + await ctx.db.select().from(tables.playlists).execute(); + }, + catch: (err) => { + console.error(err); + return {}; + }, + }); + +const presentation = (): Effect.Effect<{}, never> => { + return Effect.sync(() => ({})); +}; diff --git a/apps/api/src/xrpc/app/rocksky/playlist/insertDirectory.ts b/apps/api/src/xrpc/app/rocksky/playlist/insertDirectory.ts new file mode 100644 index 00000000..33e9f9fe --- /dev/null +++ b/apps/api/src/xrpc/app/rocksky/playlist/insertDirectory.ts @@ -0,0 +1,56 @@ +import { HandlerAuth } from "@atproto/xrpc-server"; +import { Context } from "context"; +import { Effect, pipe } from "effect"; +import { Server } from "lexicon"; +import { QueryParams } from "lexicon/types/app/rocksky/playlist/insertDirectory"; +import tables from "schema"; + +export default function (server: Server, ctx: Context) { + const insertDirectory = (params, auth: HandlerAuth) => + pipe( + { + params, + ctx, + did: auth.credentials?.did, + }, + insert, + Effect.flatMap(presentation), + Effect.retry({ times: 3 }), + Effect.timeout("10 seconds"), + Effect.catchAll((err) => { + console.error(err); + return Effect.succeed({}); + }) + ); + server.app.rocksky.playlist.insertDirectory({ + auth: ctx.authVerifier, + handler: async ({ params, auth }) => { + const result = await Effect.runPromise(insertDirectory(params, auth)); + }, + }); +} + +const insert = ({ + params, + ctx, + did, +}: { + params: QueryParams; + ctx: Context; + did?: string; +}) => + Effect.tryPromise({ + try: async () => { + await ctx.db.transaction(async (tx) => { + await tx.select().from(tables.playlists).execute(); + }); + }, + catch: (err) => { + console.error(err); + return {}; + }, + }); + +const presentation = (): Effect.Effect<{}, never> => { + return Effect.sync(() => ({})); +}; diff --git a/apps/api/src/xrpc/app/rocksky/playlist/insertFiles.ts b/apps/api/src/xrpc/app/rocksky/playlist/insertFiles.ts new file mode 100644 index 00000000..705b8e92 --- /dev/null +++ b/apps/api/src/xrpc/app/rocksky/playlist/insertFiles.ts @@ -0,0 +1,57 @@ +import { HandlerAuth } from "@atproto/xrpc-server"; +import { Context } from "context"; +import { Effect, pipe } from "effect"; +import { Server } from "lexicon"; +import { QueryParams } from "lexicon/types/app/rocksky/playlist/insertFiles"; +import tables from "schema"; + +export default function (server: Server, ctx: Context) { + const insertFiles = (params, auth: HandlerAuth) => + pipe( + { + params, + ctx, + did: auth.credentials?.did, + }, + insert, + Effect.flatMap(presentation), + Effect.retry({ times: 3 }), + Effect.timeout("10 seconds"), + Effect.catchAll((err) => { + console.error(err); + return Effect.succeed({}); + }) + ); + server.app.rocksky.playlist.insertFiles({ + auth: ctx.authVerifier, + handler: async ({ params, auth }) => { + const result = await Effect.runPromise(insertFiles(params, auth)); + }, + }); +} + +const insert = ({ + params, + ctx, + did, +}: { + params: QueryParams; + ctx: Context; + did?: string; +}) => + Effect.tryPromise({ + try: async () => { + await ctx.db.transaction(async (tx) => { + await tx.select().from(tables.playlists).execute(); + // Logic to insert files into the playlist would go here + }); + }, + catch: (err) => { + console.error(err); + return {}; + }, + }); + +const presentation = (): Effect.Effect<{}, never> => { + return Effect.sync(() => ({})); +}; diff --git a/apps/api/src/xrpc/app/rocksky/playlist/removePlaylist.ts b/apps/api/src/xrpc/app/rocksky/playlist/removePlaylist.ts new file mode 100644 index 00000000..ac3ea04a --- /dev/null +++ b/apps/api/src/xrpc/app/rocksky/playlist/removePlaylist.ts @@ -0,0 +1,55 @@ +import { HandlerAuth } from "@atproto/xrpc-server"; +import { Context } from "context"; +import { Effect, pipe } from "effect"; +import { Server } from "lexicon"; +import { QueryParams } from "lexicon/types/app/rocksky/playlist/removePlaylist"; +import tables from "schema"; + +export default function (server: Server, ctx: Context) { + const removePlaylist = (params, auth: HandlerAuth) => + pipe( + { + params, + ctx, + did: auth.credentials?.did, + }, + remove, + Effect.flatMap(presentation), + Effect.retry({ times: 3 }), + Effect.timeout("10 seconds"), + Effect.catchAll((err) => { + console.error(err); + return Effect.succeed({}); + }) + ); + server.app.rocksky.playlist.removePlaylist({ + auth: ctx.authVerifier, + handler: async ({ params, auth }) => { + const result = await Effect.runPromise(removePlaylist(params, auth)); + }, + }); +} + +const remove = ({ + params, + ctx, + did, +}: { + params: QueryParams; + ctx: Context; + did?: string; +}) => + Effect.tryPromise({ + try: async () => { + await ctx.db.select().from(tables.playlists).execute(); + // Logic to remove the playlist would go here + }, + catch: (err) => { + console.error(err); + return {}; + }, + }); + +const presentation = (): Effect.Effect<{}, never> => { + return Effect.sync(() => ({})); +}; diff --git a/apps/api/src/xrpc/app/rocksky/playlist/startPlaylist.ts b/apps/api/src/xrpc/app/rocksky/playlist/startPlaylist.ts new file mode 100644 index 00000000..6f15836c --- /dev/null +++ b/apps/api/src/xrpc/app/rocksky/playlist/startPlaylist.ts @@ -0,0 +1,57 @@ +import { HandlerAuth } from "@atproto/xrpc-server"; +import { Context } from "context"; +import { Effect, pipe } from "effect"; +import { Server } from "lexicon"; +import { QueryParams } from "lexicon/types/app/rocksky/playlist/startPlaylist"; +import tables from "schema"; + +export default function (server: Server, ctx: Context) { + const startPlaylist = (params, auth: HandlerAuth) => + pipe( + { + params, + ctx, + did: auth.credentials?.did, + }, + start, + Effect.flatMap(presentation), + Effect.retry({ times: 3 }), + Effect.timeout("10 seconds"), + Effect.catchAll((err) => { + console.error(err); + return Effect.succeed({}); + }) + ); + server.app.rocksky.playlist.startPlaylist({ + auth: ctx.authVerifier, + handler: async ({ params, auth }) => { + const result = await Effect.runPromise(startPlaylist(params, auth)); + }, + }); +} + +const start = ({ + params, + ctx, + did, +}: { + params: QueryParams; + ctx: Context; + did?: string; +}) => + Effect.tryPromise({ + try: async () => { + await ctx.db.transaction(async (tx) => { + await tx.select().from(tables.playlists).execute(); + // Logic to start the playlist would go here + }); + }, + catch: (err) => { + console.error(err); + return {}; + }, + }); + +const presentation = (): Effect.Effect<{}, never> => { + return Effect.sync(() => ({})); +}; diff --git a/apps/api/src/xrpc/index.ts b/apps/api/src/xrpc/index.ts index 4ece4d9c..ace21baf 100644 --- a/apps/api/src/xrpc/index.ts +++ b/apps/api/src/xrpc/index.ts @@ -1,8 +1,5 @@ import { Context } from "context"; import { Server } from "lexicon"; -import spotifyPause from "./app/rocksky//spotify/pause"; -import spotifyPlay from "./app/rocksky//spotify/play"; -import spotifySeek from "./app/rocksky//spotify/seek"; import getActorAlbums from "./app/rocksky/actor/getActorAlbums"; import getActorArtists from "./app/rocksky/actor/getActorArtists"; import getActorLovedSongs from "./app/rocksky/actor/getActorLovedSongs"; @@ -35,14 +32,23 @@ import dislikeShout from "./app/rocksky/like/dislikeShout"; import dislikeSong from "./app/rocksky/like/dislikeSong"; import likeShout from "./app/rocksky/like/likeShout"; import likeSong from "./app/rocksky/like/likeSong"; +import addItemsToQueue from "./app/rocksky/player/addItemsToQueue"; import getCurrentlyPlaying from "./app/rocksky/player/getCurrentlyPlaying"; +import getPlaybackQueue from "./app/rocksky/player/getPlaybackQueue"; import next from "./app/rocksky/player/next"; import pause from "./app/rocksky/player/pause"; import play from "./app/rocksky/player/play"; +import playDirectory from "./app/rocksky/player/playDirectory"; +import playFile from "./app/rocksky/player/playFile"; import previous from "./app/rocksky/player/previous"; import seek from "./app/rocksky/player/seek"; +import createPlaylist from "./app/rocksky/playlist/createPlaylist"; import getPlaylist from "./app/rocksky/playlist/getPlaylist"; import getPlaylists from "./app/rocksky/playlist/getPlaylists"; +import insertDirectory from "./app/rocksky/playlist/insertDirectory"; +import insertFiles from "./app/rocksky/playlist/insertFiles"; +import removePlaylist from "./app/rocksky/playlist/removePlaylist"; +import startPlaylist from "./app/rocksky/playlist/startPlaylist"; import createScrobble from "./app/rocksky/scrobble/createScrobble"; import getScrobble from "./app/rocksky/scrobble/getScrobble"; import getScrobbles from "./app/rocksky/scrobble/getScrobbles"; @@ -60,7 +66,10 @@ import getSong from "./app/rocksky/song/getSong"; import getSongs from "./app/rocksky/song/getSongs"; import spotifyGetCurrentlyPlaying from "./app/rocksky/spotify/getCurrentlyPlaying"; import spotifyNext from "./app/rocksky/spotify/next"; +import spotifyPause from "./app/rocksky/spotify/pause"; +import spotifyPlay from "./app/rocksky/spotify/play"; import spotifyPrevious from "./app/rocksky/spotify/previous"; +import spotifySeek from "./app/rocksky/spotify/seek"; import getStats from "./app/rocksky/stats/getStats"; export default function (server: Server, ctx: Context) { @@ -127,6 +136,15 @@ export default function (server: Server, ctx: Context) { spotifyPlay(server, ctx); getStats(server, ctx); createSong(server, ctx); + addItemsToQueue(server, ctx); + getPlaybackQueue(server, ctx); + playDirectory(server, ctx); + playFile(server, ctx); + createPlaylist(server, ctx); + insertDirectory(server, ctx); + insertFiles(server, ctx); + removePlaylist(server, ctx); + startPlaylist(server, ctx); return server; }