move track manifest & track instance classes to player core

This commit is contained in:
SrGooglo 2025-02-05 02:34:55 +00:00
parent 913e9b067c
commit b4f1282ba5
3 changed files with 263 additions and 146 deletions

View File

@ -1,133 +0,0 @@
import jsmediatags from "jsmediatags/dist/jsmediatags.min.js"
import { FastAverageColor } from "fast-average-color"
import MusicService from "@models/music"
export default class TrackManifest {
constructor(params) {
this.params = params
this.uid = params.uid ?? params._id
this._id = params._id
if (typeof params.cover !== "undefined") {
this.cover = params.cover
}
if (typeof params.title !== "undefined") {
this.title = params.title
}
if (typeof params.album !== "undefined") {
this.album = params.album
}
if (typeof params.artist !== "undefined") {
this.artist = params.artist
}
if (typeof params.artists !== "undefined" || Array.isArray(params.artists)) {
this.artistStr = params.artists.join(", ")
}
if (typeof params.source !== "undefined") {
this.source = params.source
}
if (typeof params.metadata !== "undefined") {
this.metadata = params.metadata
}
if (typeof params.lyrics_enabled !== "undefined") {
this.lyrics_enabled = params.lyrics_enabled
}
return this
}
_id = null // used for api requests
uid = null // used for internal
cover = "https://storage.ragestudio.net/comty-static-assets/default_song.png"
title = "Untitled"
album = "Unknown"
artist = "Unknown"
source = null
metadata = null
// Extended from db
lyrics_enabled = false
liked = null
async initialize() {
if (this.params.file) {
this.metadata = await this.analyzeMetadata(this.params.file.originFileObj)
if (this.metadata.tags) {
if (this.metadata.tags.title) {
this.title = this.metadata.tags.title
}
if (this.metadata.tags.artist) {
this.artist = this.metadata.tags.artist
}
if (this.metadata.tags.album) {
this.album = this.metadata.tags.album
}
if (this.metadata.tags.picture) {
this.cover = app.cores.remoteStorage.binaryArrayToFile(this.metadata.tags.picture, "cover")
const coverUpload = await app.cores.remoteStorage.uploadFile(this.cover)
this.cover = coverUpload.url
}
this.handleChanges({
cover: this.cover,
title: this.title,
artist: this.artist,
album: this.album,
})
}
}
return this
}
handleChanges = (changes) => {
if (typeof this.params.onChange === "function") {
this.params.onChange(this.uid, changes)
}
}
analyzeMetadata = async (file) => {
return new Promise((resolve, reject) => {
jsmediatags.read(file, {
onSuccess: (data) => {
return resolve(data)
},
onError: (error) => {
return reject(error)
}
})
})
}
analyzeCoverColor = async () => {
const fac = new FastAverageColor()
this.cover_analysis = await fac.getColorAsync(this.cover)
return this
}
fetchLikeStatus = async () => {
if (!this._id) {
return null
}
return await MusicService.isItemFavourited("track", this._id)
}
}

View File

