Compare commits
2 Commits
Author | SHA1 | Date |
---|---|---|
capta1nseal | fb0126547a | |
capta1nseal | 544c8a9bab |
15
cc-pic.py
15
cc-pic.py
|
@ -74,14 +74,13 @@ class Converter:
|
||||||
|
|
||||||
MAX_DIFF = 3 * 255
|
MAX_DIFF = 3 * 255
|
||||||
|
|
||||||
def __init__(self, image: Image.Image, palette: list[int] | int = PALETTE_ADAPTIVE, dither: bool = True):
|
def __init__(self, image: Image.Image, palette: list[int] | int = PALETTE_ADAPTIVE):
|
||||||
dither_mode = Image.Dither.FLOYDSTEINBERG if dither else Image.Dither.NONE
|
|
||||||
if isinstance(palette, list):
|
if isinstance(palette, list):
|
||||||
img_pal = Image.new("P", (1, 1))
|
img_pal = Image.new("P", (1, 1))
|
||||||
img_pal.putpalette(palette)
|
img_pal.putpalette(palette)
|
||||||
self._img = image.quantize(len(palette) // 3, palette=img_pal, dither=dither_mode)
|
self._img = image.quantize(len(palette) // 3, palette=img_pal)
|
||||||
else:
|
else:
|
||||||
self._img = image.convert("P", palette=palette, colors=16, dither=dither_mode)
|
self._img = image.convert("P", palette=palette, colors=16)
|
||||||
|
|
||||||
self._imgdata = self._img.load()
|
self._imgdata = self._img.load()
|
||||||
self._palette: list[int] = self._img.getpalette() or []
|
self._palette: list[int] = self._img.getpalette() or []
|
||||||
|
@ -222,12 +221,6 @@ def main():
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Output a Lua script instead of binary image",
|
help="Output a Lua script instead of binary image",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
|
||||||
"-D",
|
|
||||||
dest="nodither",
|
|
||||||
action="store_true",
|
|
||||||
help="Disable dithering"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-W",
|
"-W",
|
||||||
dest="width",
|
dest="width",
|
||||||
|
@ -398,7 +391,7 @@ def main():
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"invalid palette identifier: {args.palette!r}")
|
raise ValueError(f"invalid palette identifier: {args.palette!r}")
|
||||||
|
|
||||||
converter = Converter(canv, palette, dither=not args.nodither)
|
converter = Converter(canv, palette)
|
||||||
converter._img.save("/tmp/_ccpictmp.png")
|
converter._img.save("/tmp/_ccpictmp.png")
|
||||||
if args.textmode:
|
if args.textmode:
|
||||||
with open(args.output_path, "w") as fp:
|
with open(args.output_path, "w") as fp:
|
||||||
|
|
|
@ -10,8 +10,8 @@ cleanup() {
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
|
||||||
export INPUT="$1";
|
export URL="$1";
|
||||||
export OUTPUT="$(realpath "$2")";
|
export NAME="$2";
|
||||||
export BASE_URL="$3"
|
export BASE_URL="$3"
|
||||||
|
|
||||||
if [ -z "${BASE_URL}" ]; then
|
if [ -z "${BASE_URL}" ]; then
|
||||||
|
@ -19,21 +19,24 @@ if [ -z "${BASE_URL}" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
mkdir -p "${OUTPUT}"
|
mkdir -p "${NAME}"
|
||||||
|
|
||||||
export ORIG="$(pwd)";
|
export ORIG="$(pwd)";
|
||||||
|
|
||||||
cd "${TMP_DIR}"
|
cd "${TMP_DIR}"
|
||||||
|
|
||||||
ffmpeg -i "${INPUT}" -filter_complex "[0:a]channelsplit=channel_layout=stereo[left][right]" -map '[left]' -f s8 -ac 1 -ar 48k "${OUTPUT}/left.s8" -map '[right]' -f s8 -ac 1 -ar 48k "${OUTPUT}/right.s8"
|
yt-dlp "${URL}" -S "+height:720" -f "b" -o "${NAME}"
|
||||||
ffmpeg -i "${INPUT}" -vf fps=20 frame%04d.png
|
|
||||||
|
|
||||||
ls frame*.png | parallel 'echo {}; python3 ${ORIG}/cc-pic.py -W 164 -H 81 -p cover {} ${OUTPUT}/{.}.cpi'
|
ffmpeg -i $2* -filter_complex "[0:a]channelsplit=channel_layout=stereo[left][right]" -map '[left]' -f s8 -ac 1 -ar 48k "${ORIG}/${NAME}/left.s8" -map '[right]' -f s8 -ac 1 -ar 48k "${ORIG}/${NAME}/right.s8"
|
||||||
|
|
||||||
|
ffmpeg -i $2* -vf fps=20 frame%04d.png
|
||||||
|
rm $2*
|
||||||
|
ls frame*.png | parallel 'echo {}; python3 ${ORIG}/cc-pic.py -W 164 -H 81 -p full {} ${ORIG}/${NAME}/{.}.cpi'
|
||||||
rm frame*.png
|
rm frame*.png
|
||||||
|
|
||||||
cd "${ORIG}"
|
cd "${ORIG}"
|
||||||
|
|
||||||
export FRAME_COUNT="$(ls ${OUTPUT}/*.cpi | wc -l)";
|
export FRAME_COUNT="$(ls ${NAME}/*.cpi | wc -l)";
|
||||||
|
|
||||||
printf '{"frame_time": 0.05, "frame_count": %d, "video": "%s", "audio": {"l": "%s", "r": "%s"}}\n' "${FRAME_COUNT}" "${BASE_URL}/frame%04d.cpi" "${BASE_URL}/left.s8" "${BASE_URL}/right.s8" > "${OUTPUT}/info.json"
|
|
||||||
|
|
||||||
|
#'{"frame_time": 0.05,"frame_count": ${FRAME_COUNT},"video": "${BASE_URL}/${NAME}/frame%04d.cpi","audio": {"l": "${BASE_URL}/${NAME}/left.s8", "r": "${BASE_URL}/${NAME}/right.s8"}}' > "${NAME}/${NAME}.json"
|
||||||
|
printf '{"frame_time": 0.05, "frame_count": %d, "video": "%s", "audio": {"l": "%s", "r": "%s"}}\n' "${FRAME_COUNT}" "${BASE_URL}/${NAME}/frame%04d.cpi" "${BASE_URL}/${NAME}/left.s8" "${BASE_URL}/${NAME}/right.s8" > "${NAME}/${NAME}.json"
|
||||||
|
|
31
video.lua
31
video.lua
|
@ -2,8 +2,6 @@ local args = { ... }
|
||||||
local dfpwm = require("cc.audio.dfpwm")
|
local dfpwm = require("cc.audio.dfpwm")
|
||||||
local ccpi = require("ccpi")
|
local ccpi = require("ccpi")
|
||||||
|
|
||||||
local EV_NONCE = math.floor(0xFFFFFFFF * math.random())
|
|
||||||
|
|
||||||
settings.define("video.speaker.left", {
|
settings.define("video.speaker.left", {
|
||||||
description = "Speaker ID for left audio channel",
|
description = "Speaker ID for left audio channel",
|
||||||
default = peripheral.getName(peripheral.find("speaker")),
|
default = peripheral.getName(peripheral.find("speaker")),
|
||||||
|
@ -88,8 +86,7 @@ if not n_frames and not video_url and not audio_url_l then
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local mon_w, mon_h = monitor.getSize()
|
print(string.format("Using monitor %s", peripheral.getName(monitor)))
|
||||||
print(string.format("Using monitor %s (%dx%d)", peripheral.getName(monitor), mon_w, mon_h))
|
|
||||||
if speakers.r then
|
if speakers.r then
|
||||||
print(string.format("Stereo sound: L=%s R=%s", peripheral.getName(speakers.l), peripheral.getName(speakers.r)))
|
print(string.format("Stereo sound: L=%s R=%s", peripheral.getName(speakers.l), peripheral.getName(speakers.r)))
|
||||||
else
|
else
|
||||||
|
@ -186,11 +183,11 @@ for i = 1, loading_concurrency do
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(subthreads, function()
|
table.insert(subthreads, function()
|
||||||
repeat
|
while #frames ~= n_frames or #audio_frames.l < n_audio_samples do
|
||||||
draw_bar(ty - 3, colors.blue, colors.gray, #frames / n_frames, "Loading video [%5d / %5d]", #frames, n_frames)
|
draw_bar(ty - 3, colors.blue, colors.gray, #frames / n_frames, "Loading video [%5d / %5d]", #frames, n_frames)
|
||||||
draw_bar(ty - 2, colors.red, colors.gray, #audio_frames.l / n_audio_samples, "Loading audio [%5d / %5d]", #audio_frames.l, n_audio_samples)
|
draw_bar(ty - 2, colors.red, colors.gray, #audio_frames.l / n_audio_samples, "Loading audio [%5d / %5d]", #audio_frames.l, n_audio_samples)
|
||||||
os.sleep(0.25)
|
os.sleep(0.25)
|
||||||
until #frames >= n_frames and #audio_frames.l >= n_audio_samples
|
end
|
||||||
print()
|
print()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -211,16 +208,14 @@ table.insert(subthreads, function()
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
os.queueEvent("playback_ready", EV_NONCE)
|
os.queueEvent("playback_ready")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
table.insert(subthreads, function()
|
table.insert(subthreads, function()
|
||||||
local is_dfpwm = ({ audio_url_l:find("%.dfpwm") })[2] == #audio_url_l
|
local is_dfpwm = ({ audio_url_l:find("%.dfpwm") })[2] == #audio_url_l
|
||||||
local decode = use_dfpwm and dfpwm.make_decoder() or decode_s8
|
local decode = use_dfpwm and dfpwm.make_decoder() or decode_s8
|
||||||
|
|
||||||
repeat
|
os.pullEvent("playback_ready")
|
||||||
local _, nonce = os.pullEvent("playback_ready")
|
|
||||||
until nonce == EV_NONCE
|
|
||||||
|
|
||||||
for i = 1, n_audio_samples do
|
for i = 1, n_audio_samples do
|
||||||
local buffer = decode(audio_frames.l[i])
|
local buffer = decode(audio_frames.l[i])
|
||||||
|
@ -237,9 +232,7 @@ table.insert(subthreads, function()
|
||||||
local is_dfpwm = ({ audio_url_r:find("%.dfpwm") })[2] == #audio_url_r
|
local is_dfpwm = ({ audio_url_r:find("%.dfpwm") })[2] == #audio_url_r
|
||||||
local decode = use_dfpwm and dfpwm.make_decoder() or decode_s8
|
local decode = use_dfpwm and dfpwm.make_decoder() or decode_s8
|
||||||
|
|
||||||
repeat
|
os.pullEvent("playback_ready")
|
||||||
local _, nonce = os.pullEvent("playback_ready")
|
|
||||||
until nonce == EV_NONCE
|
|
||||||
|
|
||||||
for i = 1, n_audio_samples do
|
for i = 1, n_audio_samples do
|
||||||
local buffer = decode(audio_frames.r[i])
|
local buffer = decode(audio_frames.r[i])
|
||||||
|
@ -251,9 +244,8 @@ table.insert(subthreads, function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
table.insert(subthreads, function()
|
table.insert(subthreads, function()
|
||||||
repeat
|
os.pullEvent("playback_ready")
|
||||||
local _, nonce = os.pullEvent("playback_ready")
|
|
||||||
until nonce == EV_NONCE
|
|
||||||
local start_t = os.clock()
|
local start_t = os.clock()
|
||||||
while not playback_done do
|
while not playback_done do
|
||||||
local frame = math.floor((os.clock() - start_t) / math.max(0.05, delay))
|
local frame = math.floor((os.clock() - start_t) / math.max(0.05, delay))
|
||||||
|
@ -261,11 +253,8 @@ table.insert(subthreads, function()
|
||||||
term.setBackgroundColor(frame >= #frames and colors.red or colors.gray)
|
term.setBackgroundColor(frame >= #frames and colors.red or colors.gray)
|
||||||
term.clearLine()
|
term.clearLine()
|
||||||
term.write(string.format("Playing frame: %d/%d", frame + 1, #frames))
|
term.write(string.format("Playing frame: %d/%d", frame + 1, #frames))
|
||||||
local img = frames[frame + 1]
|
if frames[frame + 1] then
|
||||||
if img ~= nil then
|
ccpi.draw(frames[frame + 1], 1, 1, monitor)
|
||||||
local x = math.max(math.floor((mon_w - img.w) / 2), 1)
|
|
||||||
local y = math.max(math.floor((mon_h - img.h) / 2), 1)
|
|
||||||
ccpi.draw(img, x, y, monitor)
|
|
||||||
end
|
end
|
||||||
os.sleep(delay)
|
os.sleep(delay)
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue