mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-31 18:28:55 +00:00
refactor: move music assets to config directory
This commit is contained in:
@@ -25,8 +25,9 @@ MusicTracks = {
|
||||
The standalone `Music` app plays audio only inside the current player's NUI. It never creates a
|
||||
world sound, voice channel, positional event, or 3D-audio state that another player can hear.
|
||||
|
||||
Server-owned MP3/OGG tracks live in `frontend/public/music`. Define their stable ID, title, artist,
|
||||
file, and optional artwork in `sky_phone/config/music.lua`, then run `build_frontend.bat`:
|
||||
Server-owned MP3/OGG tracks and their optional artwork live directly in
|
||||
`sky_phone/config/music`. Define their stable ID, title, artist, and paths in
|
||||
`sky_phone/config/music.lua`:
|
||||
|
||||
```lua
|
||||
Tracks = {
|
||||
@@ -34,12 +35,16 @@ Tracks = {
|
||||
Id = "night-drive",
|
||||
Title = "Night Drive",
|
||||
Artist = "Sky Records",
|
||||
File = "music/night-drive.ogg",
|
||||
Artwork = "music/night-drive.webp",
|
||||
File = "config/music/night-drive.ogg",
|
||||
Artwork = "config/music/night-drive.webp",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
No frontend build is needed when tracks change. Restart the resource so FiveM republishes the
|
||||
files and reloads the track configuration. Keep existing track IDs stable because playlists store
|
||||
those IDs.
|
||||
|
||||
Players can add public YouTube video links to their own library. Metadata is requested through
|
||||
YouTube's oEmbed endpoint on the server, while playback uses the embedded YouTube player only on
|
||||
that player's NUI. Personal songs and playlists are stored per linked iFruit account, or per phone
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
Place server-owned .mp3 and .ogg files in this directory.
|
||||
|
||||
Define each published song in sky_phone/config/music.lua, then run
|
||||
build_frontend.bat so the files are copied into the deployable NUI.
|
||||
|
||||
Optional artwork can be stored here as .png, .jpg, .jpeg or .webp.
|
||||
@@ -1,13 +1,13 @@
|
||||
Config.Music = {
|
||||
-- Put MP3/OGG files in frontend/public/music and rebuild the frontend.
|
||||
-- File and Artwork paths are relative to the published NUI root.
|
||||
-- Put server-owned audio and artwork directly in config/music.
|
||||
-- Paths must stay inside that directory. Restart the resource after changes.
|
||||
Tracks = {
|
||||
-- {
|
||||
-- Id = "night-drive",
|
||||
-- Title = "Night Drive",
|
||||
-- Artist = "Sky Records",
|
||||
-- File = "music/night-drive.ogg",
|
||||
-- Artwork = "music/night-drive.webp",
|
||||
-- File = "config/music/night-drive.ogg",
|
||||
-- Artwork = "config/music/night-drive.webp",
|
||||
-- },
|
||||
},
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
Place server-owned .mp3 and .ogg files in this directory.
|
||||
|
||||
Optional artwork can be stored here as .png, .jpg, .jpeg or .webp. Register
|
||||
each track in ../music.lua using paths such as:
|
||||
|
||||
File = "config/music/night-drive.ogg"
|
||||
Artwork = "config/music/night-drive.webp"
|
||||
|
||||
Use file names containing only letters, numbers, dots, underscores or hyphens.
|
||||
Subdirectories are supported. Restart the sky_phone resource after changing
|
||||
tracks or files; a frontend build is not required.
|
||||
@@ -71,7 +71,7 @@ files {
|
||||
'source/html/index.html',
|
||||
'source/html/assets/**',
|
||||
'source/html/img/**',
|
||||
'source/html/music/**',
|
||||
'config/music/**',
|
||||
}
|
||||
|
||||
ui_page 'source/html/index.html'
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
Bridge.Database.AfterMigration("sky_phone", function()
|
||||
local server_tracks = {}
|
||||
local server_tracks_by_id = {}
|
||||
local music_asset_prefix = "config/music/"
|
||||
local audio_extensions = { mp3 = true, ogg = true }
|
||||
local artwork_extensions = { jpeg = true, jpg = true, png = true, webp = true }
|
||||
|
||||
local function trim(value)
|
||||
return type(value) == "string" and value:match("^%s*(.-)%s*$") or nil
|
||||
@@ -24,6 +27,29 @@ local function optional_text(value)
|
||||
return normalized ~= "" and normalized or nil
|
||||
end
|
||||
|
||||
local function normalize_music_asset_path(value, allowed_extensions)
|
||||
local path = trim(value)
|
||||
if not path
|
||||
or path:sub(1, #music_asset_prefix) ~= music_asset_prefix
|
||||
or not path:match("^[%w%._%-%/]+$")
|
||||
or path:find("..", 1, true)
|
||||
or path:find("//", 1, true)
|
||||
or path:find("\\", 1, true)
|
||||
then
|
||||
return nil
|
||||
end
|
||||
|
||||
local extension = path:match("%.([%w]+)$")
|
||||
if not extension or not allowed_extensions[extension:lower()] then
|
||||
return nil
|
||||
end
|
||||
return path
|
||||
end
|
||||
|
||||
local function music_asset_url(path)
|
||||
return ("https://cfx-nui-%s/%s"):format(GetCurrentResourceName(), path)
|
||||
end
|
||||
|
||||
local function affected_rows(result)
|
||||
if type(result) == "number" then
|
||||
return result
|
||||
@@ -45,25 +71,25 @@ local function normalize_server_tracks()
|
||||
local id = trim(configured.Id)
|
||||
local title = trim(configured.Title)
|
||||
local artist = trim(configured.Artist)
|
||||
local file = trim(configured.File)
|
||||
local artwork = trim(configured.Artwork)
|
||||
local valid_file = file
|
||||
and file:match("^music/[%w%._%-%/]+%.[mM][pP]3$")
|
||||
or file and file:match("^music/[%w%._%-%/]+%.[oO][gG][gG]$")
|
||||
local title_length = text_length(title)
|
||||
local artist_length = text_length(artist)
|
||||
local file = normalize_music_asset_path(configured.File, audio_extensions)
|
||||
local configured_artwork = optional_text(configured.Artwork)
|
||||
local artwork = configured_artwork
|
||||
and normalize_music_asset_path(configured_artwork, artwork_extensions)
|
||||
or nil
|
||||
|
||||
if not id
|
||||
or not id:match("^[%w%-_]+$")
|
||||
or #id > 48
|
||||
or not title
|
||||
or not text_length(title)
|
||||
or text_length(title) > 160
|
||||
or not artist
|
||||
or not text_length(artist)
|
||||
or text_length(artist) > 120
|
||||
or not valid_file
|
||||
or (artwork and not artwork:match("^music/[%w%._%-%/]+%.[pP][nN][gG]$")
|
||||
and not artwork:match("^music/[%w%._%-%/]+%.[jJ][pP][eE]?[gG]$")
|
||||
and not artwork:match("^music/[%w%._%-%/]+%.[wW][eE][bB][pP]$"))
|
||||
or not title_length
|
||||
or title_length < 1
|
||||
or title_length > 160
|
||||
or not artist_length
|
||||
or artist_length < 1
|
||||
or artist_length > 120
|
||||
or not file
|
||||
or (configured_artwork and not artwork)
|
||||
or server_tracks_by_id[id]
|
||||
then
|
||||
Bridge.Debug(
|
||||
@@ -78,8 +104,8 @@ local function normalize_server_tracks()
|
||||
source = "server",
|
||||
title = title,
|
||||
artist = artist,
|
||||
url = file,
|
||||
artwork = artwork,
|
||||
url = music_asset_url(file),
|
||||
artwork = artwork and music_asset_url(artwork) or nil,
|
||||
}
|
||||
server_tracks[#server_tracks + 1] = track
|
||||
server_tracks_by_id[id] = track
|
||||
|
||||
Reference in New Issue
Block a user