feat: add lyricResolve for resole .lrc file

feat: add backgroundDisplay for play ascii video in terminal
	feat: add musicPlayer for play music in terminal
This commit is contained in:
2026-08-30 11:35:47 +08:00
parent fdfbf5262f
commit 00b7c0b324
16 changed files with 97267 additions and 15 deletions
+76
View File
@@ -0,0 +1,76 @@
// backgroundDisplay.h
//
// Decode a video file (MP4 or any FFmpeg-supported container/codec) and play
// it in the terminal as truecolor ASCII art, honouring the video's aspect
// ratio.
//
// Terminal behaviour:
// - If the terminal can be resized programmatically ("adjustable"), the
// current width is kept and the height is set so the ASCII grid matches
// the video aspect ratio (terminal cells are roughly 2:1 tall:wide).
// - Otherwise the video is letterboxed to the largest size that fits the
// current terminal and the video frame size (e.g. "1280x720") is printed
// in the top-left corner of the terminal.
//
// This module is self-contained (raw ANSI escape sequences, no ncurses).
// Decoding runs on a background thread; playVideo() blocks the calling
// thread for the duration of playback, so callers can run several show
// components concurrently by using their own std::thread.
#pragma once
#include <string>
#include <atomic>
namespace bd {
struct TermSize {
int cols = 0; // character columns
int rows = 0; // character rows
bool ok = false; // false when stdout is not a tty or the size is unknown
};
struct GridSize {
int cols = 0;
int rows = 0;
};
// --- terminal helpers -----------------------------------------------------
// True if stdout is an interactive terminal (isatty).
bool isTerminal();
// Current terminal size in character cells.
TermSize terminalSize();
// Ask the terminal to resize to (rows x cols) via the xterm sequence
// "\x1b[8;rows;colst" and verify it actually took effect (TIOCGWINSZ).
bool resizeTerminal(int rows, int cols);
// Probe whether the terminal honours programmatic resize requests. Restores
// the original size afterwards.
bool isTerminalAdjustable();
// Largest whole grid (cols x rows) with the video aspect ratio that fits
// inside (maxCols x maxRows). Rows are counted with 2:1 cell aspect.
GridSize fitSizeForAspect(int videoW, int videoH, int maxCols, int maxRows);
// --- video playback -------------------------------------------------------
struct PlayVideoOptions {
std::string path;
bool loop = false; // repeat the video until stop
float intensity = 1.0f; // brightness multiplier for fg/bg
bool tryResize = true; // false => force the "not adjustable" fit path
int bottomRows = 0; // leave this many rows at the bottom for other content
std::atomic<bool>* stop = nullptr; // set to true to end playback early
};
// Play `path` as ASCII art. Blocks until the video ends (or `stop` is set).
// Returns:
// 0 playback finished (or stopped) cleanly
// 1 failed to open/decode the video
// 2 stdout is not a tty (nothing was written)
int playVideo(const std::string& path, const PlayVideoOptions& opts = {});
} // namespace bd