mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
320 lines
8.8 KiB
TypeScript
320 lines
8.8 KiB
TypeScript
/* eslint-disable no-await-in-loop */
|
|
import { ImageType, Server, ServerFolder, Task } from '@prisma/client';
|
|
import { prisma, throttle } from '../../lib/index';
|
|
import { uniqueArray } from '../../utils/index';
|
|
import { queue } from '../queues';
|
|
import { subsonicApi } from './subsonic.api';
|
|
import { subsonicUtils } from './subsonic.utils';
|
|
|
|
export const scanGenres = async (server: Server, task: Task) => {
|
|
await prisma.task.update({
|
|
data: { message: 'Scanning genres' },
|
|
where: { id: task.id },
|
|
});
|
|
|
|
const res = await subsonicApi.getGenres(server);
|
|
|
|
const genres = res.genres.genre.map((genre) => {
|
|
return { name: genre.value };
|
|
});
|
|
|
|
await prisma.genre.createMany({
|
|
data: genres,
|
|
skipDuplicates: true,
|
|
});
|
|
};
|
|
|
|
export const scanAlbumArtists = async (
|
|
server: Server,
|
|
serverFolder: ServerFolder,
|
|
task: Task
|
|
) => {
|
|
await prisma.task.update({
|
|
data: { message: 'Scanning artists' },
|
|
where: { id: task.id },
|
|
});
|
|
|
|
const artists = await subsonicApi.getArtists(server, serverFolder.remoteId);
|
|
|
|
for (const artist of artists) {
|
|
await prisma.albumArtist.upsert({
|
|
create: {
|
|
name: artist.name,
|
|
remoteId: artist.id,
|
|
serverFolders: { connect: { id: serverFolder.id } },
|
|
serverId: server.id,
|
|
sortName: artist.name,
|
|
},
|
|
update: {
|
|
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,
|
|
task: Task
|
|
) => {
|
|
await prisma.task.update({
|
|
data: { message: 'Scanning albums' },
|
|
where: { id: task.id },
|
|
});
|
|
|
|
const albums = await subsonicApi.getAlbums(server, {
|
|
musicFolderId: serverFolder.remoteId,
|
|
offset: 0,
|
|
size: 500,
|
|
type: 'newest',
|
|
});
|
|
|
|
await subsonicUtils.insertImages(albums);
|
|
|
|
for (const album of albums) {
|
|
const imagesConnect = album.coverArt
|
|
? {
|
|
uniqueImageId: {
|
|
remoteUrl: album.coverArt,
|
|
type: ImageType.PRIMARY,
|
|
},
|
|
}
|
|
: undefined;
|
|
|
|
const albumArtistConnect = album.artistId
|
|
? {
|
|
uniqueAlbumArtistId: {
|
|
remoteId: album.artistId,
|
|
serverId: server.id,
|
|
},
|
|
}
|
|
: undefined;
|
|
|
|
await prisma.album.upsert({
|
|
create: {
|
|
albumArtists: { connect: albumArtistConnect },
|
|
deleted: false,
|
|
genres: { connect: album.genre ? { name: album.genre } : undefined },
|
|
images: { connect: imagesConnect },
|
|
name: album.name,
|
|
releaseDate: album?.year
|
|
? new Date(Number(String(album.year).slice(4)), 0).toISOString()
|
|
: undefined,
|
|
releaseYear: album.year,
|
|
remoteCreatedAt: album.created,
|
|
remoteId: album.id,
|
|
serverFolders: { connect: { id: serverFolder.id } },
|
|
serverId: server.id,
|
|
sortName: album.name,
|
|
},
|
|
update: {
|
|
albumArtists: { connect: albumArtistConnect },
|
|
deleted: false,
|
|
genres: { connect: album.genre ? { name: album.genre } : undefined },
|
|
images: { connect: imagesConnect },
|
|
name: album.name,
|
|
releaseDate: album?.year
|
|
? new Date(Number(String(album.year).slice(4)), 0).toISOString()
|
|
: undefined,
|
|
releaseYear: album.year,
|
|
remoteCreatedAt: album.created,
|
|
remoteId: album.id,
|
|
serverFolders: { connect: { id: serverFolder.id } },
|
|
serverId: server.id,
|
|
sortName: album.name,
|
|
},
|
|
where: {
|
|
uniqueAlbumId: {
|
|
remoteId: album.id,
|
|
serverId: server.id,
|
|
},
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
const throttledAlbumFetch = throttle(
|
|
async (server: Server, serverFolder: ServerFolder, album: any) => {
|
|
const albumRes = await subsonicApi.getAlbum(server, album.remoteId);
|
|
|
|
if (albumRes) {
|
|
await subsonicUtils.insertSongImages(albumRes);
|
|
const songsUpsert = albumRes.album.song.map((song) => {
|
|
const genresConnect = song.genre ? { name: song.genre } : undefined;
|
|
|
|
const imagesConnect = song.coverArt
|
|
? {
|
|
uniqueImageId: {
|
|
remoteUrl: song.coverArt,
|
|
type: ImageType.PRIMARY,
|
|
},
|
|
}
|
|
: undefined;
|
|
|
|
const albumArtistsConnect = song.artistId
|
|
? {
|
|
uniqueAlbumArtistId: {
|
|
remoteId: song.artistId,
|
|
serverId: server.id,
|
|
},
|
|
}
|
|
: undefined;
|
|
|
|
return {
|
|
create: {
|
|
// albumArtistId: song.artistId ? song.artistId : undefined,
|
|
albumArtist: { connect: albumArtistsConnect },
|
|
artistName: !song.artistId ? song.artist : undefined,
|
|
bitRate: song.bitRate ? song.bitRate : undefined,
|
|
container: song.suffix,
|
|
deleted: false,
|
|
discNumber: song.discNumber,
|
|
duration: song.duration,
|
|
genres: { connect: genresConnect },
|
|
images: { connect: imagesConnect },
|
|
name: song.title,
|
|
releaseDate: song?.year
|
|
? new Date(Number(String(song.year).slice(4)), 0).toISOString()
|
|
: undefined,
|
|
releaseYear: song.year,
|
|
remoteCreatedAt: song.created,
|
|
remoteId: song.id,
|
|
server: { connect: { id: server.id } },
|
|
serverFolders: { connect: { id: serverFolder.id } },
|
|
// serverId: server.id,
|
|
size: song.size,
|
|
sortName: song.title,
|
|
trackNumber: song.track,
|
|
},
|
|
update: {
|
|
albumArtist: { connect: albumArtistsConnect },
|
|
// albumArtistId: song.artistId ? song.artistId : undefined,
|
|
artistName: !song.artistId ? song.artist : undefined,
|
|
|
|
bitRate: song.bitRate ? song.bitRate : undefined,
|
|
container: song.suffix,
|
|
deleted: false,
|
|
discNumber: song.discNumber,
|
|
duration: song.duration,
|
|
genres: { connect: genresConnect },
|
|
images: { connect: imagesConnect },
|
|
name: song.title,
|
|
releaseDate: song?.year
|
|
? new Date(Number(String(song.year).slice(4)), 0).toISOString()
|
|
: undefined,
|
|
releaseYear: song.year,
|
|
remoteCreatedAt: song.created,
|
|
remoteId: song.id,
|
|
server: { connect: { id: server.id } },
|
|
serverFolders: { connect: { id: serverFolder.id } },
|
|
|
|
// serverId: server.id,
|
|
size: song.size,
|
|
sortName: song.title,
|
|
trackNumber: song.track,
|
|
},
|
|
where: {
|
|
uniqueSongId: {
|
|
remoteId: song.id,
|
|
serverId: server.id,
|
|
},
|
|
},
|
|
};
|
|
});
|
|
|
|
const uniqueArtistIds = albumRes.album.song
|
|
.map((song) => song.artistId)
|
|
.filter(uniqueArray);
|
|
|
|
const artistsConnect = uniqueArtistIds.map((artistId) => {
|
|
return {
|
|
uniqueAlbumArtistId: {
|
|
remoteId: artistId!,
|
|
serverId: server.id,
|
|
},
|
|
};
|
|
});
|
|
|
|
await prisma.album.update({
|
|
data: {
|
|
// albumArtists: { connect: artistsConnect },
|
|
songs: { upsert: songsUpsert },
|
|
},
|
|
where: {
|
|
uniqueAlbumId: {
|
|
remoteId: album.remoteId,
|
|
serverId: server.id,
|
|
},
|
|
},
|
|
});
|
|
}
|
|
}
|
|
);
|
|
|
|
export const scanAlbumDetail = async (
|
|
server: Server,
|
|
serverFolder: ServerFolder,
|
|
task: Task
|
|
) => {
|
|
await prisma.task.update({
|
|
data: { message: 'Scanning songs' },
|
|
where: { id: task.id },
|
|
});
|
|
|
|
const promises = [];
|
|
const dbAlbums = await prisma.album.findMany({
|
|
where: {
|
|
serverId: server.id,
|
|
},
|
|
});
|
|
|
|
for (let i = 0; i < dbAlbums.length; i += 1) {
|
|
await throttledAlbumFetch(server, serverFolder, dbAlbums[i]);
|
|
}
|
|
};
|
|
|
|
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, task);
|
|
await scanAlbums(server, serverFolder, task);
|
|
await scanAlbumDetail(server, serverFolder, task);
|
|
|
|
await prisma.serverFolder.update({
|
|
data: { lastScannedAt: new Date() },
|
|
where: { id: serverFolder.id },
|
|
});
|
|
}
|
|
|
|
return { task };
|
|
},
|
|
id: task.id,
|
|
});
|
|
};
|
|
|
|
export const subsonicScanner = {
|
|
scanAll,
|
|
scanGenres,
|
|
};
|