@ -1,4 +1,5 @@
import TrackManifest from "../TrackManifest" import TrackManifest from "./TrackManifest"
import { MediaPlayer } from "dashjs"
export default class TrackInstance { export default class TrackInstance {
constructor(player, manifest) { constructor(player, manifest) {
@ -61,9 +62,9 @@ export default class TrackInstance {
"pause": () => { "pause": () => {
this.player.state.playback_status = "paused" this.player.state.playback_status = "paused"
}, },
// "durationchange": (duration) => { "durationchange": () => {
this.player.eventBus.emit(`player.durationchange`, this.audio.duration)
// }, },
"waiting": () => { "waiting": () => {
if (this.waitUpdateTimeout) { if (this.waitUpdateTimeout) {
clearTimeout(this.waitUpdateTimeout) clearTimeout(this.waitUpdateTimeout)
@ -83,12 +84,22 @@ export default class TrackInstance {
initialize = async () => { initialize = async () => {
this.manifest = await this.resolveManifest() this.manifest = await this.resolveManifest()
this.audio = new Audio(this.manifest.source) this.audio = new Audio()
this.audio.signal = this.abortController.signal this.audio.signal = this.abortController.signal
this.audio.crossOrigin = "anonymous" this.audio.crossOrigin = "anonymous"
this.audio.preload = "metadata" this.audio.preload = "metadata"
// support for dash audio streaming
if (this.manifest.source.endsWith(".mpd")) {
this.muxerPlayer = MediaPlayer().create()
this.muxerPlayer.initialize(this.audio, null, false)
this.muxerPlayer.attachSource(this.manifest.source)
} else {
this.audio.src = this.manifest.source
}
for (const [key, value] of Object.entries(this.mediaEvents)) { for (const [key, value] of Object.entries(this.mediaEvents)) {
this.audio.addEventListener(key, value) this.audio.addEventListener(key, value)
} }
@ -101,7 +112,13 @@ export default class TrackInstance {
} }
stop = () => { stop = () => {
this.audio.pause() if (this.audio) {
this.audio.pause()
}
if (this.muxerPlayer) {
this.muxerPlayer.destroy()
}
const lastProcessor = this.attachedProcessors[this.attachedProcessors.length - 1] const lastProcessor = this.attachedProcessors[this.attachedProcessors.length - 1]
@ -119,18 +136,22 @@ export default class TrackInstance {
} }
} }
this.manifest = new TrackManifest(this.manifest) this.manifest = new TrackManifest(this.manifest, {
serviceProviders: this.player.serviceProviders,
this.manifest = await this.manifest.analyzeCoverColor() })
if (this.manifest.service) { if (this.manifest.service) {
if (!this.player.service_providers.has(manifest.service)) { if (!this.player.serviceProviders.has(this.manifest.service)) {
throw new Error(`Service ${manifest.service} is not supported`) throw new Error(`Service ${this.manifest.service} is not supported`)
} }
// try to resolve source file // try to resolve source file
if (this.manifest.service !== "inherit" && !this.manifest.source) { if (!this.manifest.source) {
this.manifest = await this.player.service_providers.resolve(this.manifest.service, this.manifest) console.log("Resolving manifest cause no source defined")
this.manifest = await this.player.serviceProviders.resolve(this.manifest.service, this.manifest)
console.log("Manifest resolved", this.manifest)
} }
} }
@ -148,6 +169,21 @@ export default class TrackInstance {
this.manifest.metadata.title = this.manifest.source.split("/").pop() this.manifest.metadata.title = this.manifest.source.split("/").pop()
} }
// check if has overrides
const override = await this.manifest.serviceOperations.fetchOverride()
if (override) {
console.log(`Override found for track ${this.manifest._id}`, override)
this.manifest.overrides = override
}
// FIXME: idk why this is here, move somewhere else
// try {
// this.manifest = await this.manifest.analyzeCoverColor()
// } catch (error) {
// //x
// }
return this.manifest return this.manifest
} }
} }

View File

@ -0,0 +1,214 @@
import jsmediatags from "jsmediatags/dist/jsmediatags.min.js"
import { FastAverageColor } from "fast-average-color"
export default class TrackManifest {
constructor(params, ctx) {
this.params = params
this.ctx = ctx
this.uid = params.uid ?? params._id
this._id = params._id
if (typeof params.service !== "undefined") {
this.service = params.service
}
if (typeof params.overrides !== "undefined") {
this.overrides = params.overrides
}
if (typeof params.cover !== "undefined") {
this.cover = params.cover
}
if (typeof params.title !== "undefined") {
this.title = params.title
}
if (typeof params.album !== "undefined") {
this.album = params.album
}
if (typeof params.artist !== "undefined") {
this.artist = params.artist
}
if (
typeof params.artists !== "undefined" ||
Array.isArray(params.artists)
) {
this.artistStr = params.artists.join(", ")
}
if (typeof params.source !== "undefined") {
this.source = params.source
}
if (typeof params.metadata !== "undefined") {
this.metadata = params.metadata
}
if (typeof params.lyrics_enabled !== "undefined") {
this.lyrics_enabled = params.lyrics_enabled
}
return this
}
_id = null // used for api requests
uid = null // used for internal
cover =
"https://storage.ragestudio.net/comty-static-assets/default_song.png"
title = "Untitled"
album = "Unknown"
artist = "Unknown"
source = null
metadata = null
// set default service to default
service = "default"
// Extended from db
lyrics_enabled = false
liked = null
// TODO: implement this server feature to fetch some data from the server,
// used for example to fix a incorrect lyrics time
overrides = null
async initialize() {
if (this.params.file) {
this.metadata = await this.analyzeMetadata(
this.params.file.originFileObj,
)
this.metadata.format = this.metadata.type.toUpperCase()
if (this.metadata.tags) {
if (this.metadata.tags.title) {
this.title = this.metadata.tags.title
}
if (this.metadata.tags.artist) {
this.artist = this.metadata.tags.artist
}
if (this.metadata.tags.album) {
this.album = this.metadata.tags.album
}
if (this.metadata.tags.picture) {
this.cover = app.cores.remoteStorage.binaryArrayToFile(
this.metadata.tags.picture,
"cover",
)
const coverUpload =
await app.cores.remoteStorage.uploadFile(this.cover)
this.cover = coverUpload.url
delete this.metadata.tags.picture
}
this.handleChanges({
cover: this.cover,
title: this.title,
artist: this.artist,
album: this.album,
})
}
}
return this
}
handleChanges = (changes) => {
if (typeof this.params.onChange === "function") {
this.params.onChange(this.uid, changes)
}
}
analyzeMetadata = async (file) => {
return new Promise((resolve, reject) => {
jsmediatags.read(file, {
onSuccess: (data) => {
return resolve(data)
},
onError: (error) => {
return reject(error)
},
})
})
}
analyzeCoverColor = async () => {
const fac = new FastAverageColor()
return await fac.getColorAsync(this.cover)
}
serviceOperations = {
fetchLikeStatus: async () => {
if (!this._id) {
return null
}
return await this.ctx.serviceProviders.operation(
"isItemFavourited",
this.service,
this,
"track",
)
},
fetchLyrics: async () => {
if (!this._id) {
return null
}
return await this.ctx.serviceProviders.operation(
"resolveLyrics",
this.service,
this,
)
},
fetchOverride: async () => {
if (!this._id) {
return null
}
return await this.ctx.serviceProviders.operation(
"resolveOverride",
this.service,
this,
)
},
toggleItemFavourite: async (to) => {
if (!this._id) {
return null
}
return await this.ctx.serviceProviders.operation(
"toggleItemFavourite",
this.service,
this,
"track",
to,
)
},
}
toSeriableObject = () => {
return {
_id: this._id,
uid: this.uid,
title: this.title,
album: this.album,
artist: this.artist,
source: this.source,
metadata: this.metadata,
liked: this.liked,
}
}
}