diff --git a/packages/app/src/components/Searcher/index.jsx b/packages/app/src/components/Searcher/index.jsx
index 9ddbe3c0..bcfeae6d 100755
--- a/packages/app/src/components/Searcher/index.jsx
+++ b/packages/app/src/components/Searcher/index.jsx
@@ -184,12 +184,12 @@ const Searcher = (props) => {
if (typeof props.model === "function") {
result = await props.model(value, {
...props.modelParams,
- limit_per_section: app.isMobile ? 3 : 5,
+ limit: app.isMobile ? 3 : 5,
})
} else {
result = await SearchModel.search(value, {
...props.modelParams,
- limit_per_section: app.isMobile ? 3 : 5,
+ limit: app.isMobile ? 3 : 5,
})
}
diff --git a/packages/app/src/components/Searcher/index.less b/packages/app/src/components/Searcher/index.less
index d0acd1bc..4df6276f 100755
--- a/packages/app/src/components/Searcher/index.less
+++ b/packages/app/src/components/Searcher/index.less
@@ -90,7 +90,6 @@ html {
align-items: center;
- border: 0;
padding: 0;
margin: 0;
@@ -101,6 +100,7 @@ html {
padding: 0 10px;
background-color: var(--background-color-primary);
+ border: 3px solid var(--border-color) !important;
.ant-input-prefix {
font-size: 2rem;
@@ -127,6 +127,9 @@ html {
flex-wrap: wrap;
width: 100%;
+ height: fit-content;
+ max-height: 70vh;
+
gap: 10px;
padding: 20px;
@@ -137,6 +140,7 @@ html {
color: var(--text-color);
background-color: var(--background-color-primary);
+ border: 3px solid var(--border-color);
.ant-result,
.ant-result-title,
diff --git a/packages/app/src/pages/music/list/[id]/index.jsx b/packages/app/src/pages/music/list/[id]/index.jsx
index 88a56eb3..19e4d5b5 100644
--- a/packages/app/src/pages/music/list/[id]/index.jsx
+++ b/packages/app/src/pages/music/list/[id]/index.jsx
@@ -8,11 +8,19 @@ import MusicService from "@models/music"
import "./index.less"
const ListView = (props) => {
- const { type, id } = props.params
+ const { id } = props.params
- const [loading, result, error, makeRequest] = app.cores.api.useRequest(
+ const query = new URLSearchParams(window.location.search)
+ const type = query.get("type")
+ const service = query.get("service")
+
+ const [loading, result, error] = app.cores.api.useRequest(
MusicService.getReleaseData,
id,
+ {
+ type: type,
+ service: service,
+ },
)
if (error) {
@@ -29,6 +37,8 @@ const ListView = (props) => {
return
}
+ console.log(result)
+
return (
{
useUrlQuery
renderResults={false}
model={async (keywords, params) =>
- SearchModel.search(keywords, params, ["tracks"])
+ SearchModel.search(keywords, params, [
+ "tracks",
+ "albums",
+ "artists",
+ ])
}
onSearchResult={props.setSearchResults}
onEmpty={() => props.setSearchResults(false)}
diff --git a/packages/app/src/pages/music/tabs/explore/components/SearchResults/index.jsx b/packages/app/src/pages/music/tabs/explore/components/SearchResults/index.jsx
index 5abf8880..d97d81b7 100644
--- a/packages/app/src/pages/music/tabs/explore/components/SearchResults/index.jsx
+++ b/packages/app/src/pages/music/tabs/explore/components/SearchResults/index.jsx
@@ -8,11 +8,11 @@ import MusicTrack from "@components/Music/Track"
import Playlist from "@components/Music/Playlist"
const ResultGroupsDecorators = {
- playlists: {
- icon: "MdPlaylistPlay",
- label: "Playlists",
+ albums: {
+ icon: "MdAlbum",
+ label: "Albums",
renderItem: (props) => {
- return
+ return
},
},
tracks: {
@@ -23,7 +23,6 @@ const ResultGroupsDecorators = {
app.cores.player.start(props.item)}
onClick={() => app.location.push(`/play/${props.item._id}`)}
/>
)
@@ -40,6 +39,10 @@ const SearchResults = ({ data }) => {
// filter out groups with no items array property
groupsKeys = groupsKeys.filter((key) => {
+ if (!data[key]) {
+ return false
+ }
+
if (!Array.isArray(data[key].items)) {
return false
}
diff --git a/packages/server/classes/SegmentedAudioMPDJob/index.js b/packages/server/classes/SegmentedAudioMPDJob/index.js
index d6e75bc3..bd295747 100644
--- a/packages/server/classes/SegmentedAudioMPDJob/index.js
+++ b/packages/server/classes/SegmentedAudioMPDJob/index.js
@@ -3,6 +3,10 @@ import path from "node:path"
import { FFMPEGLib, Utils } from "../FFMPEGLib"
+const codecOverrides = {
+ wav: "flac",
+}
+
export default class SegmentedAudioMPDJob extends FFMPEGLib {
constructor(params = {}) {
super()
@@ -26,11 +30,11 @@ export default class SegmentedAudioMPDJob extends FFMPEGLib {
`-c:a ${this.params.audioCodec}`,
`-map 0:a`,
`-f dash`,
- `-dash_segment_type mp4`,
`-segment_time ${this.params.segmentTime}`,
`-use_template 1`,
`-use_timeline 1`,
- `-init_seg_name "init.m4s"`,
+ //`-dash_segment_type mp4`,
+ //`-init_seg_name "init.m4s"`,
]
if (this.params.includeMetadata === false) {
@@ -89,25 +93,69 @@ export default class SegmentedAudioMPDJob extends FFMPEGLib {
}
}
+ _updateMpdBandwidthAndSamplingRate = async ({
+ mpdPath,
+ bandwidth,
+ samplingRate,
+ } = {}) => {
+ try {
+ let mpdContent = await fs.promises.readFile(mpdPath, "utf-8")
+
+ // Regex to find all tags
+ const representationRegex = /(]*)(>)/g
+
+ mpdContent = mpdContent.replace(
+ representationRegex,
+ (match, startTag, endTag) => {
+ // Remove existing bandwidth and audioSamplingRate attributes if present
+ let newTag = startTag
+ .replace(/\sbandwidth="[^"]*"/, "")
+ .replace(/\saudioSamplingRate="[^"]*"/, "")
+
+ // Add new attributes
+ newTag += ` bandwidth="${bandwidth}" audioSamplingRate="${samplingRate}"`
+
+ return newTag + endTag
+ },
+ )
+
+ await fs.promises.writeFile(mpdPath, mpdContent, "utf-8")
+ } catch (error) {
+ console.error(
+ `[SegmentedAudioMPDJob] Error updating MPD bandwidth/audioSamplingRate for ${mpdPath}:`,
+ error,
+ )
+ }
+ }
+
run = async () => {
- const segmentationCmd = this.buildSegmentationArgs()
const outputPath =
this.params.outputDir ?? `${path.dirname(this.params.input)}/dash`
const outputFile = path.join(outputPath, this.params.outputMasterName)
- this.emit("start", {
- input: this.params.input,
- output: outputPath,
- params: this.params,
- })
-
- if (!fs.existsSync(outputPath)) {
- fs.mkdirSync(outputPath, { recursive: true })
- }
-
try {
+ this.emit("start", {
+ input: this.params.input,
+ output: outputPath,
+ params: this.params,
+ })
+
const inputProbe = await Utils.probe(this.params.input)
+ if (
+ this.params.audioCodec === "copy" &&
+ codecOverrides[inputProbe.format.format_name]
+ ) {
+ this.params.audioCodec =
+ codecOverrides[inputProbe.format.format_name]
+ }
+
+ const segmentationCmd = this.buildSegmentationArgs()
+
+ if (!fs.existsSync(outputPath)) {
+ fs.mkdirSync(outputPath, { recursive: true })
+ }
+
const ffmpegResult = await this.ffmpeg({
args: segmentationCmd,
onProcess: (process) => {
@@ -135,6 +183,29 @@ export default class SegmentedAudioMPDJob extends FFMPEGLib {
let outputProbe = await Utils.probe(outputFile)
+ let bandwidth = null
+ let samplingRate = null
+
+ if (
+ outputProbe &&
+ outputProbe.streams &&
+ outputProbe.streams.length > 0
+ ) {
+ bandwidth =
+ outputProbe.format.bit_rate ??
+ outputProbe.streams[0].bit_rate
+
+ samplingRate = outputProbe.streams[0].sample_rate
+ }
+
+ if (bandwidth && samplingRate) {
+ await this._updateMpdBandwidthAndSamplingRate({
+ mpdPath: outputFile,
+ bandwidth: bandwidth,
+ samplingRate: samplingRate,
+ })
+ }
+
this.emit("end", {
probe: {
input: inputProbe,
diff --git a/packages/server/classes/SyncRoomManager/index.js b/packages/server/classes/SyncRoomManager/index.js
new file mode 100644
index 00000000..0e5419d5
--- /dev/null
+++ b/packages/server/classes/SyncRoomManager/index.js
@@ -0,0 +1,35 @@
+export class SyncRoom {
+ constructor(ownerSocket) {
+ this.ownerSocket = ownerSocket
+ }
+
+ id = global.nanoid()
+
+ buffer = new Set()
+ members = new Set()
+
+ push = async (data) => {
+ if (this.buffer.size > 5) {
+ this.buffer.delete(this.buffer.keys().next().value)
+ }
+
+ this.buffer.add(data)
+
+ for (const socket of this.members) {
+ socket.emit(`syncroom:push`, data)
+ }
+ }
+
+ join = (socket) => {
+ this.members.add(socket)
+
+ // send the latest buffer
+ socket.emit("syncroom.buffer", this.buffer[0])
+ }
+
+ leave = (socket) => {
+ this.members.delete(socket)
+ }
+}
+
+export default class SyncRoomManager {}
diff --git a/packages/server/classes/Transformation/handlers/a-dash.js b/packages/server/classes/Transformation/handlers/a-dash.js
index b286bc45..bd5074d0 100644
--- a/packages/server/classes/Transformation/handlers/a-dash.js
+++ b/packages/server/classes/Transformation/handlers/a-dash.js
@@ -2,7 +2,7 @@ import path from "node:path"
import SegmentedAudioMPDJob from "@shared-classes/SegmentedAudioMPDJob"
export default async ({ filePath, workPath, onProgress }) => {
- return new Promise(async (resolve, reject) => {
+ return new Promise((resolve) => {
const outputDir = path.resolve(workPath, "a-dash")
const job = new SegmentedAudioMPDJob({
@@ -10,7 +10,7 @@ export default async ({ filePath, workPath, onProgress }) => {
outputDir: outputDir,
// set to default as raw flac
- audioCodec: "flac",
+ audioCodec: "copy",
audioBitrate: "default",
audioSampleRate: "default",
})
diff --git a/packages/server/services/music/classes/release/index.js b/packages/server/services/music/classes/release/index.js
index 32d1f9e3..027337a9 100644
--- a/packages/server/services/music/classes/release/index.js
+++ b/packages/server/services/music/classes/release/index.js
@@ -130,7 +130,7 @@ export default class Release {
const items = release.items ?? release.list
- const items_ids = items.map((item) => item._id.toString())
+ const items_ids = items.map((item) => item._id ?? item)
// delete all releated tracks
await Track.deleteMany({
diff --git a/packages/server/services/music/classes/track/methods/get.js b/packages/server/services/music/classes/track/methods/get.js
index e3f327ec..26111c50 100644
--- a/packages/server/services/music/classes/track/methods/get.js
+++ b/packages/server/services/music/classes/track/methods/get.js
@@ -6,12 +6,12 @@ async function fullfillData(list, { user_id = null }) {
list = [list]
}
- const trackIds = list.map((track) => {
- return track._id
- })
-
// if user_id is provided, fetch likes
if (user_id) {
+ const trackIds = list.map((track) => {
+ return track._id
+ })
+
const tracksLikes = await Library.isFavorite(
user_id,
trackIds,
@@ -32,21 +32,15 @@ async function fullfillData(list, { user_id = null }) {
})
list = await Promise.all(list)
+ } else {
+ list = list.map((track) => {
+ delete track.source
+ delete track.publisher
+
+ return track
+ })
}
- // process some metadata
- list = list.map(async (track) => {
- if (track.metadata) {
- if (track.metadata.bitrate && track.metadata.bitrate > 9000) {
- track.metadata.lossless = true
- }
- }
-
- return track
- })
-
- list = await Promise.all(list)
-
return list
}
diff --git a/packages/server/services/music/music.service.js b/packages/server/services/music/music.service.js
index 15f1fec2..e2df3f83 100755
--- a/packages/server/services/music/music.service.js
+++ b/packages/server/services/music/music.service.js
@@ -7,12 +7,20 @@ import RedisClient from "@shared-classes/RedisClient"
import SharedMiddlewares from "@shared-middlewares"
import LimitsClass from "@shared-classes/Limits"
+import InjectedAuth from "@shared-lib/injectedAuth"
+
export default class API extends Server {
static refName = "music"
- static useEngine = "hyper-express-ng"
- static enableWebsockets = true
- static routesPath = `${__dirname}/routes`
- static listen_port = process.env.HTTP_LISTEN_PORT ?? 3003
+ static listenPort = process.env.HTTP_LISTEN_PORT ?? 3003
+
+ static websockets = {
+ enabled: true,
+ path: "/music",
+ }
+
+ static bypassCors = true
+
+ static useMiddlewares = ["logs"]
middlewares = {
...SharedMiddlewares,
@@ -24,9 +32,28 @@ export default class API extends Server {
redis: RedisClient(),
}
+ handleWsUpgrade = async (context, token, res) => {
+ if (!token) {
+ return res.upgrade(context)
+ }
+
+ context = await InjectedAuth(context, token, res).catch(() => {
+ res.close(401, "Failed to verify auth token")
+ return false
+ })
+
+ if (!context || !context.user) {
+ res.close(401, "Unauthorized or missing auth token")
+ return false
+ }
+
+ return res.upgrade(context)
+ }
+
async onInitialize() {
global.sse = this.contexts.SSEManager
global.redis = this.contexts.redis.client
+ global.syncRoomLyrics = new Map()
await this.contexts.db.initialize()
await this.contexts.redis.initialize()
diff --git a/packages/server/services/music/package.json b/packages/server/services/music/package.json
index 07e7f330..0bdd36bb 100755
--- a/packages/server/services/music/package.json
+++ b/packages/server/services/music/package.json
@@ -1,3 +1,6 @@
{
- "name": "music"
+ "name": "music",
+ "dependencies": {
+ "linebridge": "^1.0.0-alpha.4"
+ }
}
diff --git a/packages/server/services/music/routes/music/my/library/favorite/get.js b/packages/server/services/music/routes/music/my/library/favorite/get.js
index d7037ef8..7cbdfe8b 100644
--- a/packages/server/services/music/routes/music/my/library/favorite/get.js
+++ b/packages/server/services/music/routes/music/my/library/favorite/get.js
@@ -1,7 +1,7 @@
import Library from "@classes/library"
export default {
- middlewares: ["withAuthentication"],
+ useMiddlewares: ["withAuthentication"],
fn: async (req) => {
const { kind, item_id } = req.query
diff --git a/packages/server/services/music/routes/music/my/library/favorite/put.js b/packages/server/services/music/routes/music/my/library/favorite/put.js
index 937d84c4..b1a59ac4 100644
--- a/packages/server/services/music/routes/music/my/library/favorite/put.js
+++ b/packages/server/services/music/routes/music/my/library/favorite/put.js
@@ -1,7 +1,7 @@
import Library from "@classes/library"
export default {
- middlewares: ["withAuthentication"],
+ useMiddlewares: ["withAuthentication"],
fn: async (req) => {
const { kind, item_id, to } = req.body
diff --git a/packages/server/services/music/routes/music/my/library/get.js b/packages/server/services/music/routes/music/my/library/get.js
index a073ce2f..7c3cd688 100644
--- a/packages/server/services/music/routes/music/my/library/get.js
+++ b/packages/server/services/music/routes/music/my/library/get.js
@@ -1,7 +1,7 @@
import Library from "@classes/library"
export default {
- middlewares: ["withAuthentication"],
+ useMiddlewares: ["withAuthentication"],
fn: async (req) => {
const userId = req.auth.session.user_id
const { limit = 50, offset = 0, kind } = req.query
diff --git a/packages/server/services/music/routes/music/my/releases/get.js b/packages/server/services/music/routes/music/my/releases/get.js
index 4c696b69..505e09c2 100644
--- a/packages/server/services/music/routes/music/my/releases/get.js
+++ b/packages/server/services/music/routes/music/my/releases/get.js
@@ -1,7 +1,7 @@
import { MusicRelease, Track } from "@db_models"
export default {
- middlewares: ["withAuthentication"],
+ useMiddlewares: ["withAuthentication"],
fn: async (req) => {
const { keywords, limit = 10, offset = 0 } = req.query
diff --git a/packages/server/services/music/routes/music/recently/get.js b/packages/server/services/music/routes/music/recently/get.js
index eb7fc0d5..e57b2fe1 100644
--- a/packages/server/services/music/routes/music/recently/get.js
+++ b/packages/server/services/music/routes/music/recently/get.js
@@ -3,7 +3,7 @@ import { RecentActivity } from "@db_models"
import TrackClass from "@classes/track"
export default {
- middlewares: ["withAuthentication"],
+ useMiddlewares: ["withAuthentication"],
fn: async (req, res) => {
const user_id = req.auth.session.user_id
diff --git a/packages/server/services/music/routes/music/releases/[release_id]/data/get.js b/packages/server/services/music/routes/music/releases/[release_id]/data/get.js
index 4e0caca5..b352dd18 100644
--- a/packages/server/services/music/routes/music/releases/[release_id]/data/get.js
+++ b/packages/server/services/music/routes/music/releases/[release_id]/data/get.js
@@ -1,7 +1,7 @@
import ReleaseClass from "@classes/release"
export default {
- middlewares: ["withOptionalAuthentication"],
+ useMiddlewares: ["withOptionalAuthentication"],
fn: async (req) => {
const { release_id } = req.params
const { limit = 50, offset = 0 } = req.query
diff --git a/packages/server/services/music/routes/music/releases/[release_id]/delete.js b/packages/server/services/music/routes/music/releases/[release_id]/delete.js
index 30a40b86..37ad34ba 100644
--- a/packages/server/services/music/routes/music/releases/[release_id]/delete.js
+++ b/packages/server/services/music/routes/music/releases/[release_id]/delete.js
@@ -1,7 +1,7 @@
import ReleaseClass from "@classes/release"
export default {
- middlewares: ["withAuthentication"],
+ useMiddlewares: ["withAuthentication"],
fn: async (req) => {
return await ReleaseClass.delete(req.params.release_id, {
user_id: req.auth.session.user_id,
diff --git a/packages/server/services/music/routes/music/releases/put.js b/packages/server/services/music/routes/music/releases/put.js
index dcb91418..60103835 100644
--- a/packages/server/services/music/routes/music/releases/put.js
+++ b/packages/server/services/music/routes/music/releases/put.js
@@ -1,18 +1,18 @@
import ReleaseClass from "@classes/release"
export default {
- middlewares: ["withAuthentication"],
- fn: async (req) => {
- if (req.body._id) {
- return await ReleaseClass.update(req.body._id, {
- ...req.body,
- user_id: req.auth.session.user_id,
- })
- } else {
- return await ReleaseClass.create({
- ...req.body,
- user_id: req.auth.session.user_id,
- })
- }
- }
-}
\ No newline at end of file
+ useMiddlewares: ["withAuthentication"],
+ fn: async (req) => {
+ if (req.body._id) {
+ return await ReleaseClass.update(req.body._id, {
+ ...req.body,
+ user_id: req.auth.session.user_id,
+ })
+ } else {
+ return await ReleaseClass.create({
+ ...req.body,
+ user_id: req.auth.session.user_id,
+ })
+ }
+ },
+}
diff --git a/packages/server/services/music/routes/music/tracks/[track_id]/data/get.js b/packages/server/services/music/routes/music/tracks/[track_id]/data/get.js
index 327a0516..b7b4f6c0 100644
--- a/packages/server/services/music/routes/music/tracks/[track_id]/data/get.js
+++ b/packages/server/services/music/routes/music/tracks/[track_id]/data/get.js
@@ -1,15 +1,15 @@
import TrackClass from "@classes/track"
export default {
- middlewares: ["withOptionalAuthentication"],
- fn: async (req) => {
- const { track_id } = req.params
- const user_id = req.auth?.session?.user_id
+ useMiddlewares: ["withOptionalAuthentication"],
+ fn: async (req) => {
+ const { track_id } = req.params
+ const user_id = req.auth?.session?.user_id
- const track = await TrackClass.get(track_id, {
- user_id
- })
+ const track = await TrackClass.get(track_id, {
+ user_id,
+ })
- return track
- }
-}
\ No newline at end of file
+ return track
+ },
+}
diff --git a/packages/server/services/music/routes/music/tracks/[track_id]/delete.js b/packages/server/services/music/routes/music/tracks/[track_id]/delete.js
index 5c14506f..9102e572 100644
--- a/packages/server/services/music/routes/music/tracks/[track_id]/delete.js
+++ b/packages/server/services/music/routes/music/tracks/[track_id]/delete.js
@@ -1,21 +1,21 @@
import TrackClass from "@classes/track"
export default {
- middlewares: ["withAuthentication"],
- fn: async (req) => {
- const { track_id } = req.params
+ useMiddlewares: ["withAuthentication"],
+ fn: async (req) => {
+ const { track_id } = req.params
- const track = await TrackClass.get(track_id)
+ const track = await TrackClass.get(track_id)
- if (track.publisher.user_id !== req.auth.session.user_id) {
- throw new Error("Forbidden, you don't own this track")
- }
+ if (track.publisher.user_id !== req.auth.session.user_id) {
+ throw new Error("Forbidden, you don't own this track")
+ }
- await TrackClass.delete(track_id)
+ await TrackClass.delete(track_id)
- return {
- success: true,
- track: track,
- }
- }
-}
\ No newline at end of file
+ return {
+ success: true,
+ track: track,
+ }
+ },
+}
diff --git a/packages/server/services/music/routes/music/tracks/[track_id]/lyrics/put.js b/packages/server/services/music/routes/music/tracks/[track_id]/lyrics/put.js
index 97288a21..2e917b84 100644
--- a/packages/server/services/music/routes/music/tracks/[track_id]/lyrics/put.js
+++ b/packages/server/services/music/routes/music/tracks/[track_id]/lyrics/put.js
@@ -1,66 +1,66 @@
import { TrackLyric, Track } from "@db_models"
export default {
- middlewares: ["withAuthentication"],
- fn: async (req) => {
- const { track_id } = req.params
- const { video_source, lrc, sync_audio_at } = req.body
+ useMiddlewares: ["withAuthentication"],
+ fn: async (req) => {
+ const { track_id } = req.params
+ const { video_source, lrc, sync_audio_at } = req.body
- // check if track exists
- let track = await Track.findById(track_id).catch(() => null)
+ // check if track exists
+ let track = await Track.findById(track_id).catch(() => null)
- if (!track) {
- throw new OperationError(404, "Track not found")
- }
+ if (!track) {
+ throw new OperationError(404, "Track not found")
+ }
- if (track.publisher.user_id !== req.auth.session.user_id) {
- throw new OperationError(403, "Unauthorized")
- }
+ if (track.publisher.user_id !== req.auth.session.user_id) {
+ throw new OperationError(403, "Unauthorized")
+ }
- console.log(`Setting lyrics for track ${track_id} >`, {
- track_id: track_id,
- video_source: video_source,
- lrc: lrc,
- })
+ console.log(`Setting lyrics for track ${track_id} >`, {
+ track_id: track_id,
+ video_source: video_source,
+ lrc: lrc,
+ })
- // check if trackLyric exists
- let trackLyric = await TrackLyric.findOne({
- track_id: track_id
- })
+ // check if trackLyric exists
+ let trackLyric = await TrackLyric.findOne({
+ track_id: track_id,
+ })
- // if trackLyric exists, update it, else create it
- if (!trackLyric) {
- trackLyric = new TrackLyric({
- track_id: track_id,
- video_source: video_source,
- lrc: lrc,
- sync_audio_at: sync_audio_at,
- })
+ // if trackLyric exists, update it, else create it
+ if (!trackLyric) {
+ trackLyric = new TrackLyric({
+ track_id: track_id,
+ video_source: video_source,
+ lrc: lrc,
+ sync_audio_at: sync_audio_at,
+ })
- await trackLyric.save()
- } else {
- const update = Object()
+ await trackLyric.save()
+ } else {
+ const update = Object()
- if (typeof video_source !== "undefined") {
- update.video_source = video_source
- }
+ if (typeof video_source !== "undefined") {
+ update.video_source = video_source
+ }
- if (typeof lrc !== "undefined") {
- update.lrc = lrc
- }
+ if (typeof lrc !== "undefined") {
+ update.lrc = lrc
+ }
- if (typeof sync_audio_at !== "undefined") {
- update.sync_audio_at = sync_audio_at
- }
+ if (typeof sync_audio_at !== "undefined") {
+ update.sync_audio_at = sync_audio_at
+ }
- trackLyric = await TrackLyric.findOneAndUpdate(
- {
- track_id: track_id,
- },
- update,
- )
- }
+ trackLyric = await TrackLyric.findOneAndUpdate(
+ {
+ track_id: track_id,
+ },
+ update,
+ )
+ }
- return trackLyric
- }
-}
\ No newline at end of file
+ return trackLyric
+ },
+}
diff --git a/packages/server/services/music/routes/music/tracks/[track_id]/override/put.js b/packages/server/services/music/routes/music/tracks/[track_id]/override/put.js
index 7f22fe7b..c30e53e4 100644
--- a/packages/server/services/music/routes/music/tracks/[track_id]/override/put.js
+++ b/packages/server/services/music/routes/music/tracks/[track_id]/override/put.js
@@ -1,36 +1,36 @@
import { TrackOverride } from "@db_models"
export default {
- middlewares: ["withAuthentication", "onlyAdmin"],
- fn: async (req) => {
- const { track_id } = req.params
- const { service, override } = req.body
+ useMiddlewares: ["withAuthentication", "onlyAdmin"],
+ fn: async (req) => {
+ const { track_id } = req.params
+ const { service, override } = req.body
- let trackOverride = await TrackOverride.findOne({
- track_id: track_id,
- service: service,
- }).catch(() => null)
+ let trackOverride = await TrackOverride.findOne({
+ track_id: track_id,
+ service: service,
+ }).catch(() => null)
- if (!trackOverride) {
- trackOverride = new TrackOverride({
- track_id: track_id,
- service: service,
- override: override,
- })
+ if (!trackOverride) {
+ trackOverride = new TrackOverride({
+ track_id: track_id,
+ service: service,
+ override: override,
+ })
- await trackOverride.save()
- } else {
- trackOverride = await TrackOverride.findOneAndUpdate(
- {
- track_id: track_id,
- service: service,
- },
- {
- override: override,
- },
- )
- }
+ await trackOverride.save()
+ } else {
+ trackOverride = await TrackOverride.findOneAndUpdate(
+ {
+ track_id: track_id,
+ service: service,
+ },
+ {
+ override: override,
+ },
+ )
+ }
- return trackOverride.override
- }
-}
\ No newline at end of file
+ return trackOverride.override
+ },
+}
diff --git a/packages/server/services/music/routes/music/tracks/get.js b/packages/server/services/music/routes/music/tracks/get.js
index 3dda149a..c51d2b82 100644
--- a/packages/server/services/music/routes/music/tracks/get.js
+++ b/packages/server/services/music/routes/music/tracks/get.js
@@ -19,6 +19,7 @@ export default async (req) => {
const items = await Track.find(query)
.limit(limit)
+ .select("-source -publisher -public")
.skip(trim)
.sort({ _id: -1 })
diff --git a/packages/server/services/music/routes/music/tracks/put.js b/packages/server/services/music/routes/music/tracks/put.js
index 415bb16a..f35a5879 100644
--- a/packages/server/services/music/routes/music/tracks/put.js
+++ b/packages/server/services/music/routes/music/tracks/put.js
@@ -2,7 +2,7 @@ import requiredFields from "@shared-utils/requiredFields"
import TrackClass from "@classes/track"
export default {
- middlewares: ["withAuthentication"],
+ useMiddlewares: ["withAuthentication"],
fn: async (req) => {
if (Array.isArray(req.body.items)) {
let results = []
diff --git a/packages/server/services/music/ws_routes/sync_room/join.js b/packages/server/services/music/ws_routes/sync_room/join.js
new file mode 100644
index 00000000..a3a09f8b
--- /dev/null
+++ b/packages/server/services/music/ws_routes/sync_room/join.js
@@ -0,0 +1,13 @@
+import leave from "./leave"
+
+export default async (client, user_id) => {
+ console.log(`[SYNC-ROOM] Join ${client.userId} -> ${user_id}`)
+
+ if (client.syncroom) {
+ await leave(client, client.syncroom)
+ }
+
+ // subscribe to stream topic
+ await client.subscribe(`syncroom/${user_id}`)
+ client.syncroom = user_id
+}
diff --git a/packages/server/services/music/ws_routes/sync_room/leave.js b/packages/server/services/music/ws_routes/sync_room/leave.js
new file mode 100644
index 00000000..fa0dbfab
--- /dev/null
+++ b/packages/server/services/music/ws_routes/sync_room/leave.js
@@ -0,0 +1,8 @@
+export default async (client, user_id) => {
+ console.log(`[SYNC-ROOM] Leave ${client.userId} -> ${user_id}`)
+
+ // unsubscribe from sync topic
+ await client.unsubscribe(`syncroom/${user_id}`)
+
+ client.syncroom = null
+}
diff --git a/packages/server/services/music/ws_routes/sync_room/push.js b/packages/server/services/music/ws_routes/sync_room/push.js
new file mode 100644
index 00000000..e9f52f08
--- /dev/null
+++ b/packages/server/services/music/ws_routes/sync_room/push.js
@@ -0,0 +1,7 @@
+export default async (client, payload) => {
+ console.log(`[SYNC-ROOM] Pushing to sync ${client.userId}`, payload)
+
+ const roomId = `syncroom/${client.userId}`
+
+ global.websockets.senders.toTopic(roomId, "sync:receive", payload)
+}
diff --git a/packages/server/services/music/ws_routes/sync_room/push_lyrics.js b/packages/server/services/music/ws_routes/sync_room/push_lyrics.js
new file mode 100644
index 00000000..47bdf09c
--- /dev/null
+++ b/packages/server/services/music/ws_routes/sync_room/push_lyrics.js
@@ -0,0 +1,14 @@
+export default async (client, payload) => {
+ console.log(`[SYNC-ROOM] Pushing lyrics to sync ${client.userId}`)
+
+ const roomId = `syncroom/${client.userId}`
+
+ if (!payload) {
+ // delete lyrics
+ global.syncRoomLyrics.delete(client.userId)
+ } else {
+ global.syncRoomLyrics.set(client.userId, payload)
+ }
+
+ global.websockets.senders.toTopic(roomId, "sync:lyrics:receive", payload)
+}
diff --git a/packages/server/services/music/ws_routes/sync_room/request_lyrics.js b/packages/server/services/music/ws_routes/sync_room/request_lyrics.js
new file mode 100644
index 00000000..1b51b7de
--- /dev/null
+++ b/packages/server/services/music/ws_routes/sync_room/request_lyrics.js
@@ -0,0 +1,5 @@
+export default async (client) => {
+ console.log(`[SYNC-ROOM] Requesting lyrics of room ${client.syncroom}`)
+
+ return global.syncRoomLyrics.get(client.syncroom)
+}