2025-02-27 13:47:45 +00:00
const fs = require ( "fs" ) ;
const path = require ( "path" ) ;
const csv = require ( "csv-parser" ) ;
const React = require ( "react" ) ;
const dotenv = require ( "dotenv" ) ;
const ReactDOMServer = require ( "react-dom/server" ) ;
const TemplatePageModule = require ( "./build/components/jamtracks/JKJamTracksLandingTemplatePage" ) ;
2025-03-03 13:18:54 +00:00
const ArtistTemplatePageModule = require ( "./build/components/jamtracks/JKJamTracksArtistLandingTemplatePage" ) ;
2025-02-27 13:47:45 +00:00
2025-03-03 13:18:54 +00:00
var csvFilePath = ` jamtracks-for-env/jam_tracks_for_jam_ui. ${ process . env . USER } .csv `
var artistCsvFilePath = ` jamtracks-for-env/jam_tracks_for_jam_ui_artists. ${ process . env . USER } .csv `
2025-02-27 13:47:45 +00:00
2025-03-03 13:18:54 +00:00
/ * *
* Loads a CSV file into an array of objects .
* @ param { string } csvPath - The path to the CSV file .
* @ returns { Promise < Array < Object >> } - A promise that resolves with the parsed CSV data .
* /
const load _csv = ( csvPath ) => {
return new Promise ( ( resolve , reject ) => {
const results = [ ] ;
fs . createReadStream ( csvPath )
. pipe ( csv ( ) )
. on ( 'data' , ( data ) => results . push ( data ) )
. on ( 'end' , ( ) => resolve ( results ) )
. on ( 'error' , ( error ) => reject ( error ) ) ;
} ) ;
} ;
/ * *
* Finds all songs for a given artist based on the original _artist _slug
* and sorts them alphabetically by the ` slug ` field .
* @ param { string } artistSlug - The original _artist _slug to match .
* @ param { Array < Object > } songsCsv - The songs CSV data .
* @ returns { Array < Object > } - A sorted array of matching song objects .
* /
const collect _songs _for _artist = ( artistSlug , songsCsv ) => {
return songsCsv
. filter ( ( song ) => song . original _artist _slug === artistSlug )
. sort ( ( a , b ) => a . slug . localeCompare ( b . slug ) ) ; // Sort alphabetically by slug
} ;
2025-02-27 13:47:45 +00:00
const init = ( ) => {
const node _env = process . env . NODE _ENV || 'development' ;
const environment = process . env . ENVIRONMENT || 'development' ;
console . log ( ` environment= ${ environment } node_env= ${ node _env } ` ) ;
console . log ( dotenv . config ( { path : ` ../.env. ${ environment } ` } ) ) ;
if ( environment === "production" || environment === "staging" ) {
2025-03-03 13:18:54 +00:00
csvFilePath = ` jamtracks-for-env/jam_tracks_for_jam_ui. ${ environment } .csv ` ;
artistCsvFilePath = ` jamtracks-for-env/jam_tracks_for_jam_ui_artists. ${ environment } .csv ` ;
2025-02-27 13:47:45 +00:00
}
2025-03-03 13:18:54 +00:00
console . log ( "Song csv file" , csvFilePath ) ;
console . log ( "Artist csv file" , artistCsvFilePath ) ;
2025-02-27 13:47:45 +00:00
if ( ! process . env . PUBLIC _URL ) {
console . log ( "setting public url" , process . env . REACT _APP _BASE _URL ) ;
2025-03-20 01:07:19 +00:00
process . env . PUBLIC _URL = process . env . REACT _APP _BASE _URL ;
2025-02-27 13:47:45 +00:00
}
//const __dirname = path.resolve(path.dirname(''));
console . log ( "init done successfully" )
}
2025-03-03 13:18:54 +00:00
const generateSongPages = async ( render ) => {
2025-02-27 13:47:45 +00:00
const rows = [ ] ;
const OUTPUT _DIR = path . join ( _ _dirname , ".." , "public" , "backing-tracks" ) ;
if ( ! fs . existsSync ( OUTPUT _DIR ) ) {
fs . mkdirSync ( OUTPUT _DIR , { recursive : true } ) ;
}
console . log ( "generatPages starting" )
const TemplatePage = TemplatePageModule . default ;
fs . createReadStream ( csvFilePath )
. pipe ( csv ( ) )
. on ( "data" , ( row ) => rows . push ( row ) )
. on ( "end" , async ( ) => {
console . log ( ` Processing ${ rows . length } rows... ` ) ;
for ( const row of rows ) {
// id, original_artist, name, original_artist_slug, name_slug, plan_code, slug, URL, licensor, vendor_id
2025-03-03 13:18:54 +00:00
const { id , original _artist , name , original _artist _slug , name _slug , plan _code , slug , allow _free } = row ;
2025-02-27 13:47:45 +00:00
const artist = original _artist ;
const song = name ;
const location = ` /backing-tracks/ ${ original _artist _slug } / ${ name _slug } ` ;
const fullPath = process . env . REACT _APP _BASE _URL + location ;
const logoPath = process . env . REACT _APP _BASE _URL + "/favicon.svg" ;
console . log ( ` Generating ${ artist } - ${ song } ` ) ;
const html = render
? ReactDOMServer . renderToStaticMarkup (
React . createElement ( TemplatePage , { id , plan _code , slug , artist , song , location } )
)
: "" ;
const fullHtml = ` <!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< link rel = "shortcut icon" href = "/favicon.svg" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title > $ { artist } - $ { song } - Free Backing Track < / t i t l e >
< link rel = "stylesheet" href = "${process.env.REACT_APP_BASE_URL}/css/theme.css" >
< meta name = "description" content = "Get free ${song} by ${artist} backing track, plus free tools to mute any part, slow down for practice, record yourself, more" >
< meta name = "keywords" content = "Backing Track, ${artist}, ${song}, Instrumental" >
< meta name = "author" content = "JamKazam" >
<!-- Open Graph ( Facebook , LinkedIn , etc . ) -- >
< meta property = "og:title" content = "${artist} - ${song} | Free Backing Track" >
< meta property = "og:description" content = "Get free ${song} by ${artist} backing track, plus free tools to mute any part, slow down for practice, record yourself, more" >
< meta property = "og:image" content = "${logoPath}" >
< meta property = "og:url" content = "${fullPath}" >
< meta property = "og:type" content = "music.song" >
<!-- Twitter Cards -- >
< meta name = "twitter:card" content = "summary_large_image" >
< meta name = "twitter:title" content = "${artist} - ${song} | Free Backing Track" >
< meta name = "twitter:description" content = "Get free ${song} by ${artist} backing track, plus free tools to mute any part, slow down for practice, record yourself, more" >
< meta name = "twitter:image" content = "${logoPath}" >
<!-- Canonical URL -- >
< link rel = "canonical" href = "${fullPath}" / >
<!-- Structured Data ( Schema . org ) -- >
< script type = "application/ld+json" >
{
"@context" : "https://schema.org" ,
"@type" : "MusicRecording" ,
"name" : "${song}" ,
"byArtist" : {
"@type" : "MusicGroup" ,
"name" : "${artist}"
} ,
"url" : "${fullPath}" ,
"image" : "${logoPath}"
}
< / s c r i p t >
< script >
window . jamtrack _data = {
id : "${id}" ,
plan _code : "${plan_code}" ,
slug : "${slug}" ,
artist : "${artist}" ,
song : "${song}" ,
location : "${location}"
}
< / s c r i p t >
< / h e a d >
< body >
< div id = "root" > $ { html } < / d i v >
< script src = "/js/client-hydrate.bundle.js" > < / s c r i p t >
< / b o d y >
< / h t m l > ` ;
const ARTIST _DIR = path . join ( OUTPUT _DIR , original _artist _slug ) ;
if ( ! fs . existsSync ( ARTIST _DIR ) ) {
fs . mkdirSync ( ARTIST _DIR , { recursive : false } ) ;
}
2025-03-03 13:18:54 +00:00
const finalOutputPath = process . env . NODE _ENV === "development" ? ` ${ name _slug } .html ` : ` ${ name _slug } .html ` ;
2025-02-27 13:47:45 +00:00
const outputFilePath = path . join ( ARTIST _DIR , finalOutputPath ) ;
fs . writeFileSync ( outputFilePath , fullHtml ) ;
console . log ( ` Generated: ${ outputFilePath } ` ) ;
}
console . log ( "All pages generated!" ) ;
} ) ;
} ;
2025-03-03 13:18:54 +00:00
const generateArtistPages = async ( render ) => {
const rows = [ ] ;
const OUTPUT _DIR = path . join ( _ _dirname , ".." , "public" , "backing-tracks" ) ;
if ( ! fs . existsSync ( OUTPUT _DIR ) ) {
fs . mkdirSync ( OUTPUT _DIR , { recursive : true } ) ;
}
console . log ( "generatPages starting" )
const TemplatePage = TemplatePageModule . default ;
const songs _csv = await load _csv ( csvFilePath ) ;
fs . createReadStream ( artistCsvFilePath )
. pipe ( csv ( ) )
. on ( "data" , ( row ) => rows . push ( row ) )
. on ( "end" , async ( ) => {
console . log ( ` Processing ${ rows . length } rows... ` ) ;
for ( const row of rows ) {
const { original _artist , original _artist _slug , url } = row ;
const artist = original _artist ;
const matchingSongs = collect _songs _for _artist ( original _artist _slug , songs _csv ) ;
console . log ( ` Found ${ matchingSongs . length } songs for ${ artist } ` ) ;
const location = ` /backing-tracks/ ${ original _artist _slug } ` ;
const fullPath = process . env . REACT _APP _BASE _URL + location ;
const logoPath = process . env . REACT _APP _BASE _URL + "/favicon.svg" ;
console . log ( ` Generating ${ artist } ` ) ;
const songs = matchingSongs . map ( ( song ) => {
return {
name : song . name ,
plan _code : song . plan _code ,
url : process . env . REACT _APP _BASE _URL + "/backing-tracks/" + song . original _artist _slug + "/" + song . name _slug
}
} ) ;
const html = render
? ReactDOMServer . renderToStaticMarkup (
React . createElement ( ArtistTemplatePage , { artist , original _artist _slug , location } )
)
: "" ;
const fullHtml = ` <!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< link rel = "shortcut icon" href = "/favicon.svg" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title > $ { artist } - Free Backing Track < / t i t l e >
< link rel = "stylesheet" href = "${process.env.REACT_APP_BASE_URL}/css/theme.css" >
< meta name = "description" content = "Get a free ${artist} backing track, plus free tools to mute any part, slow down for practice, record yourself, more" >
< meta name = "keywords" content = "Backing Track, ${artist}, Instrumental" >
< meta name = "author" content = "JamKazam" >
<!-- Open Graph ( Facebook , LinkedIn , etc . ) -- >
< meta property = "og:title" content = "${artist} | Free Backing Track" >
< meta property = "og:description" content = "Get a free ${artist} backing track, plus free tools to mute any part, slow down for practice, record yourself, more" >
< meta property = "og:image" content = "${logoPath}" >
< meta property = "og:url" content = "${fullPath}" >
< meta property = "og:type" content = "music.song" >
<!-- Twitter Cards -- >
< meta name = "twitter:card" content = "summary_large_image" >
< meta name = "twitter:title" content = "${artist} | Free Backing Track" >
< meta name = "twitter:description" content = "Get a free ${artist} backing track, plus free tools to mute any part, slow down for practice, record yourself, more" >
< meta name = "twitter:image" content = "${logoPath}" >
<!-- Canonical URL -- >
< link rel = "canonical" href = "${fullPath}" / >
< script >
window . jamtrack _data = {
artist : "${artist}" ,
original _artist _slug : "${original_artist_slug}" ,
location : "${location}" ,
songs : $ { JSON . stringify ( songs ) }
}
< / s c r i p t >
< / h e a d >
< body >
< div id = "root" > $ { html } < / d i v >
< script src = "/js/client-hydrate.bundle.js" > < / s c r i p t >
< / b o d y >
< / h t m l > ` ;
const finalOutputPath = process . env . NODE _ENV === "development" ? ` ${ original _artist _slug } .html ` : ` ${ original _artist _slug } .html ` ;
const outputFilePath = path . join ( OUTPUT _DIR , finalOutputPath ) ;
fs . writeFileSync ( outputFilePath , fullHtml ) ;
console . log ( ` Generated: ${ outputFilePath } ` ) ;
}
console . log ( "All pages generated!" ) ;
} ) ;
} ;
2025-02-27 13:47:45 +00:00
let render = false ;
if ( process . argv . length > 2 ) {
render = process . argv [ 2 ] === "true" || process . argv [ 2 ] === "yes" || process . argv [ 2 ] === "1" ;
}
init ( )
2025-03-03 13:18:54 +00:00
generateSongPages ( render ) ;
generateArtistPages ( render ) ;