mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-16 21:50:35 +02:00
Move server directory outside of frontend src
This commit is contained in:
@@ -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,
|
||||
};
|
||||
Reference in New Issue
Block a user