@exezt-/webamp-custom
Version:
Winamp 2 implemented in HTML5 and JavaScript
733 lines (732 loc) • 19.1 kB
TypeScript
import { PlaylistState } from "./reducers/playlist";
import { SettingsState } from "./reducers/settings";
import { UserInputState } from "./reducers/userInput";
import { MediaState } from "./reducers/media";
import { ThunkDispatch, ThunkAction } from "redux-thunk";
import { DisplayState } from "./reducers/display";
import { WindowsState, WindowPositions as _WindowPositions, WebampWindow as _WebampWindow, WindowInfo as _WindowInfo } from "./reducers/windows";
import { EqualizerState } from "./reducers/equalizer";
import { NetworkState } from "./reducers/network";
import { MilkdropState } from "./reducers/milkdrop";
import { SerializedStateV1 } from "./serializedStates/v1Types";
import { TracksState } from "./reducers/tracks";
import { IAudioMetadata, IOptions } from "music-metadata-browser";
import { Store as ReduxStore } from "redux";
export type WebampWindow = _WebampWindow;
export type WindowInfo = _WindowInfo;
export type WindowPositions = _WindowPositions;
export interface Point {
x: number;
y: number;
}
export interface Diff {
x?: number;
y?: number;
}
export interface BoundingBox {
width: number;
height: number;
}
export interface Box extends Point {
width: number;
height: number;
}
export interface FilePicker {
contextMenuName: string;
filePicker: () => Promise<Track[]>;
requiresNetwork: boolean;
}
export type Skin = {
url: string;
name: string;
};
export interface MilkdropMessage {
text: string;
time: number;
}
export type Band = 60 | 170 | 310 | 600 | 1000 | 3000 | 6000 | 12000 | 14000 | 16000;
export type Slider = Band | "preamp";
export type CursorImage = {
type: "cur";
url: string;
} | {
type: "ani";
aniData: Uint8Array;
};
export type Cursors = {
[cursor: string]: CursorImage;
};
export type GenLetterWidths = {
[letter: string]: number;
};
export interface PlaylistStyle {
normal: string;
current: string;
normalbg: string;
selectedbg: string;
font: string;
}
export type SkinImages = {
[sprite: string]: string;
};
export type SkinRegion = {
[windowName: string]: string[];
};
export type DummyVizData = {
0: 11.75;
8: 11.0625;
16: 8.5;
24: 7.3125;
32: 6.75;
40: 6.4375;
48: 6.25;
56: 5.875;
64: 5.625;
72: 5.25;
80: 5.125;
88: 4.875;
96: 4.8125;
104: 4.375;
112: 3.625;
120: 1.5625;
};
export interface SkinGenExColors {
itemBackground: string;
itemForeground: string;
windowBackground: string;
buttonText: string;
windowText: string;
divider: string;
playlistSelection: string;
listHeaderBackground: string;
listHeaderText: string;
listHeaderFrameTopAndLeft: string;
listHeaderFrameBottomAndRight: string;
listHeaderFramePressed: string;
listHeaderDeadArea: string;
scrollbarOne: string;
scrollbarTwo: string;
pressedScrollbarOne: string;
pressedScrollbarTwo: string;
scrollbarDeadArea: string;
listTextHighlighted: string;
listTextHighlightedBackground: string;
listTextSelected: string;
listTextSelectedBackground: string;
}
export type WindowId = "main" | "playlist" | "equalizer" | "milkdrop";
export type SkinData = {
skinImages: SkinImages;
skinColors: string[];
skinPlaylistStyle: PlaylistStyle;
skinCursors: Cursors;
skinRegion: SkinRegion;
skinGenLetterWidths: GenLetterWidths;
skinGenExColors: SkinGenExColors | null;
};
export type ButterchurnPresetJson = {
name: string;
butterchurnPresetObject: Object;
};
export interface ButterchurnPresetUrl {
name: string;
butterchurnPresetUrl: string;
}
export type LazyButterchurnPresetJson = {
name: string;
getButterchrunPresetObject: () => Promise<Object>;
};
export type Preset = ButterchurnPresetJson | ButterchurnPresetUrl | LazyButterchurnPresetJson;
export type StatePreset = {
type: "RESOLVED";
name: string;
preset: Object;
} | {
type: "UNRESOLVED";
name: string;
getPreset: () => Promise<Object>;
};
export interface ButterchurnOptions {
getPresets(): Promise<Preset[]>;
importButterchurn(): Promise<any>;
importConvertPreset?: () => Promise<{
convertPreset(file: string, endpoint: string): Promise<Object>;
}>;
presetConverterEndpoint?: string;
butterchurnOpen: boolean;
}
export interface EqfPreset {
name: string;
hz60: number;
hz170: number;
hz310: number;
hz600: number;
hz1000: number;
hz3000: number;
hz12000: number;
hz14000: number;
hz16000: number;
hz6000: number;
preamp: number;
}
export declare enum TransitionType {
IMMEDIATE = 0,
DEFAULT = 1,
USER_PRESET = 2
}
export interface Size {
width: number;
height: number;
}
export type Action = {
type: "@@init";
} | {
type: "NETWORK_CONNECTED";
} | {
type: "NETWORK_DISCONNECTED";
} | {
type: "SET_AVAILABLE_SKINS";
skins: Array<Skin>;
} | {
type: "PLAY";
} | {
type: "IS_PLAYING";
} | {
type: "PAUSE";
} | {
type: "STOP";
} | {
type: "IS_STOPPED";
} | {
type: "TOGGLE_TIME_MODE";
} | {
type: "UPDATE_TIME_ELAPSED";
elapsed: number;
} | {
type: "ADD_TRACK_FROM_URL";
atIndex: number | null;
id: number;
defaultName?: string;
duration?: number;
url: string;
} | {
type: "SET_MEDIA";
id: number;
length: number;
kbps: string;
khz: string;
channels: number;
} | {
type: "SET_VOLUME";
volume: number;
} | {
type: "SET_BALANCE";
balance: number;
} | {
type: "TOGGLE_REPEAT";
} | {
type: "TOGGLE_SHUFFLE";
} | {
type: "SET_FOCUS";
input: string;
} | {
type: "SET_BAND_FOCUS";
input: string;
bandFocused: Slider;
} | {
type: "UNSET_FOCUS";
} | {
type: "SET_SCRUB_POSITION";
position: number;
} | {
type: "SET_USER_MESSAGE";
message: string;
} | {
type: "UNSET_USER_MESSAGE";
} | {
type: "TOGGLE_DOUBLESIZE_MODE";
} | {
type: "TOGGLE_LLAMA_MODE";
} | {
type: "STEP_MARQUEE";
} | {
type: "DISABLE_MARQUEE";
} | {
type: "STOP_WORKING";
} | {
type: "START_WORKING";
} | {
type: "CLOSE_WINAMP";
} | {
type: "OPEN_WINAMP";
} | {
type: "LOADING";
} | {
type: "LOADED";
} | {
type: "SET_SKIN_DATA";
data: SkinData;
} | {
type: "TOGGLE_VISUALIZER_STYLE";
} | {
type: "SET_PLAYLIST_SCROLL_POSITION";
position: number;
} | {
type: "SET_Z_INDEX";
zIndex: number;
} | {
type: "SET_DUMMY_VIZ_DATA";
data: DummyVizData;
} | {
type: "SET_BAND_VALUE";
band: Slider;
value: number;
} | {
type: "SET_EQ_ON";
} | {
type: "SET_EQ_OFF";
} | {
type: "SET_EQ_AUTO";
value: boolean;
} | {
type: "SET_FOCUSED_WINDOW";
window: WindowId | null;
} | {
type: "TOGGLE_WINDOW_SHADE_MODE";
windowId: WindowId;
} | {
type: "TOGGLE_WINDOW";
windowId: WindowId;
} | {
type: "CLOSE_WINDOW";
windowId: WindowId;
} | {
type: "SET_WINDOW_VISIBILITY";
windowId: WindowId;
hidden: boolean;
} | {
type: "ADD_GEN_WINDOW";
windowId: WindowId;
title: string;
open: boolean;
} | {
type: "WINDOW_SIZE_CHANGED";
windowId: WindowId;
size: [number, number];
} | {
type: "UPDATE_WINDOW_POSITIONS";
positions: WindowPositions;
absolute?: boolean;
} | {
type: "CLICKED_TRACK";
index: number;
} | {
type: "CTRL_CLICKED_TRACK";
index: number;
} | {
type: "SHIFT_CLICKED_TRACK";
index: number;
} | {
type: "SELECT_ALL";
} | {
type: "SELECT_ZERO";
} | {
type: "INVERT_SELECTION";
} | {
type: "REMOVE_ALL_TRACKS";
} | {
type: "REMOVE_TRACKS";
ids: number[];
} | {
type: "REVERSE_LIST";
} | {
type: "RANDOMIZE_LIST";
} | {
type: "SET_TRACK_ORDER";
trackOrder: number[];
} | {
type: "SET_MEDIA_TAGS";
id: number;
title: string;
artist: string;
album?: string;
albumArtUrl?: string | null;
numberOfChannels?: number;
bitrate?: number;
sampleRate?: number;
} | {
type: "MEDIA_TAG_REQUEST_INITIALIZED";
id: number;
} | {
type: "MEDIA_TAG_REQUEST_FAILED";
id: number;
} | {
type: "SET_MEDIA_DURATION";
id: number;
duration: number;
} | {
type: "PLAY_TRACK";
id: number;
} | {
type: "BUFFER_TRACK";
id: number;
} | {
type: "DRAG_SELECTED";
offset: number;
} | {
type: "PLAY";
} | {
type: "PAUSE";
} | {
type: "SEEK_TO_PERCENT_COMPLETE";
percent: number;
} | {
type: "MINIMIZE_WINAMP";
} | {
type: "CLOSE_REQUESTED";
cancel: () => void;
} | {
type: "LOAD_SERIALIZED_STATE";
serializedState: SerializedStateV1;
} | {
type: "RESET_WINDOW_SIZES";
} | {
type: "BROWSER_WINDOW_SIZE_CHANGED";
height: number;
width: number;
} | {
type: "LOAD_DEFAULT_SKIN";
} | {
type: "ENABLE_MILKDROP";
open: boolean;
} | {
type: "SCHEDULE_MILKDROP_MESSAGE";
message: string;
} | {
type: "SET_MILKDROP_DESKTOP";
enabled: boolean;
} | {
type: "SET_MILKDROP_FULLSCREEN";
enabled: boolean;
} | {
type: "PRESET_REQUESTED";
index: number;
addToHistory: boolean;
} | {
type: "GOT_BUTTERCHURN_PRESETS";
presets: StatePreset[];
} | {
type: "GOT_BUTTERCHURN";
butterchurn: any;
} | {
type: "TOGGLE_RANDOMIZE_PRESETS";
} | {
type: "TOGGLE_PRESET_CYCLING";
} | {
type: "RESOLVE_PRESET_AT_INDEX";
index: number;
json: Object;
} | {
type: "SELECT_PRESET_AT_INDEX";
index: number;
transitionType: TransitionType;
} | {
type: "TOGGLE_PRESET_OVERLAY";
} | {
type: "MAIN_CONTEXT_MENU_OPENED";
} | {
type: "DROPPED_FILES";
count: number;
firstFileName: string | null;
windowId: WindowId;
} | {
type: "OPENED_FILES";
expectedType: "SKIN" | "MEDIA" | "EQ";
count: number;
firstFileName: string | null;
};
export type MediaTagRequestStatus = "INITIALIZED" | "FAILED" | "COMPLETE" | "NOT_REQUESTED";
export type MediaStatus = "PLAYING" | "STOPPED" | "PAUSED";
export type LoadStyle = "BUFFER" | "PLAY" | "NONE";
export type TimeMode = "ELAPSED" | "REMAINING";
interface TrackInfo {
/**
* Name to be used until ID3 tags can be resolved.
*
* If the track has a `url`, and this property is not given,
* the filename will be used instead.
*
* Example: `'My Song'`
*/
defaultName?: string;
/**
* Data to be used _instead_ of trying to fetch ID3 tags.
*
* Example: `{ artist: 'Jordan Eldredge', title: "Jordan's Song" }`
*/
metaData?: {
artist: string;
title: string;
album?: string;
albumArtUrl?: string;
};
/**
* Duration (in seconds) to be used instead of fetching enough of the file to measure its length.
*
* Example: 95
*/
duration?: number;
}
export interface URLTrack extends TrackInfo {
/**
* Source URL of the track
*
* Note: This URL must be served the with correct CORs headers.
*
* Example: `'https://example.com/song.mp3'`
*/
url: string;
}
export interface BlobTrack extends TrackInfo {
/**
* Blob source of the track
*/
blob: Blob;
}
export interface LoadedURLTrack {
url: string;
metaData: {
artist: string | null;
title: string | null;
album: string | null;
albumArtUrl: string | null;
};
}
export interface Options {
/**
* An object representing the initial skin to use.
*
* If omitted, the default skin, included in the bundle, will be used.
* Note: This URL must be served the with correct CORs headers.
*
* Example: `{ url: './path/to/skin.wsz' }`
*/
initialSkin?: {
url: string;
};
/**
* An array of `Track`s to prepopulate the playlist with.
*/
initialTracks?: Track[];
/**
* An array of objects representing available skins.
*
* These will appear in the "Options" menu under "Skins".
* Note: These URLs must be served with the correct CORs headers.
*
* Example: `[ { url: "./green.wsz", name: "Green Dimension V2" } ]`
*/
availableSkins?: {
url: string;
name: string;
}[];
/**
* Configure how the Winamp windows should be laid out on initial render.
*/
windowLayout?: WindowLayout;
/**
* Controls if "double size mode", where the fixed sized windows are rendered
* at 2x, should be enabled
*
* **Note:** In keeping with the original Winamp, double size mode does not
* apply to resizable windows like the equalizer or Milkdrop.
*
* Default: `false`
*/
enableDoubleSizeMode?: boolean;
/**
* Should global hotkeys be enabled?
*
* Default: `false`
*/
enableHotkeys?: boolean;
/**
* An array of additional file pickers.
*
* These will appear in the "Options" menu under "Play".
*
* For example, this option can be used to provide a "Dropbox" file picker.
*/
filePickers?: [
{
/**
* The name that will appear in the context menu.
*
* Example: `"My File Picker..."`
*/
contextMenuName: string;
/**
* A function which returns a Promise that resolves to an array of `Track`s
*
* Example: `() => Promise.resolve([{ url: './rick_roll.mp3' }])`
*/
filePicker: () => Promise<Track[]>;
/**
* Indicates if this options should be made available when the user is offline.
*/
requiresNetwork: boolean;
}
];
zIndex?: number;
handleTrackDropEvent?: (e: React.DragEvent<HTMLDivElement>) => Track[] | null | Promise<Track[] | null>;
handleAddUrlEvent?: () => Track[] | null | Promise<Track[] | null>;
handleLoadListEvent?: () => Track[] | null | Promise<Track[] | null>;
handleSaveListEvent?: (tracks: Track[]) => null | Promise<null>;
}
/**
* Specifies the initial position and size of a the Winamp windows.
*
* Positions are specified in pixels from the top left corner of an imaginary
* box. On initial render, the collection of visible windows will be centered
* within the HTML element passed to `Webamp.renderWhenReady(element)`. In other
* words, the positions given here will determine the _relative_ position of the
* windows. The absolute position will be determined by the HTML element in
* which Webamp is centered.
*
* Enabling "shade mode" for a window that supports it, will cause it to be
* rendered minimized. Be default windows are not in shade mode.
*
* Windows which support resizing can have their size specified. If omitted,
* they default to their small base size.
*
* Windows that are omitted will start closed. Enabling "closed" for a window
* that supports it, will cause it to start closed.
*/
export type WindowLayout = {
main?: {
position: WindowPosition;
shadeMode?: boolean;
closed?: boolean;
};
equalizer?: {
position: WindowPosition;
shadeMode?: boolean;
closed?: boolean;
};
playlist?: {
position: WindowPosition;
shadeMode?: boolean;
size?: WindowSize | null;
closed?: boolean;
};
milkdrop?: {
position: WindowPosition;
size?: WindowSize | null;
closed?: boolean;
};
};
/**
* Offset from the top left corner of an imaginary box.
*/
export type WindowPosition = {
top: number;
left: number;
};
/**
* Resizable windows in Winamp have a base size and can be expanded in
* increments based on the size of the skin sprite.
*
* To specify a window being larger than its base size, use `extraHeight` to
* specify how many sprite increments to expand the window's height by, and
* `extraWidth` to specify how many sprite increments to expand the window's
* width by.
*/
export type WindowSize = {
extraHeight: number;
extraWidth: number;
};
/**
* Many methods on the webamp instance deal with track.
*
* Either `url` or `blob` must be specified
*/
export type Track = URLTrack | BlobTrack;
export interface PlaylistTrack {
id: number;
artist?: string;
title?: string;
album?: string;
url: string;
defaultName: string | null;
albumArtUrl?: string | null;
mediaTagsRequestStatus: MediaTagRequestStatus;
duration: number | null;
kbps?: string;
khz: string;
channels?: number;
}
export interface AppState {
userInput: UserInputState;
windows: WindowsState;
display: DisplayState;
settings: SettingsState;
equalizer: EqualizerState;
playlist: PlaylistState;
media: MediaState;
network: NetworkState;
tracks: TracksState;
milkdrop: MilkdropState;
}
export type PartialState = any;
/**
* Type definition of the music-metadata-browser module.
* Ref: https://github.com/Borewit/music-metadata-browser/blob/master/src/index.ts
*/
export interface IMusicMetadataBrowserApi {
/**
* Parse Web API File
* @param {Blob} blob
* @param {IOptions} options Parsing options
* @returns {Promise<IAudioMetadata>}
*/
parseBlob(blob: Blob, options?: IOptions): Promise<IAudioMetadata>;
/**
* Parse fetched file, using the Web Fetch API
* @param {string} audioTrackUrl URL to download the audio track from
* @param {IOptions} options Parsing options
* @returns {Promise<IAudioMetadata>}
*/
fetchFromUrl(audioTrackUrl: string, options?: IOptions): Promise<IAudioMetadata>;
/**
* Parse audio from Node Buffer
* @param {Stream.Readable} stream Audio input stream
* @param {string} mimeType <string> Content specification MIME-type, e.g.: 'audio/mpeg'
* @param {IOptions} options Parsing options
* @returns {Promise<IAudioMetadata>}
*/
parseBuffer(buf: Buffer, mimeType?: string, options?: IOptions): Promise<IAudioMetadata>;
}
export interface Extras {
requireJSZip(): Promise<any>;
requireMusicMetadata(): Promise<IMusicMetadataBrowserApi>;
convertPreset: ((file: File) => Promise<Object>) | null;
handleTrackDropEvent?: (e: React.DragEvent<HTMLDivElement>) => Track[] | null | Promise<Track[] | null>;
handleAddUrlEvent?: () => Track[] | null | Promise<Track[] | null>;
handleLoadListEvent?: () => Track[] | null | Promise<Track[] | null>;
handleSaveListEvent?: (tracks: Track[]) => null | Promise<null>;
}
export type GetState = () => AppState;
export type Thunk = ThunkAction<void, AppState, Extras, Action>;
export type Dispatch = ThunkDispatch<AppState, Extras, Action>;
export type Reducer = (state: AppState, action: Action) => AppState;
export type Middleware = (store: MiddlewareStore) => (next: Dispatch) => (action: Action) => any;
export interface Store extends ReduxStore {
subscribe(cb: () => void): () => void;
dispatch: Dispatch;
getState: GetState;
}
export interface MiddlewareStore {
dispatch: Dispatch;
getState: GetState;
}
export {};