Files
Lazuli/src/lib/utils/utils.js

120 lines
4.1 KiB
JavaScript
Raw Normal View History

import Joi from 'joi'
2024-01-06 22:05:51 -05:00
export const ticksToTime = (ticks) => {
const totalSeconds = ~~(ticks / 10000000)
const totalMinutes = ~~(totalSeconds / 60)
const hours = ~~(totalMinutes / 60)
const remainderMinutes = totalMinutes - hours * 60
const remainderSeconds = totalSeconds - totalMinutes * 60
const format = (value) => {
return value < 10 ? `0${value}` : value
}
if (hours > 0) {
return `${hours}:${format(remainderMinutes)}:${format(remainderSeconds)}`
} else {
return `${remainderMinutes}:${format(remainderSeconds)}`
}
}
export class JellyfinUtils {
static #AUDIO_PRESETS = {
default: {
MaxStreamingBitrate: 999999999,
2024-01-06 22:05:51 -05:00
Container: 'opus,webm|opus,mp3,aac,m4a|aac,m4b|aac,flac,webma,webm|webma,wav,ogg',
TranscodingContainer: 'ts',
TranscodingProtocol: 'hls',
AudioCodec: 'aac',
// userId: REMEMBER TO ADD THIS TO THE END,
2024-01-06 22:05:51 -05:00
},
}
static mediaItemFactory = (itemData, connectionData) => {
const generalItemSchema = Joi.object({
ServerId: Joi.string().required(),
Type: Joi.string().required(),
}).unknown(true)
2024-01-06 22:05:51 -05:00
const generalItemValidation = generalItemSchema.validate(itemData)
if (generalItemValidation.error) throw new Error(generalItemValidation.error.message)
2024-01-06 22:05:51 -05:00
switch (itemData.Type) {
case 'Audio':
return this.songFactory(itemData, connectionData)
case 'MusicAlbum':
break
}
2024-01-06 22:05:51 -05:00
}
static songFactory = (songData, connectionData) => {
const { id, serviceType, serviceUserId, serviceUrl } = connectionData
const songSchema = Joi.object({
Name: Joi.string().required(),
Id: Joi.string().required(),
RunTimeTicks: Joi.number().required(),
}).unknown(true)
const songValidation = songSchema.validate(songData)
if (songValidation.error) throw new Error(songValidation.error.message)
const artistData = songData?.ArtistItems
? Array.from(songData.ArtistItems, (artist) => {
return { name: artist.Name, id: artist.Id }
})
: null
const albumData = songData?.AlbumId
? {
name: songData.Album,
id: songData.AlbumId,
artists: songData.AlbumArtists,
image: songData?.AlbumPrimaryImageTag ? new URL(`Items/${songData.AlbumId}/Images/Primary`, serviceUrl).href : null,
}
: null
const imageSource = songData?.ImageTags?.Primary
? new URL(`Items/${songData.Id}/Images/Primary`, serviceUrl).href
: songData?.AlbumPrimaryImageTag
? new URL(`Items/${songData.AlbumId}/Images/Primary`, serviceUrl).href
: null
const audioSearchParams = new URLSearchParams(this.#AUDIO_PRESETS.default)
audioSearchParams.append('userId', serviceUserId)
const audoSource = new URL(`Audio/${songData.Id}/universal?${audioSearchParams.toString()}`, serviceUrl).href
return {
connectionId: id,
serviceType,
mediaType: 'song',
name: songData.Name,
id: songData.Id,
duration: Math.floor(songData.RunTimeTicks / 10000), // <-- Converts 'ticks' (whatever that means) to milliseconds, a sane unit of measure
artists: artistData,
album: albumData,
image: imageSource,
audio: audoSource,
video: null,
releaseDate: songData?.ProductionYear,
}
2024-01-06 22:05:51 -05:00
}
static getLocalDeviceUUID = () => {
2024-01-06 22:05:51 -05:00
const existingUUID = localStorage.getItem('lazuliDeviceUUID')
if (!existingUUID) {
const newUUID = '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16))
localStorage.setItem('lazuliDeviceUUID', newUUID)
return newUUID
}
return existingUUID
}
}
export class YouTubeMusicUtils {
static mediaItemFactory = () => {}
}