.gitignore: refactor. add tour, oppanmachine
This commit is contained in:
parent
346cc478c9
commit
0e770d78e8
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,2 @@
|
|||||||
nate-higgers-assets
|
|
||||||
anal-probes
|
anal-probes
|
||||||
scripts
|
lib/sh
|
||||||
|
11
Makefile
Normal file
11
Makefile
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
all: | build
|
||||||
|
prod: | build validate
|
||||||
|
|
||||||
|
build:
|
||||||
|
node lib/build.js
|
||||||
|
validate:
|
||||||
|
ajv -s data/schema.yaml -d data/songs.json
|
||||||
|
links:
|
||||||
|
lib/sh/getlatest.sh $(i)
|
||||||
|
dbg:
|
||||||
|
node -e 'console.debug(require("util").inspect(require("./data/songs.js"), { colors: true, depth: Infinity, maxArrayLength: Infinity }))'
|
72
data/schema.yaml
Normal file
72
data/schema.yaml
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
required: [ version, songs ]
|
||||||
|
$defs:
|
||||||
|
GWRadioURI:
|
||||||
|
type: string
|
||||||
|
description: 'A URI that points to the source file. See `GWMediaPlayer.fromURIToAbsolute()` for how to convert to a URL'
|
||||||
|
MediaCodec:
|
||||||
|
type: string
|
||||||
|
MediaCodecs:
|
||||||
|
type: array
|
||||||
|
description: 'An array of codecs contained within the source file. Items should follow RFC6381'
|
||||||
|
items:
|
||||||
|
- $ref: "#/$defs/MediaCodec"
|
||||||
|
properties:
|
||||||
|
version: { type: integer }
|
||||||
|
songs:
|
||||||
|
items:
|
||||||
|
required: [ id, sources ]
|
||||||
|
properties:
|
||||||
|
index:
|
||||||
|
type: integer
|
||||||
|
description: 'Original song index'
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
metadata:
|
||||||
|
properties:
|
||||||
|
title: { type: string }
|
||||||
|
artist: { type: string }
|
||||||
|
href:
|
||||||
|
$ref: "#/$defs/GWRadioURI"
|
||||||
|
description: 'A URI where the original source can be found, if known. Link may be dead'
|
||||||
|
_nigid:
|
||||||
|
description: 'The original song id from nigge.rs.'
|
||||||
|
type: string
|
||||||
|
_nigkey:
|
||||||
|
description: 'Content of _id from the original nigge.rs song entry.'
|
||||||
|
type: string
|
||||||
|
|
||||||
|
misc:
|
||||||
|
type: integer
|
||||||
|
description: 'Media bitflags. See BMiscInfo'
|
||||||
|
tags:
|
||||||
|
description: 'A list of tags used for filtering.'
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
anyOf:
|
||||||
|
- const: moonman
|
||||||
|
- const: metadata-prefer-id
|
||||||
|
- regex: '^radio\-media\-style\-[a-z\-]+'
|
||||||
|
- type: string
|
||||||
|
|
||||||
|
sources:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
- required: [ type, uri ]
|
||||||
|
properties:
|
||||||
|
type: { type: string, format: mimetype }
|
||||||
|
codecs: { "$ref": "#/$defs/MediaCodecs" }
|
||||||
|
uri: { "$ref": "#/$defs/GWRadioURI" }
|
||||||
|
previews:
|
||||||
|
description: >-
|
||||||
|
This is an array so GWMediaPlayer can better display videos being played as audio only.
|
||||||
|
The first Item is how the MediaItem *should* be displayed, other Items are alternative versions the player may use.
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
- properties:
|
||||||
|
display_type:
|
||||||
|
type: integer
|
||||||
|
description: 'Used to determine how the media should be displayed. See EDisplayType'
|
||||||
|
type: { "$ref": "#/$defs/MediaCodec" }
|
||||||
|
codecs: { "$ref": "#/$defs/MediaCodecs" }
|
||||||
|
uri: { "$ref": "#/$defs/GWRadioURI" }
|
||||||
|
size: { type: string }
|
1043
data/songs.js
Normal file
1043
data/songs.js
Normal file
File diff suppressed because it is too large
Load Diff
132
lib/RadioItem.js
Normal file
132
lib/RadioItem.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
"use-strict";
|
||||||
|
const { objSerialize, objBulkSet } = require("./util.js")
|
||||||
|
const { RadioMetadata } = require("./RadioMetadata.js")
|
||||||
|
const { RadioMiscInfo } = require("./RadioMiscInfo.js")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @constructor
|
||||||
|
* @params {string} id
|
||||||
|
* @returns {RadioItem}
|
||||||
|
*/
|
||||||
|
const RadioItem = function (id, miscInfo) {
|
||||||
|
this.id = id
|
||||||
|
this.metadata = undefined
|
||||||
|
this.info = undefined
|
||||||
|
this.misc = miscInfo || undefined
|
||||||
|
this.tags = undefined
|
||||||
|
this.sources = []
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {string} id
|
||||||
|
* @returns {RadioItem}
|
||||||
|
*/
|
||||||
|
RadioItem.new = id => new RadioItem(id)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {string|undefined} title
|
||||||
|
* @params {string|undefined} artist
|
||||||
|
* @params {URL|string|undefined} [href] href
|
||||||
|
* @params {string} [extra] extra Playback effects. i.e: Pitch, speed
|
||||||
|
* @returns {RadioItem}
|
||||||
|
*/
|
||||||
|
RadioItem.prototype.setMetadata = function (title, artist, href, extra) {
|
||||||
|
if (!this.metadata)
|
||||||
|
this.metadata = RadioMetadata.new()
|
||||||
|
objBulkSet(this, "metadata", { title, artist, href, extra });
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {RadioMetadata} metadata
|
||||||
|
* @returns {RadioItem}
|
||||||
|
*/
|
||||||
|
RadioItem.prototype.addItemInfo = function (metadata) {
|
||||||
|
if (!this.info)
|
||||||
|
this.info = []
|
||||||
|
this.info.push(metadata)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {RadioMiscInfo} miscInfo
|
||||||
|
* @returns {RadioItem}
|
||||||
|
*/
|
||||||
|
RadioItem.prototype.setMiscInfo = function (miscInfo) {
|
||||||
|
this.misc = miscInfo
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @returns {RadioItem}
|
||||||
|
*/
|
||||||
|
RadioItem.prototype.hoistMiscInfo = function () {
|
||||||
|
return this.misc ? this.misc : this.misc = RadioMiscInfo.new()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {string[]} tags
|
||||||
|
* @returns {RadioItem}
|
||||||
|
*/
|
||||||
|
RadioItem.prototype.addTags = function (tags) { this.tags = this.tags ? [ ...(this.tags), ...tags ] : tags; return this }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias for addTags; prefixes all items with "radio-media-style-"
|
||||||
|
* @public
|
||||||
|
* @params {string[]} tags
|
||||||
|
* @returns {RadioItem}
|
||||||
|
* @see RadioItem.prototype.addTags
|
||||||
|
*/
|
||||||
|
RadioItem.prototype.addStyleTags = function (tags) { return this.addTags(tags.map(a => `radio-media-style-${a}`)); return this }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {RadioSource} source
|
||||||
|
* @returns {RadioItem}
|
||||||
|
*/
|
||||||
|
RadioItem.prototype.addSource = function (source) { this.sources.push(source); return this }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set metadata for songs that were previously on nigge.rs; adds the "niggers" tag.
|
||||||
|
* @public
|
||||||
|
* @params {string|undefined} nigid
|
||||||
|
* @params {string|undefined} nigkey
|
||||||
|
* @returns {RadioItem}
|
||||||
|
*/
|
||||||
|
RadioItem.prototype.setNiggadata = function (nigid, nigkey) {
|
||||||
|
this.setMetadata()
|
||||||
|
this.metadata._nigid = nigid
|
||||||
|
this.metadata._nigkey = nigkey
|
||||||
|
//this.addTags([ "niggers" ])
|
||||||
|
this.hoistMiscInfo().bNiggersSong()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
RadioItem.prototype.serialize = function (index) {
|
||||||
|
const self = this
|
||||||
|
return objSerialize(this, [
|
||||||
|
"id",
|
||||||
|
"tags",
|
||||||
|
"metadata"
|
||||||
|
], obj => ({
|
||||||
|
index,
|
||||||
|
...obj,
|
||||||
|
"misc": self.misc ? self.misc.serialize() : undefined,
|
||||||
|
"info": self.info?.length ? self.info.map(info => info.serialize()) : undefined,
|
||||||
|
"sources": self.sources.map(source => source.serialize())
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { RadioItem }
|
192
lib/RadioMetadata.js
Normal file
192
lib/RadioMetadata.js
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
"use-strict";
|
||||||
|
const { objSerialize, fromConstToPascal } = require("./util.js")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
const BMetadataType = {
|
||||||
|
"AUDIO": 0b1,
|
||||||
|
"VIDEO": 0b10,
|
||||||
|
"ORIGINAL": 0b100,
|
||||||
|
"ALT": 0b1000,
|
||||||
|
"OFFICIAL": 0b10000,
|
||||||
|
"EXPLICIT": 0b100000,
|
||||||
|
"PREFERED": 0b1000000,
|
||||||
|
"PSEUDONYM": 0b10000000,
|
||||||
|
"UNAVAILABLE": 0b100000000,
|
||||||
|
"PREMIUM": 0b1000000000
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @constructor
|
||||||
|
* @params {string|undefined} title
|
||||||
|
* @params {string|undefined} artist
|
||||||
|
* @params {URL|string|undefined} [href] href
|
||||||
|
* @params {string} [extra] extra Playback effects. i.e: Pitch, speed
|
||||||
|
* @returns {RadioMetadata}
|
||||||
|
*/
|
||||||
|
const RadioMetadata = function (title, artist, href, extra) {
|
||||||
|
this.info = undefined
|
||||||
|
this.title = title || undefined
|
||||||
|
this.artist = artist || undefined
|
||||||
|
this.href = href || undefined
|
||||||
|
this.extra = extra || undefined
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {string|undefined} title
|
||||||
|
* @params {string|undefined} artist
|
||||||
|
* @params {URL|string|undefined} [href] href
|
||||||
|
* @params {string} [extra] extra Playback effects. i.e: Pitch, speed
|
||||||
|
* @returns {RadioMetadata}
|
||||||
|
*/
|
||||||
|
RadioMetadata.new = (title, artist, href, extra) => new RadioMetadata(title, artist, href, extra)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias for new(...).bVideo().bAudio()
|
||||||
|
* @public
|
||||||
|
* @params {string|undefined} title
|
||||||
|
* @params {string|undefined} artist
|
||||||
|
* @params {URL|string|undefined} [href] href
|
||||||
|
* @params {string} [extra] extra Playback effects. i.e: Pitch, speed
|
||||||
|
* @returns {RadioMetadata}
|
||||||
|
*/
|
||||||
|
RadioMetadata.newLAV = (title, artist, href, extra) => (new RadioMetadata(title, artist, href, extra)).bVideo().bAudio()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias for new(...).bVideo()
|
||||||
|
* @public
|
||||||
|
* @params {string|undefined} title
|
||||||
|
* @params {string|undefined} artist
|
||||||
|
* @params {URL|string|undefined} [href] href
|
||||||
|
* @params {string} [extra] extra Playback effects. i.e: Pitch, speed
|
||||||
|
* @returns {RadioMetadata}
|
||||||
|
*/
|
||||||
|
RadioMetadata.newLV = (title, artist, href, extra) => (new RadioMetadata(title, artist, href, extra)).bVideo()
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias for new(...).bAudio()
|
||||||
|
* @public
|
||||||
|
* @params {string|undefined} title
|
||||||
|
* @params {string|undefined} artist
|
||||||
|
* @params {URL|string|undefined} [href] href
|
||||||
|
* @params {string} [extra] extra Playback effects. i.e: Pitch, speed
|
||||||
|
* @returns {RadioMetadata}
|
||||||
|
*/
|
||||||
|
RadioMetadata.newLA = (title, artist, href, extra) => (new RadioMetadata(title, artist, href, extra)).bAudio()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {string} watch_id
|
||||||
|
* @returns {RadioMetadata}
|
||||||
|
*/
|
||||||
|
RadioMetadata.createYouTubeURI = id => `x-yt://${id}`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {string} id
|
||||||
|
* @returns {RadioMetadata}
|
||||||
|
*/
|
||||||
|
RadioMetadata.createNicoNicoURI = id => `x-nn://${id}`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {string} id
|
||||||
|
* @returns {RadioMetadata}
|
||||||
|
*/
|
||||||
|
RadioMetadata.createInternetArchiveURI = id => `x-ia://${id}`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {bigint|string} post_id
|
||||||
|
* @params {string} username
|
||||||
|
* @returns {RadioMetadata}
|
||||||
|
*/
|
||||||
|
RadioMetadata.createTikTokURI = (post_id, username) => `x-tt://${username}/${post_id}`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {string} track
|
||||||
|
* @params {string} artist
|
||||||
|
* @returns {RadioMetadata}
|
||||||
|
*/
|
||||||
|
RadioMetadata.createBandcampURI = (track, artist) => `x-bc://${artist}/${track}`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {string} track
|
||||||
|
* @params {string} artist
|
||||||
|
* @returns {RadioMetadata}
|
||||||
|
*/
|
||||||
|
RadioMetadata.createSoundcloudURI = (track, artist) => `x-sc://${artist}/${track}`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {number} post_id
|
||||||
|
* @returns {RadioMetadata}
|
||||||
|
*/
|
||||||
|
RadioMetadata.createBooruSoyURI = id => `x-bs://${id}`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {number} track_id
|
||||||
|
* @params {number|undefined} album_id
|
||||||
|
* @returns {RadioMetadata}
|
||||||
|
*/
|
||||||
|
RadioMetadata.createAppleMusicURI = (track_id, album_id) => `x-am://${track_id}${album_id ? '/' + album_id : ''}`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {string} id
|
||||||
|
* @returns {RadioMetadata}
|
||||||
|
*/
|
||||||
|
RadioMetadata.createWikipediaURI = id => `x-wp://${id}`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {number} string
|
||||||
|
* @returns {RadioMetadata}
|
||||||
|
*/
|
||||||
|
RadioMetadata.createWikimediaCommonsURI = id => `x-wc://${id}`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to differentiate identical info bits for different info items
|
||||||
|
* @public
|
||||||
|
* @params {string} key
|
||||||
|
* @returns {RadioMetadata}
|
||||||
|
*/
|
||||||
|
RadioMetadata.prototype.setFor = function (key) {
|
||||||
|
this.for = key
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* @params {RadioSource} radioSource
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
RadioMetadata.prototype.serialize = function () {
|
||||||
|
return objSerialize(this, [
|
||||||
|
"info",
|
||||||
|
"for",
|
||||||
|
"title",
|
||||||
|
"artist",
|
||||||
|
"href",
|
||||||
|
"extra"
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key in BMetadataType) {
|
||||||
|
RadioMetadata.prototype[`b${fromConstToPascal(key)}`] = function () {
|
||||||
|
//this.info = (this.info || 0) | BMetadataType[key]
|
||||||
|
this.info |= BMetadataType[key]
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { RadioMetadata, BMetadataType }
|
52
lib/RadioMiscInfo.js
Normal file
52
lib/RadioMiscInfo.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
"use-strict";
|
||||||
|
const { fromConstToPascal } = require("./util.js")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
const BMiscInfo = {
|
||||||
|
"EXPLICIT": 0b1,
|
||||||
|
"SPLASH_SONG": 0b10,
|
||||||
|
"NIGGERS_SONG": 0b100,
|
||||||
|
"AMOGUS_SONG": 0b1000,
|
||||||
|
"METADATA_PREFER_ID": 0b10000,
|
||||||
|
"ALT_MEDIA": 0b100000
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @constructor
|
||||||
|
* @params {string|undefined} title
|
||||||
|
* @params {string|undefined} artist
|
||||||
|
* @params {URL|string|undefined} [href] href
|
||||||
|
* @params {string} [extra] extra Playback effects. i.e: Pitch, speed
|
||||||
|
* @returns {RadioMetadata}
|
||||||
|
*/
|
||||||
|
const RadioMiscInfo = function (value) {
|
||||||
|
this.miscInfo = value || 0
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {number|undefined} value
|
||||||
|
* @returns {RadioPlaybackInfo}
|
||||||
|
*/
|
||||||
|
RadioMiscInfo.new = value => new RadioMiscInfo(value)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @returns {number|undefined}
|
||||||
|
*/
|
||||||
|
RadioMiscInfo.prototype.serialize = function () { return this.miscInfo || undefined; }
|
||||||
|
|
||||||
|
for (const key in BMiscInfo) {
|
||||||
|
RadioMiscInfo.prototype[`b${fromConstToPascal(key)}`] = function () {
|
||||||
|
//this.miscInfo = (this.miscInfo || 0) | BMiscInfo[key]
|
||||||
|
this.miscInfo |= BMiscInfo[key]
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { RadioMiscInfo, BMiscInfo }
|
37
lib/RadioPlaybackInfo.js
Normal file
37
lib/RadioPlaybackInfo.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
"use-strict";
|
||||||
|
const { objSerialize } = require("./util.js")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @constructor
|
||||||
|
* @returns {RadioPlaybackInfo}
|
||||||
|
*/
|
||||||
|
const RadioPlaybackInfo = function () {
|
||||||
|
this.volume = undefined
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @returns {RadioPlaybackInfo}
|
||||||
|
*/
|
||||||
|
RadioPlaybackInfo.new = () => new RadioPlaybackInfo()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @param {number} volume - Playback volume. Crude version of ReplayGain
|
||||||
|
* @returns {RadioPlaybackInfo}
|
||||||
|
*/
|
||||||
|
RadioPlaybackInfo.prototype.setPlaybackVolume = function (volume) { this.volume = volume; return this; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
RadioPlaybackInfo.prototype.serialize = function () {
|
||||||
|
return objSerialize(this, [
|
||||||
|
"volume"
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { RadioPlaybackInfo }
|
62
lib/RadioPreview.js
Normal file
62
lib/RadioPreview.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
"use-strict";
|
||||||
|
const { objSerialize, notNullElse } = require("./util.js")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @readonly
|
||||||
|
* @enum {number}
|
||||||
|
*/
|
||||||
|
const EDisplayType = {
|
||||||
|
"NATIVE": -1,
|
||||||
|
"LANDSCAPE": 0,
|
||||||
|
"SQUARE": 1,
|
||||||
|
"STRETCH": 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @constructor
|
||||||
|
* @params {string} uri
|
||||||
|
* @params {string} type
|
||||||
|
* @params {string[]} codecs
|
||||||
|
* @params {String} size
|
||||||
|
* @params {EDisplayType} [displayType=null] size
|
||||||
|
* @returns {RadioPreview}
|
||||||
|
*/
|
||||||
|
const RadioPreview = function (uri, type, codecs, size, displayType=null) {
|
||||||
|
this.uri = uri
|
||||||
|
this.type = type
|
||||||
|
this.codecs = codecs?.length ? codecs : undefined
|
||||||
|
this.size = size.replace(/x/g, "×")
|
||||||
|
this.display_type = displayType
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {string} uri
|
||||||
|
* @params {string} type
|
||||||
|
* @params {string[]} codecs
|
||||||
|
* @params {String} size
|
||||||
|
* @params {EDisplayType} [displayType=null] size
|
||||||
|
* @returns {RadioPreview}
|
||||||
|
*/
|
||||||
|
RadioPreview.new = (uri, type, codecs, size, displayType) => new RadioPreview(uri, type, codecs, size, displayType)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* @params {RadioSource} radioSource
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
RadioPreview.prototype.serialize = function (radioSource) {
|
||||||
|
this.display_type = notNullElse(this.display_type, radioSource.type.startsWith("video/") ? EDisplayType.NATIVE : EDisplayType.SQUARE)
|
||||||
|
return objSerialize(this, [
|
||||||
|
"display_type",
|
||||||
|
"type",
|
||||||
|
"codecs",
|
||||||
|
"size",
|
||||||
|
"uri"
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { RadioPreview, EDisplayType }
|
126
lib/RadioSource.js
Normal file
126
lib/RadioSource.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
"use-strict";
|
||||||
|
const { objSerialize, objBulkSet } = require("./util.js")
|
||||||
|
const { RadioMetadata } = require("./RadioMetadata.js")
|
||||||
|
const { RadioPreview } = require("./RadioPreview.js")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @constructor
|
||||||
|
* @params {string} type
|
||||||
|
* @params {string[]} [codecs] codecs
|
||||||
|
* @returns {RadioSource}
|
||||||
|
*/
|
||||||
|
const RadioSource = function (type, codecs) {
|
||||||
|
this.type = type
|
||||||
|
this.uri = null
|
||||||
|
this.previews = []
|
||||||
|
this.setTypeWCodec(type, codecs)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {string} type
|
||||||
|
* @params {string[]} [codecs] codecs
|
||||||
|
* @returns {RadioSource}
|
||||||
|
*/
|
||||||
|
RadioSource.new = (type, codecs) => new RadioSource(type, codecs)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {string} uri
|
||||||
|
* @returns {RadioSource}
|
||||||
|
*/
|
||||||
|
RadioSource.prototype.setURI = function (uri) { this.uri = uri; return this }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set metadata that specifically applies to *this* source. Used for items where the audio has a different attribution than
|
||||||
|
* the video.
|
||||||
|
* @public
|
||||||
|
* @params {string|undefined} title
|
||||||
|
* @params {string|undefined} artist
|
||||||
|
* @params {URL|string|undefined} [href] href
|
||||||
|
* @params {string} [extra] extra Playback effects. i.e: Pitch, speed
|
||||||
|
* @returns {RadioSource}
|
||||||
|
*/
|
||||||
|
RadioSource.prototype.setMetadata = function (title, artist, href, extra) {
|
||||||
|
if (!this.metadata)
|
||||||
|
this.metadata = RadioMetadata.new()
|
||||||
|
objBulkSet(this, "metadata", { title, artist, href, extra });
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {RadioPlaybackInfo} playbackInfo
|
||||||
|
* @returns {RadioSource}
|
||||||
|
*/
|
||||||
|
RadioSource.prototype.setPlaybackInfo = function (playbackInfo) { this.playbackinfo = playbackInfo; return this; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {string} uri
|
||||||
|
* @returns {RadioSource}
|
||||||
|
*/
|
||||||
|
RadioSource.prototype.setTypeWCodec = function (type, codecs) {
|
||||||
|
this.type = type
|
||||||
|
this.codecs = codecs?.length ? codecs : undefined
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {string} uri
|
||||||
|
* @params {string} type
|
||||||
|
* @params {string} size
|
||||||
|
* @params {EDisplayType} [displayType] displayType
|
||||||
|
* @returns {RadioSource}
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @params {string} uri
|
||||||
|
* @params {string} type
|
||||||
|
* @params {string[]} codecs
|
||||||
|
* @params {String} size
|
||||||
|
* @params {EDisplayType} [displayType] displayType
|
||||||
|
* @returns {RadioSource}
|
||||||
|
*/
|
||||||
|
RadioSource.prototype.addPreview = function () {
|
||||||
|
let uri, type, codecs, size, displayType
|
||||||
|
if (typeof arguments[0] == "string" && typeof arguments[1] == "string" && arguments[2] instanceof Array) {
|
||||||
|
uri = arguments[0]
|
||||||
|
type = arguments[1]
|
||||||
|
codecs = arguments[2]
|
||||||
|
size = arguments[3]
|
||||||
|
displayType = arguments[4]
|
||||||
|
} else if (typeof arguments[0] == "string" && typeof arguments[1] == "string" && typeof arguments[2] == "string") {
|
||||||
|
uri = arguments[0]
|
||||||
|
type = arguments[1]
|
||||||
|
size = arguments[2]
|
||||||
|
displayType = arguments[3]
|
||||||
|
} else
|
||||||
|
throw new TypeError("Could not recognize arguments. Expecting [ LString, LString, LString, ...?] or [ LString, LString, LString[], ...?]")
|
||||||
|
|
||||||
|
this.previews.push(RadioPreview.new(uri, type, codecs, size, displayType))
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
RadioSource.prototype.serialize = function () {
|
||||||
|
return objSerialize(this, [
|
||||||
|
"type",
|
||||||
|
"codecs",
|
||||||
|
"uri"
|
||||||
|
], (obj, self) => ({
|
||||||
|
...obj,
|
||||||
|
"metadata": self.metadata ? self.metadata.serialize() : undefined,
|
||||||
|
"playbackinfo": self.playbackinfo ? self.playbackinfo.serialize() : undefined,
|
||||||
|
"previews": this.previews.map(preview => preview.serialize(obj))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { RadioSource }
|
17
lib/build.js
Normal file
17
lib/build.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
"use-strict";
|
||||||
|
const fs = require("fs")
|
||||||
|
const { arrSerializeSort, toRadioSongs } = require("./util.js")
|
||||||
|
const { mediaItemsLint, mediaItemsSerialize } = require("./lint.js")
|
||||||
|
let songs = arrSerializeSort(require("../data/songs.js"))
|
||||||
|
|
||||||
|
mediaItemsLint(songs)
|
||||||
|
songs = toRadioSongs(songs)
|
||||||
|
|
||||||
|
console.info(`I: Version ${songs.version} with ${songs.songs.length} entries`)
|
||||||
|
|
||||||
|
fs.writeFileSync("./data/songs.json", JSON.stringify(songs))
|
||||||
|
|
||||||
|
//console.info(require("util").inspect(mediaItems[0], { colors: true, depth: Infinity }))
|
||||||
|
//fs.writeFileSync("./data/songs.json", JSON.stringify(radioObj))
|
||||||
|
//fs.writeFileSync("./data/songs.js", `glowersRadioSongsCallback(${JSON.stringify(radioObj)})`)
|
||||||
|
//fs.writeFileSync("./data/songs.html", `<html><script>glowersRadioSongsCallback(${JSON.stringify(radioObj)})</script></html>`)
|
50
lib/lint.js
Normal file
50
lib/lint.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
"use-strict";
|
||||||
|
const { BMiscInfo } = require("./RadioMiscInfo.js")
|
||||||
|
const { EDisplayType } = require("./RadioPreview.js")
|
||||||
|
|
||||||
|
const CODEC_WARNINGS = {
|
||||||
|
"video/mp4": [
|
||||||
|
[ "warn", "avc1", "vague, however browsers will accept it. See MDN for format details" ],
|
||||||
|
[ "error", "mp4a", "too vague; Browsers will not accept it! See MDN for format details" ],
|
||||||
|
[ "error", "aac", "invalid! The correct codec is mp4a. See MDN for format details" ]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const CODEC_SUGGESTIONS = {
|
||||||
|
"video/mp4": 'codecs may be "avc1" with "mp4a.40.2" (AAC-LC)',
|
||||||
|
"audio/mp3": 'codec is probably "mp3"'
|
||||||
|
}
|
||||||
|
|
||||||
|
const mediaItemsLint = mediaItems => {
|
||||||
|
for (const item of mediaItems) {
|
||||||
|
const hasAudioSource = item.sources.some(a => a.type.startsWith("audio/"))
|
||||||
|
for (const index in item.sources) {
|
||||||
|
const source = item.sources[index]
|
||||||
|
|
||||||
|
if (!hasAudioSource && source.type.startsWith("video/") && !source.previews.some(item => item.display_type != EDisplayType.NATIVE)) {
|
||||||
|
const logType = (item?.misc & BMiscInfo.ALT_MEDIA) ? "warn" : "error"
|
||||||
|
console.warn(`${logType[0].toUpperCase()}: [previews] [${item.id}:${index}] No audio fallback preview set!`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source.codecs?.length)
|
||||||
|
for (const [ logType, codec, reason ] of (Object.hasOwn(CODEC_WARNINGS, source.type) ? CODEC_WARNINGS[source.type] : [])) {
|
||||||
|
if (!source.codecs.includes(codec))
|
||||||
|
continue
|
||||||
|
const msg = `${logType[0].toUpperCase()}: [codecs] [${item.id}:${index}] The declaration "${codec}" is ${reason}`
|
||||||
|
console[logType](msg)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
console.warn(`W: [codecs] [${item.id}:${index}] No codec declarations are set!${Object.hasOwn(CODEC_SUGGESTIONS, source.type)?` The ${CODEC_SUGGESTIONS[source.type]}, check with ffprobe.`:''}`)
|
||||||
|
|
||||||
|
for (const p_index in source.previews) {
|
||||||
|
const preview = item.sources[index].previews[p_index]
|
||||||
|
if (!preview.type)
|
||||||
|
console.warn(`W: [previews] [${item.id}:${index}] No type set for preview ${p_index} source ${index} of ${item.id}`)
|
||||||
|
if (!preview.size)
|
||||||
|
console.warn(`W: [previews] [${item.id}:${index}] No size set for preview ${p_index} source ${index} of ${item.id}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { mediaItemsLint }
|
51
lib/util.js
Normal file
51
lib/util.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
"use-strict";
|
||||||
|
|
||||||
|
const objSerialize = (obj, keys, cb) => {
|
||||||
|
let a = Object.create(null)
|
||||||
|
for (const key of keys)
|
||||||
|
a[key] = obj[key]
|
||||||
|
if (cb)
|
||||||
|
return cb(a, obj)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
const objBulkSet = (obj, target, pairs) => {
|
||||||
|
if (typeof obj[target] != "object")
|
||||||
|
obj[target] = Object.create(null)
|
||||||
|
for (const key in pairs)
|
||||||
|
if (pairs[key] != undefined)
|
||||||
|
obj[target][key] = pairs[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
const fromConstToPascal = string => {
|
||||||
|
let a = ''
|
||||||
|
let b = true
|
||||||
|
for (const char of string)
|
||||||
|
if (char == '_')
|
||||||
|
b = true
|
||||||
|
else {
|
||||||
|
a += b ? char : char.toLowerCase()
|
||||||
|
b = false
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
const notNullElse = (a, fallback) => a != null && a != undefined ? a : fallback
|
||||||
|
|
||||||
|
const arrSerializeSort = array => array.filter(Boolean).map((a,i) => a.serialize(i)).sort((a,b) => a.id.localeCompare(b.id))
|
||||||
|
|
||||||
|
const SCHEMA_VERSION = 7
|
||||||
|
const toRadioSongs = songs => ({
|
||||||
|
"version": SCHEMA_VERSION,
|
||||||
|
songs
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
SCHEMA_VERSION,
|
||||||
|
objSerialize,
|
||||||
|
objBulkSet,
|
||||||
|
arrSerializeSort,
|
||||||
|
fromConstToPascal,
|
||||||
|
notNullElse,
|
||||||
|
toRadioSongs
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user