mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-13 20:10:07 +02:00
Move server directory outside of frontend src
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
import { navidromeApi } from './navidrome.api';
|
||||
import { navidromeScanner } from './navidrome.scanner';
|
||||
|
||||
export const navidrome = {
|
||||
api: navidromeApi,
|
||||
scanner: navidromeScanner,
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
import { Server } from '@prisma/client';
|
||||
import axios from 'axios';
|
||||
import {
|
||||
NDAlbumListResponse,
|
||||
NDGenreListResponse,
|
||||
NDAlbumListParams,
|
||||
NDGenreListParams,
|
||||
NDSongListParams,
|
||||
NDSongListResponse,
|
||||
NDArtistListResponse,
|
||||
NDAuthenticate,
|
||||
} from './navidrome.types';
|
||||
|
||||
const api = axios.create();
|
||||
|
||||
const authenticate = async (options: {
|
||||
password: string;
|
||||
url: string;
|
||||
username: string;
|
||||
}) => {
|
||||
const { password, url, username } = options;
|
||||
const cleanServerUrl = url.replace(/\/$/, '');
|
||||
|
||||
const { data } = await api.post<NDAuthenticate>(
|
||||
`${cleanServerUrl}/auth/login`,
|
||||
{ password, username }
|
||||
);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
const getGenres = async (server: Server, params?: NDGenreListParams) => {
|
||||
const { data } = await api.get<NDGenreListResponse>(
|
||||
`${server.url}/api/genre`,
|
||||
{
|
||||
headers: { 'x-nd-authorization': `Bearer ${server.token}` },
|
||||
params,
|
||||
}
|
||||
);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
const getArtists = async (server: Server, params?: NDGenreListParams) => {
|
||||
const { data } = await api.get<NDArtistListResponse>(
|
||||
`${server.url}/api/artist`,
|
||||
{
|
||||
headers: { 'x-nd-authorization': `Bearer ${server.token}` },
|
||||
params,
|
||||
}
|
||||
);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
const getAlbums = async (server: Server, params?: NDAlbumListParams) => {
|
||||
const { data } = await api.get<NDAlbumListResponse>(
|
||||
`${server.url}/api/album`,
|
||||
{
|
||||
headers: { 'x-nd-authorization': `Bearer ${server.token}` },
|
||||
params,
|
||||
}
|
||||
);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
const getSongs = async (server: Server, params?: NDSongListParams) => {
|
||||
const { data } = await api.get<NDSongListResponse>(`${server.url}/api/song`, {
|
||||
headers: { 'x-nd-authorization': `Bearer ${server.token}` },
|
||||
params,
|
||||
});
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
export const navidromeApi = {
|
||||
authenticate,
|
||||
getAlbums,
|
||||
getArtists,
|
||||
getGenres,
|
||||
getSongs,
|
||||
};
|
||||
@@ -0,0 +1,376 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import {
|
||||
ExternalSource,
|
||||
ExternalType,
|
||||
Folder,
|
||||
ImageType,
|
||||
Server,
|
||||
ServerFolder,
|
||||
Task,
|
||||
} from '@prisma/client';
|
||||
import uniqBy from 'lodash/uniqBy';
|
||||
import { prisma } from '@lib/prisma';
|
||||
import { groupByProperty } from '@utils/group-by-property';
|
||||
import { queue } from '../queues/index';
|
||||
import { navidromeApi } from './navidrome.api';
|
||||
import { navidromeUtils } from './navidrome.utils';
|
||||
|
||||
const CHUNK_SIZE = 5000;
|
||||
|
||||
export const scanGenres = async (server: Server, task: Task) => {
|
||||
await prisma.task.update({
|
||||
data: { message: 'Scanning genres' },
|
||||
where: { id: task.id },
|
||||
});
|
||||
|
||||
const res = await navidromeApi.getGenres(server);
|
||||
|
||||
const genres = res.map((genre) => {
|
||||
return { name: genre.name };
|
||||
});
|
||||
|
||||
await prisma.genre.createMany({
|
||||
data: genres,
|
||||
skipDuplicates: true,
|
||||
});
|
||||
};
|
||||
|
||||
export const scanAlbumArtists = async (
|
||||
server: Server,
|
||||
serverFolder: ServerFolder
|
||||
) => {
|
||||
const artists = await navidromeApi.getArtists(server);
|
||||
|
||||
const externalsCreateMany = artists
|
||||
.filter((artist) => artist.mbzArtistId)
|
||||
.map((artist) => ({
|
||||
source: ExternalSource.MUSICBRAINZ,
|
||||
type: ExternalType.ID,
|
||||
value: artist.mbzArtistId,
|
||||
}));
|
||||
|
||||
await prisma.external.createMany({
|
||||
data: externalsCreateMany,
|
||||
skipDuplicates: true,
|
||||
});
|
||||
|
||||
for (const artist of artists) {
|
||||
const genresConnect = artist.genres
|
||||
? artist.genres.map((genre) => ({ name: genre.name }))
|
||||
: undefined;
|
||||
|
||||
const externalsConnect = artist.mbzArtistId
|
||||
? {
|
||||
uniqueExternalId: {
|
||||
source: ExternalSource.MUSICBRAINZ,
|
||||
value: artist.mbzArtistId,
|
||||
},
|
||||
}
|
||||
: undefined;
|
||||
|
||||
await prisma.albumArtist.upsert({
|
||||
create: {
|
||||
deleted: false,
|
||||
externals: { connect: externalsConnect },
|
||||
genres: { connect: genresConnect },
|
||||
name: artist.name,
|
||||
remoteId: artist.id,
|
||||
serverFolders: { connect: { id: serverFolder.id } },
|
||||
serverId: server.id,
|
||||
sortName: artist.name,
|
||||
},
|
||||
update: {
|
||||
deleted: false,
|
||||
externals: { connect: externalsConnect },
|
||||
genres: { connect: genresConnect },
|
||||
name: artist.name,
|
||||
remoteId: artist.id,
|
||||
serverFolders: { connect: { id: serverFolder.id } },
|
||||
serverId: server.id,
|
||||
sortName: artist.name,
|
||||
},
|
||||
where: {
|
||||
uniqueAlbumArtistId: {
|
||||
remoteId: artist.id,
|
||||
serverId: server.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const scanAlbums = async (
|
||||
server: Server,
|
||||
serverFolder: ServerFolder
|
||||
) => {
|
||||
let start = 0;
|
||||
let count = 5000;
|
||||
do {
|
||||
const albums = await navidromeApi.getAlbums(server, {
|
||||
_end: start + CHUNK_SIZE,
|
||||
_start: start,
|
||||
});
|
||||
|
||||
const imagesCreateMany = albums
|
||||
.filter((album) => album.coverArtId)
|
||||
.map((album) => ({
|
||||
remoteUrl: album.coverArtId,
|
||||
type: ImageType.PRIMARY,
|
||||
}));
|
||||
|
||||
await prisma.image.createMany({
|
||||
data: imagesCreateMany,
|
||||
skipDuplicates: true,
|
||||
});
|
||||
|
||||
const artistIds = (
|
||||
await prisma.artist.findMany({
|
||||
select: { remoteId: true },
|
||||
where: { serverId: server.id },
|
||||
})
|
||||
).map((artist) => artist.remoteId);
|
||||
|
||||
for (const album of albums) {
|
||||
const imagesConnect = album.coverArtId
|
||||
? {
|
||||
uniqueImageId: {
|
||||
remoteUrl: album.coverArtId,
|
||||
type: ImageType.PRIMARY,
|
||||
},
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const genresConnect = album.genres
|
||||
? album.genres.map((genre) => ({ name: genre.name }))
|
||||
: undefined;
|
||||
|
||||
const validArtistIds = [];
|
||||
const ndArtistIds = album.allArtistIds.split(' ');
|
||||
|
||||
for (const artistId of ndArtistIds) {
|
||||
if (artistIds.includes(artistId)) {
|
||||
validArtistIds.push(artistId);
|
||||
}
|
||||
}
|
||||
|
||||
const artistsConnect = validArtistIds.map((id) => ({
|
||||
uniqueArtistId: {
|
||||
remoteId: id,
|
||||
serverId: server.id,
|
||||
},
|
||||
}));
|
||||
|
||||
const albumArtistConnect = album.artistId
|
||||
? {
|
||||
uniqueAlbumArtistId: {
|
||||
remoteId: album.artistId,
|
||||
serverId: server.id,
|
||||
},
|
||||
}
|
||||
: undefined;
|
||||
|
||||
await prisma.album.upsert({
|
||||
create: {
|
||||
albumArtists: { connect: albumArtistConnect },
|
||||
artists: { connect: artistsConnect },
|
||||
deleted: false,
|
||||
genres: { connect: genresConnect },
|
||||
images: { connect: imagesConnect },
|
||||
name: album.name,
|
||||
releaseDate: album?.minYear
|
||||
? new Date(album.minYear, 0).toISOString()
|
||||
: undefined,
|
||||
releaseYear: album.minYear,
|
||||
remoteCreatedAt: album.createdAt,
|
||||
remoteId: album.id,
|
||||
serverFolders: { connect: { id: serverFolder.id } },
|
||||
serverId: server.id,
|
||||
sortName: album.name,
|
||||
},
|
||||
update: {
|
||||
albumArtists: { connect: albumArtistConnect },
|
||||
artists: { connect: artistsConnect },
|
||||
deleted: false,
|
||||
genres: { connect: genresConnect },
|
||||
images: { connect: imagesConnect },
|
||||
name: album.name,
|
||||
releaseDate: album?.minYear
|
||||
? new Date(album.minYear, 0).toISOString()
|
||||
: undefined,
|
||||
releaseYear: album.minYear,
|
||||
remoteCreatedAt: album.createdAt,
|
||||
remoteId: album.id,
|
||||
serverFolders: { connect: { id: serverFolder.id } },
|
||||
serverId: server.id,
|
||||
sortName: album.name,
|
||||
},
|
||||
where: {
|
||||
uniqueAlbumId: {
|
||||
remoteId: album.id,
|
||||
serverId: server.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
start += CHUNK_SIZE;
|
||||
count = albums.length;
|
||||
} while (count === CHUNK_SIZE);
|
||||
};
|
||||
|
||||
const scanSongs = async (server: Server, serverFolder: ServerFolder) => {
|
||||
let start = 0;
|
||||
let count = 5000;
|
||||
do {
|
||||
const songs = await navidromeApi.getSongs(server, {
|
||||
_end: start + CHUNK_SIZE,
|
||||
_start: start,
|
||||
});
|
||||
|
||||
const externalsCreateMany = [];
|
||||
const genresCreateMany = [];
|
||||
for (const song of songs) {
|
||||
if (song.mbzTrackId) {
|
||||
externalsCreateMany.push({
|
||||
source: ExternalSource.MUSICBRAINZ,
|
||||
type: ExternalType.ID,
|
||||
value: song.mbzTrackId,
|
||||
});
|
||||
}
|
||||
|
||||
if (song.genres?.length > 0) {
|
||||
genresCreateMany.push(
|
||||
...song.genres.map((genre) => ({ name: genre.name }))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.external.createMany({
|
||||
data: externalsCreateMany,
|
||||
skipDuplicates: true,
|
||||
});
|
||||
|
||||
await prisma.genre.createMany({
|
||||
data: genresCreateMany,
|
||||
skipDuplicates: true,
|
||||
});
|
||||
|
||||
const folderGroups = songs.map((song) => {
|
||||
const songPaths = song.path.split('/');
|
||||
const paths = [];
|
||||
for (let b = 0; b < songPaths.length - 1; b += 1) {
|
||||
paths.push({
|
||||
name: songPaths[b],
|
||||
path: songPaths.slice(0, b + 1).join('/'),
|
||||
});
|
||||
}
|
||||
|
||||
return paths;
|
||||
});
|
||||
|
||||
const uniqueFolders = uniqBy(
|
||||
folderGroups.flatMap((folder) => folder).filter((f) => f.path !== ''),
|
||||
'path'
|
||||
);
|
||||
|
||||
const createdFolders: Folder[] = [];
|
||||
for (const folder of uniqueFolders) {
|
||||
const createdFolder = await prisma.folder.upsert({
|
||||
create: {
|
||||
name: folder.name,
|
||||
path: folder.path,
|
||||
serverFolders: {
|
||||
connect: {
|
||||
uniqueServerFolderId: {
|
||||
remoteId: serverFolder.remoteId,
|
||||
serverId: server.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
serverId: server.id,
|
||||
},
|
||||
update: {
|
||||
name: folder.name,
|
||||
path: folder.path,
|
||||
serverFolders: {
|
||||
connect: {
|
||||
uniqueServerFolderId: {
|
||||
remoteId: serverFolder.remoteId,
|
||||
serverId: server.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
uniqueFolderId: {
|
||||
path: folder.path,
|
||||
serverId: server.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
createdFolders.push(createdFolder);
|
||||
}
|
||||
|
||||
for (const folder of createdFolders) {
|
||||
if (folder.parentId) break;
|
||||
|
||||
const pathSplit = folder.path.split('/');
|
||||
const parentPath = pathSplit.slice(0, pathSplit.length - 1).join('/');
|
||||
|
||||
const parentPathData = createdFolders.find(
|
||||
(save) => save.path === parentPath
|
||||
);
|
||||
|
||||
if (parentPathData) {
|
||||
await prisma.folder.update({
|
||||
data: {
|
||||
parentId: parentPathData.id,
|
||||
},
|
||||
where: { id: folder.id },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const albumSongGroups = groupByProperty(songs, 'albumId');
|
||||
const albumIds = Object.keys(albumSongGroups);
|
||||
|
||||
for (const id of albumIds) {
|
||||
const songGroup = albumSongGroups[id];
|
||||
await navidromeUtils.insertSongGroup(server, serverFolder, songGroup, id);
|
||||
}
|
||||
|
||||
start += CHUNK_SIZE;
|
||||
count = songs.length;
|
||||
} while (count === CHUNK_SIZE);
|
||||
};
|
||||
|
||||
const scanAll = async (
|
||||
server: Server,
|
||||
serverFolders: ServerFolder[],
|
||||
task: Task
|
||||
) => {
|
||||
queue.scanner.push({
|
||||
fn: async () => {
|
||||
await prisma.task.update({
|
||||
data: { message: 'Beginning scan...' },
|
||||
where: { id: task.id },
|
||||
});
|
||||
|
||||
for (const serverFolder of serverFolders) {
|
||||
await scanGenres(server, task);
|
||||
await scanAlbumArtists(server, serverFolder);
|
||||
await scanAlbums(server, serverFolder);
|
||||
await scanSongs(server, serverFolder);
|
||||
}
|
||||
|
||||
return { task };
|
||||
},
|
||||
id: task.id,
|
||||
});
|
||||
};
|
||||
|
||||
export const navidromeScanner = {
|
||||
scanAll,
|
||||
scanGenres,
|
||||
};
|
||||
@@ -0,0 +1,169 @@
|
||||
export type NDAuthenticate = {
|
||||
id: string;
|
||||
isAdmin: boolean;
|
||||
name: string;
|
||||
subsonicSalt: string;
|
||||
subsonicToken: string;
|
||||
token: string;
|
||||
username: string;
|
||||
};
|
||||
|
||||
export type NDGenre = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type NDAlbum = {
|
||||
albumArtist: string;
|
||||
albumArtistId: string;
|
||||
allArtistIds: string;
|
||||
artist: string;
|
||||
artistId: string;
|
||||
compilation: boolean;
|
||||
coverArtId: string;
|
||||
coverArtPath: string;
|
||||
createdAt: string;
|
||||
duration: number;
|
||||
fullText: string;
|
||||
genre: string;
|
||||
genres: NDGenre[];
|
||||
id: string;
|
||||
maxYear: number;
|
||||
mbzAlbumArtistId: string;
|
||||
mbzAlbumId: string;
|
||||
minYear: number;
|
||||
name: string;
|
||||
orderAlbumArtistName: string;
|
||||
orderAlbumName: string;
|
||||
playCount: number;
|
||||
playDate: string;
|
||||
rating: number;
|
||||
size: number;
|
||||
songCount: number;
|
||||
sortAlbumArtistName: string;
|
||||
sortArtistName: string;
|
||||
starred: boolean;
|
||||
starredAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export type NDSong = {
|
||||
album: string;
|
||||
albumArtist: string;
|
||||
albumArtistId: string;
|
||||
albumId: string;
|
||||
artist: string;
|
||||
artistId: string;
|
||||
bitRate: number;
|
||||
bookmarkPosition: number;
|
||||
channels: number;
|
||||
compilation: boolean;
|
||||
createdAt: string;
|
||||
discNumber: number;
|
||||
duration: number;
|
||||
fullText: string;
|
||||
genre: string;
|
||||
genres: NDGenre[];
|
||||
hasCoverArt: boolean;
|
||||
id: string;
|
||||
mbzAlbumArtistId: string;
|
||||
mbzAlbumId: string;
|
||||
mbzArtistId: string;
|
||||
mbzTrackId: string;
|
||||
orderAlbumArtistName: string;
|
||||
orderAlbumName: string;
|
||||
orderArtistName: string;
|
||||
orderTitle: string;
|
||||
path: string;
|
||||
playCount: number;
|
||||
playDate: string;
|
||||
rating: number;
|
||||
size: number;
|
||||
sortAlbumArtistName: string;
|
||||
sortArtistName: string;
|
||||
starred: boolean;
|
||||
starredAt: string;
|
||||
suffix: string;
|
||||
title: string;
|
||||
trackNumber: number;
|
||||
updatedAt: string;
|
||||
year: number;
|
||||
};
|
||||
|
||||
export type NDArtist = {
|
||||
albumCount: number;
|
||||
biography: string;
|
||||
externalInfoUpdatedAt: string;
|
||||
externalUrl: string;
|
||||
fullText: string;
|
||||
genres: NDGenre[];
|
||||
id: string;
|
||||
largeImageUrl: string;
|
||||
mbzArtistId: string;
|
||||
mediumImageUrl: string;
|
||||
name: string;
|
||||
orderArtistName: string;
|
||||
playCount: number;
|
||||
playDate: string;
|
||||
rating: number;
|
||||
size: number;
|
||||
smallImageUrl: string;
|
||||
songCount: number;
|
||||
starred: boolean;
|
||||
starredAt: string;
|
||||
};
|
||||
|
||||
export type NDGenreListResponse = NDGenre[];
|
||||
|
||||
export type NDAlbumListResponse = NDAlbum[];
|
||||
|
||||
export type NDSongListResponse = NDSong[];
|
||||
|
||||
export type NDArtistListResponse = NDArtist[];
|
||||
|
||||
export type NDPagination = {
|
||||
_end?: number;
|
||||
_start?: number;
|
||||
};
|
||||
|
||||
export type NDOrder = {
|
||||
_order?: 'ASC' | 'DESC';
|
||||
};
|
||||
|
||||
export enum NDGenreSort {
|
||||
NAME = 'name',
|
||||
}
|
||||
|
||||
export type NDGenreListParams = {
|
||||
_sort?: NDGenreSort;
|
||||
id?: string;
|
||||
} & NDPagination &
|
||||
NDOrder;
|
||||
|
||||
export enum NDAlbumSort {
|
||||
ARTIST = 'artist',
|
||||
MAX_YEAR = 'max_year',
|
||||
NAME = 'name',
|
||||
RANDOM = 'random',
|
||||
RECENTLY_ADDED = 'recently_added',
|
||||
}
|
||||
|
||||
export type NDAlbumListParams = {
|
||||
_sort?: NDAlbumSort;
|
||||
artist_id?: string;
|
||||
compilation?: boolean;
|
||||
genre_id?: string;
|
||||
has_rating?: boolean;
|
||||
id?: string;
|
||||
name?: string;
|
||||
recently_played?: boolean;
|
||||
starred?: boolean;
|
||||
year?: number;
|
||||
} & NDPagination &
|
||||
NDOrder;
|
||||
|
||||
export type NDSongListParams = {
|
||||
genre_id?: string;
|
||||
starred?: boolean;
|
||||
} & NDPagination &
|
||||
NDOrder;
|
||||
@@ -0,0 +1,125 @@
|
||||
import { ExternalSource, Server, ServerFolder } from '@prisma/client';
|
||||
import { prisma } from '@lib/prisma';
|
||||
import { NDSong } from './navidrome.types';
|
||||
|
||||
const insertSongGroup = async (
|
||||
server: Server,
|
||||
serverFolder: ServerFolder,
|
||||
songs: NDSong[],
|
||||
remoteAlbumId: string
|
||||
) => {
|
||||
const songsWithArtistIds = songs.filter((song) => song.artistId);
|
||||
const artistId =
|
||||
songsWithArtistIds.length > 0 ? songsWithArtistIds[0].artistId : undefined;
|
||||
|
||||
const albumArtist = artistId
|
||||
? await prisma.albumArtist.findUnique({
|
||||
where: {
|
||||
uniqueAlbumArtistId: {
|
||||
remoteId: artistId,
|
||||
serverId: server.id,
|
||||
},
|
||||
},
|
||||
})
|
||||
: undefined;
|
||||
|
||||
const songsUpsert = songs.map((song) => {
|
||||
const genresConnect = song.genres
|
||||
? song.genres.map((genre) => ({ name: genre.name }))
|
||||
: undefined;
|
||||
|
||||
const externalsConnect = song.mbzTrackId
|
||||
? {
|
||||
uniqueExternalId: {
|
||||
source: ExternalSource.MUSICBRAINZ,
|
||||
value: song.mbzTrackId,
|
||||
},
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const pathSplit = song.path.split('/');
|
||||
const parentPath = pathSplit.slice(0, pathSplit.length - 1).join('/');
|
||||
|
||||
return {
|
||||
create: {
|
||||
albumArtistId: albumArtist?.id,
|
||||
artistName: !song.artistId ? song.artist : undefined,
|
||||
bitRate: song.bitRate,
|
||||
container: song.suffix,
|
||||
deleted: false,
|
||||
discNumber: song.discNumber,
|
||||
duration: song.duration,
|
||||
externals: { connect: externalsConnect },
|
||||
folders: {
|
||||
connect: {
|
||||
uniqueFolderId: { path: parentPath, serverId: server.id },
|
||||
},
|
||||
},
|
||||
genres: { connect: genresConnect },
|
||||
name: song.title,
|
||||
releaseDate: song?.year
|
||||
? new Date(song.year, 0).toISOString()
|
||||
: undefined,
|
||||
releaseYear: song?.year,
|
||||
remoteCreatedAt: song.createdAt,
|
||||
remoteId: song.id,
|
||||
serverFolders: { connect: { id: serverFolder.id } },
|
||||
serverId: server.id,
|
||||
size: song.size,
|
||||
sortName: song.title,
|
||||
trackNumber: song.trackNumber,
|
||||
},
|
||||
update: {
|
||||
albumArtistId: albumArtist?.id,
|
||||
artistName: !song.artistId ? song.artist : undefined,
|
||||
bitRate: song.bitRate,
|
||||
container: song.suffix,
|
||||
deleted: false,
|
||||
discNumber: song.discNumber,
|
||||
duration: song.duration,
|
||||
externals: { connect: externalsConnect },
|
||||
folders: {
|
||||
connect: {
|
||||
uniqueFolderId: { path: parentPath, serverId: server.id },
|
||||
},
|
||||
},
|
||||
genres: { connect: genresConnect },
|
||||
name: song.title,
|
||||
releaseDate: song?.year
|
||||
? new Date(song.year, 0).toISOString()
|
||||
: undefined,
|
||||
releaseYear: song?.year,
|
||||
remoteCreatedAt: song.createdAt,
|
||||
remoteId: song.id,
|
||||
serverFolders: { connect: { id: serverFolder.id } },
|
||||
serverId: server.id,
|
||||
size: song.size,
|
||||
sortName: song.title,
|
||||
trackNumber: song.trackNumber,
|
||||
},
|
||||
where: {
|
||||
uniqueSongId: {
|
||||
remoteId: song.id,
|
||||
serverId: server.id,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
await prisma.album.update({
|
||||
data: {
|
||||
deleted: false,
|
||||
songs: { upsert: songsUpsert },
|
||||
},
|
||||
where: {
|
||||
uniqueAlbumId: {
|
||||
remoteId: remoteAlbumId,
|
||||
serverId: server.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const navidromeUtils = {
|
||||
insertSongGroup,
|
||||
};
|
||||
Reference in New Issue
Block a user