Skip to main content
Glama

Ableton Copilot MCP

by xiaolaa2
snapshot-utils.ts4.97 kB
import { logger } from '../main.js' import { getOperationHistoryRepository, getSnapshotRepository } from '../db.js' import { OperationHistory, OperationStatus } from '../entities/OperationHistory.js' import { NoteSnapshotData, Snapshot, SnapshotType } from '../entities/Snapshot.js' import { getClipById, isNoteExtendedArray } from './obj-utils.js' import { FindOneOptions } from 'typeorm' import { removeAllNotes, setNotesExtended } from './clip-utils.js' import { ErrorTypes } from '../mcp/error-handler.js' import { Note } from 'ableton-js/util/note.js' export async function createOperationHistory( operation: Omit<OperationHistory, 'id' | 'createdAt'> ): Promise<number> { const repo = getOperationHistoryRepository() try { const newHistory = repo.create(operation) const savedHistory = await repo.save(newHistory) return savedHistory.id } catch (err) { logger.error('Failed to create operation history', err) throw err } } export async function updateOperationHistoryById( id: number, operation: Partial<Omit<OperationHistory, 'id' | 'createdAt'>> ): Promise<void> { const repo = getOperationHistoryRepository() try { const result = await repo.update(id, operation) if (result.affected === 0) { logger.warn(`Operation history with id ${id} not found for update.`) } } catch (err) { logger.error('Failed to update operation history', err) throw err } } export async function getOperationHistoriesPage(page: number, pageSize: number): Promise<OperationHistory[]> { const repo = getOperationHistoryRepository() try { return await repo.find({ order: { createdAt: 'DESC' }, skip: (page - 1) * pageSize, take: pageSize, }) } catch (err) { logger.error('Failed to get operation histories', err) throw err } } export async function getOperationHistoryById(id: number): Promise<OperationHistory | null> { const repo = getOperationHistoryRepository() try { return await repo.findOneBy({ id }) } catch (err) { logger.error('Failed to get operation history by id', err) throw err } } export async function createSnapshot( snapshotData: Omit<Snapshot, 'id' | 'createdAt' | 'updatedAt'> ): Promise<number> { const repo = getSnapshotRepository() try { const newSnapshot = repo.create(snapshotData) const savedSnapshot = await repo.save(newSnapshot) return savedSnapshot.id } catch (err) { logger.error('Failed to create snapshot', err) throw err } } export async function getSnapShotByHistoryId(historyId: number): Promise<Snapshot | null> { const repo = getSnapshotRepository() try { const options: FindOneOptions<Snapshot> = { where: { history_id: historyId }, } return await repo.findOne(options) } catch (err) { logger.error('Failed to get snapshot by history_id', err) throw err } } export async function rollbackByHistoryId(historyId: number): Promise<void> { const history = await getOperationHistoryById(historyId) if (!history) { throw ErrorTypes.NOT_FOUND(`Operation history not found for historyId: ${historyId}`) } history.status = typeof history.status !== 'number' ? Number(history.status) : history.status if (history.status !== OperationStatus.SUCCESS) { throw ErrorTypes.INTERNAL_ERROR(`Operation history with id ${historyId} is not successful, cannot be rolled back.`) } const snapshot = await getSnapShotByHistoryId(historyId) if (!snapshot) { throw ErrorTypes.NOT_FOUND(`Snapshot not found for historyId: ${historyId}`) } if (!snapshot.snapshot_data) { throw ErrorTypes.INTERNAL_ERROR(`Snapshot data is empty for historyId: ${historyId}`) } switch (snapshot.snapshot_type) { case SnapshotType.NOTE: { const noteData = JSON.parse(snapshot.snapshot_data) as NoteSnapshotData await rollbackNoteSnapshot(noteData) break } default: throw ErrorTypes.INTERNAL_ERROR('Unsupported snapshot type') } } async function rollbackNoteSnapshot(snapshot_data: NoteSnapshotData): Promise<void> { const { clip_id, notes } = snapshot_data const clip = getClipById(clip_id) if (!clip) { throw ErrorTypes.NOT_FOUND(`Clip with id ${clip_id} not found during rollback.`) } if (!notes) { throw ErrorTypes.INTERNAL_ERROR(`Notes data is empty for clipId: ${clip_id}`) } await removeAllNotes(clip) // Since there is no set_notes_extend method, we need to use set_notes and apply_note_modifications instead if (isNoteExtendedArray(notes)) { await setNotesExtended(clip, notes) } else { await clip.setNotes(notes as Note[]) } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/xiaolaa2/ableton-copilot-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server