Files
Bridge-Multi/src-shared/interfaces/search.interface.ts
Geomitron 353994b8e1 - Update API
- Add Chart Preview
- Add Drum Type dropdown when the "drums" instrument is selected
- Add Min/Max Year to advanced search
- Add Track Hash to advanced search
- Add "Download Video Backgrounds" setting
- Updated and improved detected chart issues
2024-07-16 15:20:58 -05:00

271 lines
11 KiB
TypeScript

import { FolderIssueType, Instrument, NotesData, ScannedChart } from 'scan-chart'
import { z } from 'zod'
import { difficulties, drumTypeNames, instruments } from '../UtilFunctions.js'
export const sources = ['website', 'bridge'] as const
export const GeneralSearchSchema = z.object({
search: z.string(),
page: z.number().positive(),
instrument: z.enum(instruments).nullable(),
difficulty: z.enum(difficulties).nullable(),
drumType: z.enum(drumTypeNames).nullable(),
source: z.enum(sources).optional(),
})
export type GeneralSearch = z.infer<typeof GeneralSearchSchema>
const md5Validator = z.string().regex(/^[a-f0-9]{32}$/, 'Invalid MD5 hash')
export const AdvancedSearchSchema = z.object({
instrument: z.string().refine(selectedInstruments => {
const values = selectedInstruments.split(',')
for (const value of values) {
if (!instruments.includes(value as Instrument)) { return false }
}
return true
}, { message: 'Invalid instrument list' }).nullable(),
difficulty: z.enum(difficulties).nullable(),
drumType: z.enum(drumTypeNames).nullable(),
source: z.enum(sources).optional(),
name: z.object({ value: z.string(), exact: z.boolean(), exclude: z.boolean() }),
artist: z.object({ value: z.string(), exact: z.boolean(), exclude: z.boolean() }),
album: z.object({ value: z.string(), exact: z.boolean(), exclude: z.boolean() }),
genre: z.object({ value: z.string(), exact: z.boolean(), exclude: z.boolean() }),
year: z.object({ value: z.string(), exact: z.boolean(), exclude: z.boolean() }),
charter: z.object({ value: z.string(), exact: z.boolean(), exclude: z.boolean() }),
minLength: z.number().nullable(),
maxLength: z.number().nullable(),
minIntensity: z.number().nullable(),
maxIntensity: z.number().nullable(),
minAverageNPS: z.number().nullable(),
maxAverageNPS: z.number().nullable(),
minMaxNPS: z.number().nullable(),
maxMaxNPS: z.number().nullable(),
modifiedAfter: z.string().regex(/^\d+-\d{2}-\d{2}$/, 'Invalid date').or(z.literal('')).nullable(),
hash: z.string().transform(data =>
data === '' || data.split(',').every(hash => md5Validator.safeParse(hash).success) ? data : 'invalid'
).nullable(),
hasSoloSections: z.boolean().nullable(),
hasForcedNotes: z.boolean().nullable(),
hasOpenNotes: z.boolean().nullable(),
hasTapNotes: z.boolean().nullable(),
hasLyrics: z.boolean().nullable(),
hasVocals: z.boolean().nullable(),
hasRollLanes: z.boolean().nullable(),
has2xKick: z.boolean().nullable(),
hasIssues: z.boolean().nullable(),
hasVideoBackground: z.boolean().nullable(),
modchart: z.boolean().nullable(),
})
export type AdvancedSearch = z.infer<typeof AdvancedSearchSchema>
export const advancedSearchTextProperties = [
'name',
'artist',
'album',
'genre',
'year',
'charter',
] as const
export const advancedSearchNumberProperties = [
'minLength',
'maxLength',
'minIntensity',
'maxIntensity',
'minAverageNPS',
'maxAverageNPS',
'minMaxNPS',
'maxMaxNPS',
] as const
export const advancedSearchBooleanProperties = [
'hasSoloSections',
'hasForcedNotes',
'hasOpenNotes',
'hasTapNotes',
'hasLyrics',
'hasVocals',
'hasRollLanes',
'has2xKick',
'hasIssues',
'hasVideoBackground',
'modchart',
] as const
export const ReportSchema = z.object({
chartId: z.number().positive(),
reason: z.string(),
extraInfo: z.string(),
})
export type Report = z.infer<typeof ReportSchema>
export interface FolderIssue {
folderIssue: FolderIssueType
description: string
}
export type ChartData = SearchResult['data'][number]
export interface SearchResult {
found: number
out_of: number
page: number
search_time_ms: number
data: {
/** The song name. */
name: string | null
/** The song artist. */
artist: string | null
/** The song album. */
album: string | null
/** The song genre. */
genre: string | null
/** The song year. */
year: string | null
/** The name of the chart, or `null` if the same as `name`. */
chartName: string | null
/** The genre of the chart, or `null` if the same as `genre`. */
chartGenre: string | null
/** The album of the chart, or `null` if the same as `album`. */
chartAlbum: string | null
/** The year of the chart, or `null` if the same as `year`. */
chartYear: string | null
/** The unique database identifier for the chart. */
chartId: number
/** The unique database identifier for the song, or `null` if there is only one chart of the song. */
songId: number | null
/** The unique database identifier for the song, or (-versionGroupId) if there is only one chart of the song. */
groupId: number
/** The MD5 hash of the normalized album art file. */
albumArtMd5: string | null
/** The MD5 hash of the chart folder or .sng file. */
md5: string
/**
* A blake3 hash of just the chart file and the .ini modifiers that impact chart parsing.
* If this changes, the in-game score is reset.
*/
chartHash: string
/**
* Different versions of the same chart have the same `versionGroupId`.
* All charts in a version group have this set to the smallest `id` in the group.
*/
versionGroupId: number
/** The chart's charter(s). */
charter: string | null
/** The length of the chart's audio, in milliseconds. If there are stems, this is the length of the longest stem. */
song_length: number | null
/** The difficulty rating of the chart as a whole. Usually an integer between 0 and 6 (inclusive) */
diff_band: number | null
/** The difficulty rating of the lead guitar chart. Usually an integer between 0 and 6 (inclusive) */
diff_guitar: number | null
/** The difficulty rating of the co-op guitar chart. Usually an integer between 0 and 6 (inclusive) */
diff_guitar_coop: number | null
/** The difficulty rating of the rhythm guitar chart. Usually an integer between 0 and 6 (inclusive) */
diff_rhythm: number | null
/** The difficulty rating of the bass guitar chart. Usually an integer between 0 and 6 (inclusive) */
diff_bass: number | null
/** The difficulty rating of the drums chart. Usually an integer between 0 and 6 (inclusive) */
diff_drums: number | null
/** The difficulty rating of the Phase Shift "real drums" chart. Usually an integer between 0 and 6 (inclusive) */
diff_drums_real: number | null
/** The difficulty rating of the keys chart. Usually an integer between 0 and 6 (inclusive) */
diff_keys: number | null
/** The difficulty rating of the GHL (6-fret) lead guitar chart. Usually an integer between 0 and 6 (inclusive) */
diff_guitarghl: number | null
/** The difficulty rating of the GHL (6-fret) co-op guitar chart. Usually an integer between 0 and 6 (inclusive) */
diff_guitar_coop_ghl: number | null
/** The difficulty rating of the GHL (6-fret) rhythm guitar chart. Usually an integer between 0 and 6 (inclusive) */
diff_rhythm_ghl: number | null
/** The difficulty rating of the GHL (6-fret) bass guitar chart. Usually an integer between 0 and 6 (inclusive) */
diff_bassghl: number | null
/** The difficulty rating of the vocals chart. Usually an integer between 0 and 6 (inclusive) */
diff_vocals: number | null
/** The number of milliseconds into the song where the chart's audio preview should start playing. */
preview_start_time: number | null
/** The name of the icon to be displayed on the chart. Usually represents a charter or setlist. */
icon: string | null
/** A text phrase that will be displayed before the chart begins. */
loading_phrase: string | null
/** The ordinal position of the song on the album. This is `undefined` if it's not on an album. */
album_track: number | null
/** The ordinal position of the chart in its setlist. This is `undefined` if it's not on a setlist. */
playlist_track: number | null
/** `true` if the chart is a modchart. This only affects how the chart is filtered and displayed, and doesn't impact gameplay. */
modchart: boolean | null
/** The amount of time the game should delay the start of the track in milliseconds. */
delay: number | null
/** The amount of time the game should delay the start of the track in seconds. */
chart_offset: number | null
/** Overrides the default HOPO threshold with a specified value in ticks. Only applies to .mid charts. */
hopo_frequency: number | null
/** Sets the HOPO threshold to be a 1/8th step. Only applies to .mid charts. */
eighthnote_hopo: boolean | null
/** Overrides the .mid note number for Star Power on 5-Fret Guitar. Valid values are 103 and 116. Only applies to .mid charts. */
multiplier_note: number | null
/**
* For .mid charts, setting this causes any sustains not larger than the threshold (in number of ticks) to be reduced to length 0.
* By default, this happens to .mid sustains shorter than 1/12 step.
*/
sustain_cutoff_threshold?: number
/**
* Notes at or closer than this threshold (in number of ticks) will be merged into a chord.
* All note and modifier ticks are set to the tick of the earliest merged note.
* All note sustains are set to the length of the shortest merged note.
*/
chord_snap_threshold?: number
/**
* The amount of time that should be skipped from the beginning of the video background in milliseconds.
* A negative value will delay the start of the video by that many milliseconds.
*/
video_start_time: number | null
/** `true` if the "drums" track should be interpreted as 5-lane drums. */
five_lane_drums: boolean | null
/** `true` if the "drums" track should be interpreted as 4-lane pro drums. */
pro_drums: boolean | null
/** `true` if the chart's end events should be used to end the chart early. Only applies to .mid charts. */
end_events: boolean | null
/** Data describing properties of the .chart or .mid file. `undefined` if the .chart or .mid file couldn't be parsed. */
notesData: NotesData
/** Issues with the chart files. */
folderIssues: ScannedChart['folderIssues']
/** Issues with the chart's metadata. */
metadataIssues: ScannedChart['metadataIssues']
/** `true` if the chart has a video background. */
hasVideoBackground: boolean
/** The date of the last time this chart was modified in Google Drive. */
modifiedTime: string
/** The Drive ID of the chart's application folder. */
applicationDriveId: string
/** The primary username of the chart's application's applicant, or `null` if packName is not `null` */
applicationUsername: string
/** The name of the pack source, or `null` if the application is not a pack source. */
packName: string | null
/** The `folderId` of the Google Drive folder that contains the chart (or the shortcut to it). */
parentFolderId: string
/**
* A string containing the relative path from the application folder to the `DriveChart`.
*
* Doesn't contain the application folder name, the file name (for file charts), or leading/trailing slashes.
*/
drivePath: string
/** The Drive ID of the chart file, or `null` if the chart is a chart folder. */
driveFileId: string | null
/** The file name of the chart file, or `null` if the chart is a chart folder. */
driveFileName: string | null
/** If there is more than one chart contained inside this `DriveChart`. */
driveChartIsPack: boolean
/**
* A string containing the relative path from the driveChart's root to the chart inside it.
*
* This starts with the archive name if the driveChart is an archive.
*
* This ends with the name of the .sng file if this is an .sng file.
*
* This is an empty string if the driveChart is not an archive or an .sng file.
*/
internalPath: string
}[]
}