Convert gifv to avif
This commit is contained in:
+60
-9
@@ -1,21 +1,72 @@
|
|||||||
|
ARG TARGETARCH
|
||||||
FROM public.ecr.aws/lambda/python:3.12 AS builder
|
FROM public.ecr.aws/lambda/python:3.12 AS builder
|
||||||
RUN dnf -y install git cargo && dnf clean all
|
RUN dnf -y install git cargo && dnf clean all
|
||||||
RUN git clone https://github.com/ImageOptim/gifski /gifski
|
RUN git clone https://github.com/ImageOptim/gifski /gifski
|
||||||
WORKDIR /gifski
|
WORKDIR /gifski
|
||||||
RUN cargo build --release
|
RUN cargo build --release
|
||||||
|
|
||||||
FROM public.ecr.aws/lambda/python:3.12 AS ffmpeg
|
#FROM public.ecr.aws/lambda/python:3.12 AS ffmpeg-linux-amd64
|
||||||
RUN dnf -y update
|
#RUN dnf -y update
|
||||||
RUN dnf -y install git wget tar.x86_64 xz && dnf clean all
|
#RUN dnf -y install git wget tar.x86_64 xz && dnf clean all
|
||||||
WORKDIR /ffmpeg
|
#WORKDIR /ffmpeg
|
||||||
RUN wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
|
#RUN wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
|
||||||
RUN tar -xvf ffmpeg-release-amd64-static.tar.xz
|
#RUN tar -xvf ffmpeg-release-amd64-static.tar.xz
|
||||||
|
|
||||||
|
#FROM public.ecr.aws/lambda/python:3.12 AS ffmpeg-linux-arm64
|
||||||
|
#RUN dnf -y update
|
||||||
|
#RUN dnf -y install git wget tar xz && dnf clean all
|
||||||
|
#WORKDIR /ffmpeg
|
||||||
|
#RUN wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-arm64-static.tar.xz
|
||||||
|
#RUN tar -xvf ffmpeg-release-arm64-static.tar.xz
|
||||||
|
|
||||||
|
#FROM ffmpeg-linux-${TARGETARCH} AS ffmpeg
|
||||||
|
|
||||||
|
FROM public.ecr.aws/lambda/python:3.12 AS ffmpeg
|
||||||
|
# tl;dr: build ffmpeg from source with svt-av1 support and statically linked binaries
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN dnf remove -y microdnf-dnf && microdnf install -y dnf
|
||||||
|
RUN dnf group install "Development Tools" -y
|
||||||
|
|
||||||
|
RUN mkdir /ffmpeg_sources
|
||||||
|
WORKDIR /ffmpeg_sources
|
||||||
|
RUN git clone https://git.ffmpeg.org/ffmpeg.git .
|
||||||
|
RUN git clone --branch stable --depth 1 https://code.videolan.org/videolan/x264.git
|
||||||
|
RUN git clone https://gitlab.com/AOMediaCodec/SVT-AV1.git
|
||||||
|
|
||||||
|
RUN dnf install curl nasm glibc-static libstdc++-static cmake --allowerasing -y
|
||||||
|
|
||||||
|
# Install x264
|
||||||
|
WORKDIR /ffmpeg_sources/x264
|
||||||
|
RUN PKG_CONFIG_PATH="/ffmpeg_build/lib/pkgconfig" ./configure --prefix="/ffmpeg_build" --bindir="/bin" --enable-static
|
||||||
|
RUN make -j
|
||||||
|
RUN make install
|
||||||
|
|
||||||
|
# Install SVT-AV1
|
||||||
|
WORKDIR /ffmpeg_sources
|
||||||
|
WORKDIR /ffmpeg_sources/SVT-AV1/Build
|
||||||
|
|
||||||
|
RUN PKG_CONFIG_PATH="/ffmpeg_build/lib/pkgconfig" cmake .. -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DENABLE_STATIC=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_C_FLAGS_INIT="-static"
|
||||||
|
|
||||||
|
RUN make -j
|
||||||
|
RUN make install
|
||||||
|
|
||||||
|
# Install FFmpeg
|
||||||
|
WORKDIR /ffmpeg_sources
|
||||||
|
RUN PKG_CONFIG_PATH="/ffmpeg_build/lib/pkgconfig" ./configure --prefix="/ffmpeg_build" \
|
||||||
|
--pkg-config-flags="--static" --extra-ldexeflags="-static" --extra-libs="-lpthread -lm" --bindir="/bin" --disable-shared --enable-static --enable-gpl --enable-pthreads \
|
||||||
|
--enable-libx264 --enable-libsvtav1 --disable-ffplay
|
||||||
|
|
||||||
|
RUN make -j
|
||||||
|
|
||||||
FROM public.ecr.aws/lambda/python:3.12
|
FROM public.ecr.aws/lambda/python:3.12
|
||||||
COPY --from=builder /gifski/target/release/gifski ./
|
|
||||||
COPY --from=ffmpeg /ffmpeg/ffmpeg-*-amd64-static/ffmpeg /usr/local/bin/ffmpeg
|
#COPY --from=builder /gifski/target/release/gifski /usr/local/bin/gifski
|
||||||
COPY --from=ffmpeg /ffmpeg/ffmpeg-*-amd64-static/ffprobe /usr/local/bin/ffprobe
|
#COPY --from=ffmpeg /ffmpeg/ffmpeg-*-static/ffmpeg /usr/local/bin/ffmpeg
|
||||||
|
#COPY --from=ffmpeg /ffmpeg/ffmpeg-*-static/ffprobe /usr/local/bin/ffprobe
|
||||||
|
COPY --from=ffmpeg /ffmpeg_sources/ffmpeg /usr/local/bin/ffmpeg
|
||||||
|
COPY --from=ffmpeg /ffmpeg_sources/ffprobe /usr/local/bin/ffprobe
|
||||||
|
|
||||||
RUN pip install requests==2.32.3
|
RUN pip install requests==2.32.3
|
||||||
|
|
||||||
# Copy function code
|
# Copy function code
|
||||||
|
|||||||
+39
-50
@@ -21,55 +21,44 @@ else:
|
|||||||
def extractStatus(url):
|
def extractStatus(url):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def get_video_frame_rate(filename):
|
def convert_video_to_gif(filename):
|
||||||
result = subprocess.run(
|
try:
|
||||||
[
|
new_filename = tempfile.mkstemp(suffix=".gif")[1]
|
||||||
"ffprobe",
|
print("converting gif w gifski")
|
||||||
"-v",
|
p_ffmpeg = subprocess.Popen(["ffmpeg", "-i", filename, "-f", "yuv4mpegpipe", "-"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
|
||||||
"error",
|
p_gifski = subprocess.Popen(["gifski","--quality","70","--lossy-quality","30","-o", new_filename, "-"], stdin=p_ffmpeg.stdout, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
|
||||||
"-select_streams",
|
|
||||||
"v",
|
|
||||||
"-of",
|
|
||||||
"default=noprint_wrappers=1:nokey=1",
|
|
||||||
"-show_entries",
|
|
||||||
"stream=r_frame_rate",
|
|
||||||
filename,
|
|
||||||
],
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.STDOUT,
|
|
||||||
)
|
|
||||||
result_string = result.stdout.decode('utf-8').split()[0].split('/')
|
|
||||||
fps = float(result_string[0])/float(result_string[1])
|
|
||||||
return fps
|
|
||||||
|
|
||||||
def get_video_length_seconds(filename):
|
p_ffmpeg.stdout.close()
|
||||||
result = subprocess.run(
|
ret_gifski = p_gifski.wait()
|
||||||
[
|
ret_ffmpeg = p_ffmpeg.wait()
|
||||||
"ffprobe",
|
if ret_gifski != 0:
|
||||||
"-v",
|
print("err: gifski exited with code:", ret_gifski)
|
||||||
"error",
|
if ret_ffmpeg != 0:
|
||||||
"-show_entries",
|
print("err: ffmpeg exited with code:", ret_ffmpeg)
|
||||||
"format=duration",
|
if os.path.isfile(new_filename) and os.path.getsize(new_filename) > 0:
|
||||||
"-of",
|
return new_filename
|
||||||
"default=noprint_wrappers=1:nokey=1",
|
else:
|
||||||
filename,
|
print("gifski failed to convert gif")
|
||||||
],
|
return filename
|
||||||
stdout=subprocess.PIPE,
|
except Exception as e:
|
||||||
stderr=subprocess.STDOUT,
|
print("error converting gif (convert_video_to_gif):")
|
||||||
)
|
print(e)
|
||||||
result_string = result.stdout.decode('utf-8').split()[0]
|
return filename
|
||||||
return float(result_string)
|
|
||||||
|
|
||||||
def loop_video_until_length(filename, length):
|
def convert_video_to_avif(filename):
|
||||||
# use stream_loop to loop video until it's at least length seconds long
|
try:
|
||||||
video_length = get_video_length_seconds(filename)
|
fd,new_filename = tempfile.mkstemp(suffix=".avif")
|
||||||
if video_length < length:
|
os.close(fd)
|
||||||
loops = int(length/video_length)
|
print("converting gif w ffmpeg to avif")
|
||||||
new_filename = tempfile.mkstemp(suffix=".mp4")[1]
|
subprocess.call(["ffmpeg","-nostdin","-y", "-i", filename,"-pix_fmt","yuv420p","-an","-c:v","libsvtav1","-crf","30","-b:v","0","-threads","4", new_filename], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
|
||||||
subprocess.call(["ffmpeg","-stream_loop",str(loops),"-i",filename,"-c","copy","-y",new_filename],stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
|
if os.path.isfile(new_filename) and os.path.getsize(new_filename) > 0:
|
||||||
|
return new_filename
|
||||||
return new_filename
|
else:
|
||||||
else:
|
print("ffmpeg failed to convert avif")
|
||||||
|
return filename
|
||||||
|
except Exception as e:
|
||||||
|
print("error converting avif (convert_video_to_avif):")
|
||||||
|
print(e)
|
||||||
return filename
|
return filename
|
||||||
|
|
||||||
def redir(url):
|
def redir(url):
|
||||||
@@ -99,7 +88,7 @@ def lambda_handler(event, context):
|
|||||||
|
|
||||||
if useBucket:
|
if useBucket:
|
||||||
id=re.search(r"https:\/\/video\.twimg\.com\/tweet_video\/(.*?)\..*",url).group(1)
|
id=re.search(r"https:\/\/video\.twimg\.com\/tweet_video\/(.*?)\..*",url).group(1)
|
||||||
bfilename = str(id)+".mp4"
|
bfilename = str(id)+".avif"
|
||||||
furl=f"https://gifs.vxtwitter.com/{bfilename}" #f"https://{bucketname}.s3.amazonaws.com/{bfilename}"
|
furl=f"https://gifs.vxtwitter.com/{bfilename}" #f"https://{bucketname}.s3.amazonaws.com/{bfilename}"
|
||||||
print("get req for: "+url)
|
print("get req for: "+url)
|
||||||
try:
|
try:
|
||||||
@@ -121,7 +110,7 @@ def lambda_handler(event, context):
|
|||||||
print("error downloading video")
|
print("error downloading video")
|
||||||
return redir(url)
|
return redir(url)
|
||||||
|
|
||||||
videoLocationLooped = loop_video_until_length(videoLocation, 30)
|
videoLocationLooped = convert_video_to_avif(videoLocation)
|
||||||
if videoLocationLooped != videoLocation:
|
if videoLocationLooped != videoLocation:
|
||||||
os.remove(videoLocation)
|
os.remove(videoLocation)
|
||||||
videoLocation = videoLocationLooped
|
videoLocation = videoLocationLooped
|
||||||
@@ -137,7 +126,7 @@ def lambda_handler(event, context):
|
|||||||
'statusCode': 200,
|
'statusCode': 200,
|
||||||
"headers":
|
"headers":
|
||||||
{
|
{
|
||||||
"Content-Type": "video/mp4"
|
"Content-Type": "image/avif",
|
||||||
},
|
},
|
||||||
'body': encoded_string,
|
'body': encoded_string,
|
||||||
'isBase64Encoded': True
|
'isBase64Encoded': True
|
||||||
|
|||||||
+6
-1
@@ -439,8 +439,13 @@ def twitfix(sub_path):
|
|||||||
suffix = media["suffix"]
|
suffix = media["suffix"]
|
||||||
if media['type'] == "image":
|
if media['type'] == "image":
|
||||||
return Response(renderImageTweetEmbed(tweetData,media['url'] , appnameSuffix=suffix,embedIndex=embedIndex),headers={"Cache-Tag": "embed"})
|
return Response(renderImageTweetEmbed(tweetData,media['url'] , appnameSuffix=suffix,embedIndex=embedIndex),headers={"Cache-Tag": "embed"})
|
||||||
elif media['type'] == "video" or media['type'] == "gif":
|
elif media['type'] == "video":
|
||||||
return Response(renderVideoTweetEmbed(tweetData,media,appnameSuffix=suffix,embedIndex=embedIndex),headers={"Cache-Tag": "embed"})
|
return Response(renderVideoTweetEmbed(tweetData,media,appnameSuffix=suffix,embedIndex=embedIndex),headers={"Cache-Tag": "embed"})
|
||||||
|
elif media['type'] == "gif":
|
||||||
|
if "originalUrl" in media and media["url"] != media["originalUrl"]:
|
||||||
|
return Response(renderImageTweetEmbed(tweetData,media['url'] , appnameSuffix=suffix,embedIndex=embedIndex),headers={"Cache-Tag": "embed"})
|
||||||
|
else:
|
||||||
|
return Response(renderVideoTweetEmbed(tweetData,media,appnameSuffix=suffix,embedIndex=embedIndex),headers={"Cache-Tag": "embed"})
|
||||||
|
|
||||||
return message(msgs.failedToScan)
|
return message(msgs.failedToScan)
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ def determineMediaToEmbed(tweetData,embedIndex = -1,convertGif = True):
|
|||||||
#if gcApi == "local": # TODO
|
#if gcApi == "local": # TODO
|
||||||
#gcApi = f"{config['config']['url']}/gifconvert"
|
#gcApi = f"{config['config']['url']}/gifconvert"
|
||||||
vurl=media['originalUrl'] if 'originalUrl' in media else media['url']
|
vurl=media['originalUrl'] if 'originalUrl' in media else media['url']
|
||||||
media['url'] = gcApi + "/convert?url=" + vurl
|
media['url'] = gcApi + "/convert.avif?url=" + vurl
|
||||||
suffix += " • GIF"
|
suffix += " • GIF"
|
||||||
media["suffix"] = suffix
|
media["suffix"] = suffix
|
||||||
return media
|
return media
|
||||||
Reference in New Issue
Block a user