libfdk_aac with HE-AAC profiles
This commit is contained in:
parent
6adf7401a5
commit
46c84fe381
33
.docker/APKBUILD
Normal file
33
.docker/APKBUILD
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Based on https://github.com/alpinelinux/aports/blob/a7514b6cb4a2a50704a9685a94519a9d6307f276/community/fdk-aac/APKBUILD
|
||||||
|
# This APKBUILD uses non-stripped version of fdk-aac.
|
||||||
|
pkgname=fdk-aac-nonfree
|
||||||
|
pkgver=2.0.2
|
||||||
|
pkgrel=4
|
||||||
|
pkgdesc="Fraunhofer FDK AAC codec library with non-free components"
|
||||||
|
url="https://github.com/mstorsjo/fdk-aac"
|
||||||
|
arch="all"
|
||||||
|
license="FDK-AAC"
|
||||||
|
makedepends="cmake samurai"
|
||||||
|
source="https://github.com/mstorsjo/fdk-aac/archive/refs/tags/v$pkgver.zip"
|
||||||
|
options="!check" # no upstream/available testsuite
|
||||||
|
builddir="$srcdir/fdk-aac-$pkgver"
|
||||||
|
|
||||||
|
build() {
|
||||||
|
cmake -B build -G Ninja \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
|
-DCMAKE_INSTALL_LIBDIR=lib \
|
||||||
|
-DBUILD_SHARED_LIBS=True \
|
||||||
|
-DCMAKE_BUILD_TYPE=MinSizeRel
|
||||||
|
|
||||||
|
cmake --build build
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
DESTDIR="$pkgdir" cmake --install build
|
||||||
|
install -Dm644 NOTICE \
|
||||||
|
"$pkgdir"/usr/share/licenses/libfdk-aac/NOTICE
|
||||||
|
}
|
||||||
|
|
||||||
|
sha512sums="
|
||||||
|
003f23a6b9e14757905b9cd331a442ed001ed7b835aae2c75f93db2d3a51ec15b5c4366a21bb211195cca2254e8a9805dab33a16fa670f39bd265d00b7ba5493 v2.0.2.zip
|
||||||
|
"
|
@ -16,4 +16,7 @@ tower-http = { version = "0.5.2", features = ["trace"] }
|
|||||||
num_cpus = "1.16.0"
|
num_cpus = "1.16.0"
|
||||||
ffmpeg-next = "7.0.1"
|
ffmpeg-next = "7.0.1"
|
||||||
ureq = { version = "*", features = ["json", "charset"] }
|
ureq = { version = "*", features = ["json", "charset"] }
|
||||||
mime_guess = "2.0.4"
|
http = "1.1.0"
|
||||||
|
tokio-util = { version = "0.7.11", features = ["io"] }
|
||||||
|
futures-util = "0.3.30"
|
||||||
|
infer = "0.15.0"
|
||||||
|
30
Dockerfile
30
Dockerfile
@ -4,7 +4,6 @@ FROM alpine:3.20 AS builder
|
|||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
rust \
|
rust \
|
||||||
cargo \
|
cargo \
|
||||||
fdk-aac \
|
|
||||||
fdk-aac-dev \
|
fdk-aac-dev \
|
||||||
clang16 \
|
clang16 \
|
||||||
clang16-static \
|
clang16-static \
|
||||||
@ -16,16 +15,9 @@ RUN apk add --no-cache \
|
|||||||
build-base \
|
build-base \
|
||||||
nasm \
|
nasm \
|
||||||
yasm \
|
yasm \
|
||||||
aom-dev \
|
|
||||||
dav1d-dev \
|
|
||||||
lame-dev \
|
lame-dev \
|
||||||
opus-dev \
|
opus-dev \
|
||||||
svt-av1-dev \
|
|
||||||
libvorbis-dev \
|
libvorbis-dev \
|
||||||
libvpx-dev \
|
|
||||||
x264-dev \
|
|
||||||
x265-dev \
|
|
||||||
numactl-dev \
|
|
||||||
libass-dev \
|
libass-dev \
|
||||||
libunistring-dev \
|
libunistring-dev \
|
||||||
gnutls-dev && \
|
gnutls-dev && \
|
||||||
@ -44,18 +36,11 @@ RUN apk add --no-cache \
|
|||||||
--bindir="/ffmpeg/bin" \
|
--bindir="/ffmpeg/bin" \
|
||||||
--enable-gpl \
|
--enable-gpl \
|
||||||
--enable-gnutls \
|
--enable-gnutls \
|
||||||
--enable-libaom \
|
|
||||||
--enable-libass \
|
--enable-libass \
|
||||||
--enable-libfdk-aac \
|
--enable-libfdk-aac \
|
||||||
--enable-libfreetype \
|
|
||||||
--enable-libmp3lame \
|
--enable-libmp3lame \
|
||||||
--enable-libopus \
|
--enable-libopus \
|
||||||
--enable-libsvtav1 \
|
|
||||||
--enable-libdav1d \
|
|
||||||
--enable-libvorbis \
|
--enable-libvorbis \
|
||||||
--enable-libvpx \
|
|
||||||
--enable-libx264 \
|
|
||||||
--enable-libx265 \
|
|
||||||
--enable-nonfree && \
|
--enable-nonfree && \
|
||||||
PATH="/ffmpeg/bin:$PATH" make -j$(nproc) && \
|
PATH="/ffmpeg/bin:$PATH" make -j$(nproc) && \
|
||||||
make install && \
|
make install && \
|
||||||
@ -68,6 +53,18 @@ RUN cd /build && \
|
|||||||
export PKG_CONFIG_PATH="/ffmpeg/ffmpeg_build/lib/pkgconfig" && \
|
export PKG_CONFIG_PATH="/ffmpeg/ffmpeg_build/lib/pkgconfig" && \
|
||||||
cargo build --verbose --release
|
cargo build --verbose --release
|
||||||
|
|
||||||
|
FROM alpine:3.20 AS fdk-builder
|
||||||
|
COPY ./.docker/APKBUILD /fdk-aac/APKBUILD
|
||||||
|
RUN apk add --no-cache sudo abuild build-base cmake samurai && \
|
||||||
|
cd /fdk-aac && \
|
||||||
|
adduser -G abuild -g "Alpine Package Builder" -s /bin/ash -D builder && \
|
||||||
|
echo "builder ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \
|
||||||
|
echo "root ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \
|
||||||
|
chown -R builder:abuild /fdk-aac && \
|
||||||
|
chmod 777 /tmp && \
|
||||||
|
sudo -u builder sh -c 'abuild-keygen -ani && abuild -r' && \
|
||||||
|
find /home/builder -name 'fdk-aac*' -exec mv {} /fdk-aac.apk \;
|
||||||
|
|
||||||
FROM alpine:3.20
|
FROM alpine:3.20
|
||||||
WORKDIR /
|
WORKDIR /
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
@ -75,11 +72,12 @@ RUN apk add --no-cache \
|
|||||||
ffmpeg-libavformat \
|
ffmpeg-libavformat \
|
||||||
ffmpeg-libavfilter \
|
ffmpeg-libavfilter \
|
||||||
ffmpeg-libavdevice \
|
ffmpeg-libavdevice \
|
||||||
fdk-aac \
|
|
||||||
dumb-init \
|
dumb-init \
|
||||||
mailcap \
|
mailcap \
|
||||||
tzdata \
|
tzdata \
|
||||||
gnutls
|
gnutls
|
||||||
|
COPY --from=fdk-builder /fdk-aac.apk /tmp/fdk-aac.apk
|
||||||
|
RUN apk add --allow-untrusted /tmp/fdk-aac.apk && rm /tmp/fdk-aac.apk
|
||||||
COPY --from=builder /build/target/release/atranscoder-rpc /usr/local/bin
|
COPY --from=builder /build/target/release/atranscoder-rpc /usr/local/bin
|
||||||
EXPOSE 8090
|
EXPOSE 8090
|
||||||
ENTRYPOINT ["/usr/bin/dumb-init", "--", "/usr/local/bin/atranscoder-rpc"]
|
ENTRYPOINT ["/usr/bin/dumb-init", "--", "/usr/local/bin/atranscoder-rpc"]
|
||||||
|
34
README.md
34
README.md
@ -12,30 +12,36 @@ Transcoding can be done like this:
|
|||||||
2. Upload file for transcoding:
|
2. Upload file for transcoding:
|
||||||
```bash
|
```bash
|
||||||
curl --location 'http://localhost:8090/enqueue' \
|
curl --location 'http://localhost:8090/enqueue' \
|
||||||
--form 'file=@"/home/user/Music/test.mp3"' \
|
--form 'file=@"/home/user/Music/test.mp3"' \
|
||||||
--form 'format="adts"' \
|
--form 'format="mp4"' \
|
||||||
--form 'codec="aac"' \
|
--form 'codec="libfdk_aac"' \
|
||||||
--form 'bitRate="64000"' \
|
--form 'codecOpts="profile=aac_he"' \
|
||||||
--form 'maxBitRate="64000"' \
|
--form 'bitRate="160000"' \
|
||||||
--form 'sampleRate="8000"' \
|
--form 'maxBitRate="160000"' \
|
||||||
--form 'channelLayout="mono"' \
|
--form 'sampleRate="44100"' \
|
||||||
--form 'uploadUrl="http://127.0.0.1:8909/upload"'
|
--form 'channelLayout="stereo"' \
|
||||||
|
--form 'callbackUrl="http://127.0.0.1:8909/callback"'
|
||||||
|
```
|
||||||
|
3. Your `callbackUrl` will receive JSON response with job ID and error in case of failure. Error will be null if transcoding was successful.
|
||||||
|
4. You can download transcoded file like this (replace `job_id` with the ID you've received):
|
||||||
|
```bash
|
||||||
|
curl -L http://localhost:8090/get/job_id -o file.mp4
|
||||||
```
|
```
|
||||||
3. Your `uploadUrl` will receive JSON response with job ID and error in case of failure and the entire transcoded file contents in case of success (success request will have `X-Task-Id` header with task ID). Use `Content-Type` header to differentiate between the two data types.
|
|
||||||
|
|
||||||
You can change configuration using this environment variables:
|
You can change configuration using this environment variables:
|
||||||
- `LISTEN` - change this environment variable to change TCP listen address. Default is `0.0.0.0:8090`.
|
- `LISTEN` - change this environment variable to change TCP listen address. Default is `0.0.0.0:8090`.
|
||||||
- `NUM_WORKERS` - can be used to change how many threads will be used to transcode incoming files. Default is equal to logical CPUs.
|
- `NUM_WORKERS` - can be used to change how many threads will be used to transcode incoming files. Default is equal to logical CPUs.
|
||||||
- `TEMP_DIR` - this can be used to change which directory should be used to store incoming downloads and transcoding results. Useful if you want to use a Docker volume for this. Default is system temp directory (`/tmp` for Linux).
|
- `TEMP_DIR` - this can be used to change which directory should be used to store incoming downloads and transcoding results. Useful if you want to use a Docker volume for this. Default is system temp directory (`/tmp` for Linux).
|
||||||
- `LOG_LEVEL` - changes log verbosity, default is `info`.
|
- `LOG_LEVEL` - changes log verbosity, default is `info`.
|
||||||
|
- `MAX_BODY_SIZE` - changes max body size for `/enqueue`. Default is 100MB.
|
||||||
|
- `FFMPEG_VERBOSE` - if set to `1` changes FFmpeg log level from quiet to trace.
|
||||||
|
|
||||||
# Roadmap
|
# Roadmap
|
||||||
- [x] Implement somewhat acceptable error handling.
|
- [x] Implement somewhat acceptable error handling.
|
||||||
- [x] Remove old conversion results and input files that are older than 1 hour.
|
- [x] Remove old conversion results and input files that are older than 1 hour.
|
||||||
- [x] Remove input file after transcoding it.
|
- [x] Remove input file after transcoding it.
|
||||||
- [x] Implement file upload to `uploadUrl` (if `Content-Type: application/json` then conversion was not successful and body contains an error info).
|
- [x] Do not upload files directly, add download route with streaming instead.
|
||||||
- [x] Remove transcoding result after uploading it to the `uploadUrl`.
|
- [ ] If FFmpeg fails, `send_error` won't be called - fix that.
|
||||||
- [ ] Docker image for `amd64` and `arm64` (currently only `amd64` is supported).
|
- [ ] Default errors are returned in plain text. Change it to the JSON.
|
||||||
- [ ] ~~Restart threads in case of panic.~~ It's better to not panic. Current error handling seems ok for now.
|
- [ ] Docker image for `amd64` and `arm64` (currently only `amd64` is supported because `arm64` cross-compilation with QEMU is sloooooooooooowwwww...).
|
||||||
- [ ] ~~Statically linked binary for Docker image & result docker image based on `scratch` (reduce image size).~~ Not yet, see [Dockerfile.scratch](Dockerfile.scratch).
|
|
||||||
- [ ] Tests!
|
- [ ] Tests!
|
@ -18,7 +18,7 @@ pub struct ConvertRequest {
|
|||||||
pub max_bit_rate: usize,
|
pub max_bit_rate: usize,
|
||||||
pub sample_rate: i32,
|
pub sample_rate: i32,
|
||||||
pub channel_layout: String,
|
pub channel_layout: String,
|
||||||
pub upload_url: String,
|
pub callback_url: String,
|
||||||
|
|
||||||
#[form_data(limit = "25MiB")]
|
#[form_data(limit = "25MiB")]
|
||||||
pub file: FieldData<NamedTempFile>,
|
pub file: FieldData<NamedTempFile>,
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
|
use std::env;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::path::Path;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
use axum::extract::{DefaultBodyLimit, State};
|
use axum::extract::{DefaultBodyLimit, Path, State};
|
||||||
use axum::http::StatusCode;
|
use axum::http::StatusCode;
|
||||||
use axum::routing::post;
|
use axum::response::IntoResponse;
|
||||||
|
use axum::routing::{get, post};
|
||||||
use axum::{Json, Router};
|
use axum::{Json, Router};
|
||||||
use axum_typed_multipart::TypedMultipart;
|
use axum_typed_multipart::TypedMultipart;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
@ -19,11 +19,21 @@ use crate::dto::{ConvertRequest, ConvertResponse};
|
|||||||
use crate::task::{Task, TaskParams};
|
use crate::task::{Task, TaskParams};
|
||||||
use crate::thread_pool::ThreadPool;
|
use crate::thread_pool::ThreadPool;
|
||||||
|
|
||||||
const CONTENT_LENGTH_LIMIT: usize = 30 * 1024 * 1024;
|
use axum::body::Body;
|
||||||
|
use axum::body::Bytes;
|
||||||
|
use futures_util::StreamExt;
|
||||||
|
use std::path::Path as StdPath;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::fs::File;
|
||||||
|
use tokio::io::AsyncReadExt;
|
||||||
|
use tokio_util::io::ReaderStream;
|
||||||
|
|
||||||
|
const CONTENT_LENGTH_LIMIT: usize = 100 * 1024 * 1024;
|
||||||
const WORK_DIR_IN_OUT_LIFETIME: u64 = 60 * 60;
|
const WORK_DIR_IN_OUT_LIFETIME: u64 = 60 * 60;
|
||||||
|
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
thread_pool: Arc<ThreadPool>,
|
thread_pool: Arc<ThreadPool>,
|
||||||
|
max_body_size: usize,
|
||||||
work_dir: String,
|
work_dir: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,6 +41,9 @@ impl Server {
|
|||||||
pub(crate) fn new(thread_pool: ThreadPool, work_dir: String) -> Server {
|
pub(crate) fn new(thread_pool: ThreadPool, work_dir: String) -> Server {
|
||||||
Server {
|
Server {
|
||||||
thread_pool: Arc::new(thread_pool),
|
thread_pool: Arc::new(thread_pool),
|
||||||
|
max_body_size: env::var("MAX_BODY_SIZE").map_or(CONTENT_LENGTH_LIMIT, |val| {
|
||||||
|
val.parse().map_or(CONTENT_LENGTH_LIMIT, |val| val)
|
||||||
|
}),
|
||||||
work_dir,
|
work_dir,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,8 +68,9 @@ impl Server {
|
|||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route(
|
.route(
|
||||||
"/enqueue",
|
"/enqueue",
|
||||||
post(enqueue_file).layer(DefaultBodyLimit::max(CONTENT_LENGTH_LIMIT)),
|
post(enqueue_file).layer(DefaultBodyLimit::max(this.max_body_size)),
|
||||||
)
|
)
|
||||||
|
.route("/get/:identifier", get(download_file))
|
||||||
.with_state(this)
|
.with_state(this)
|
||||||
.layer(TraceLayer::new_for_http());
|
.layer(TraceLayer::new_for_http());
|
||||||
|
|
||||||
@ -71,8 +85,8 @@ async fn enqueue_file(
|
|||||||
TypedMultipart(req): TypedMultipart<ConvertRequest>,
|
TypedMultipart(req): TypedMultipart<ConvertRequest>,
|
||||||
) -> (StatusCode, Json<ConvertResponse>) {
|
) -> (StatusCode, Json<ConvertResponse>) {
|
||||||
let task_id = Uuid::new_v4();
|
let task_id = Uuid::new_v4();
|
||||||
let input = Path::new(&server.work_dir).join(format!("{}.in.atranscoder", task_id));
|
let input = StdPath::new(&server.work_dir).join(format!("{}.in.atranscoder", task_id));
|
||||||
let output = Path::new(&server.work_dir).join(format!("{}.out.atranscoder", task_id));
|
let output = StdPath::new(&server.work_dir).join(format!("{}.out.atranscoder", task_id));
|
||||||
|
|
||||||
let file = req.file;
|
let file = req.file;
|
||||||
|
|
||||||
@ -95,7 +109,7 @@ async fn enqueue_file(
|
|||||||
max_bit_rate: req.max_bit_rate,
|
max_bit_rate: req.max_bit_rate,
|
||||||
sample_rate: req.sample_rate,
|
sample_rate: req.sample_rate,
|
||||||
channel_layout: req.channel_layout,
|
channel_layout: req.channel_layout,
|
||||||
upload_url: req.upload_url,
|
callback_url: req.callback_url,
|
||||||
input_path: input_path.to_string(),
|
input_path: input_path.to_string(),
|
||||||
output_path: output_path.to_string(),
|
output_path: output_path.to_string(),
|
||||||
};
|
};
|
||||||
@ -116,6 +130,49 @@ async fn enqueue_file(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn download_file(
|
||||||
|
State(server): State<Arc<Server>>,
|
||||||
|
Path(identifier): Path<String>,
|
||||||
|
) -> Result<impl IntoResponse, StatusCode> {
|
||||||
|
let file_name = format!("{}.out.atranscoder", identifier);
|
||||||
|
let file_path = StdPath::new(&server.work_dir).join(file_name);
|
||||||
|
|
||||||
|
if !file_path.exists() {
|
||||||
|
return Err(StatusCode::NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file = match File::open(&file_path).await {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(_) => return Err(StatusCode::INTERNAL_SERVER_ERROR),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buffer = [0; 512];
|
||||||
|
let n = match file.read(&mut buffer).await {
|
||||||
|
Ok(n) if n > 0 => n,
|
||||||
|
_ => return Err(StatusCode::INTERNAL_SERVER_ERROR),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mime_type = infer::get(&buffer[..n]).map_or("application/octet-stream".to_string(), |t| {
|
||||||
|
t.mime_type().to_string()
|
||||||
|
});
|
||||||
|
|
||||||
|
let file = match File::open(&file_path).await {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(_) => return Err(StatusCode::INTERNAL_SERVER_ERROR),
|
||||||
|
};
|
||||||
|
|
||||||
|
let stream = ReaderStream::new(file);
|
||||||
|
let body = Body::from_stream(stream.map(|result| match result {
|
||||||
|
Ok(bytes) => Ok(Bytes::from(bytes)),
|
||||||
|
Err(err) => Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::Other,
|
||||||
|
err.to_string(),
|
||||||
|
)),
|
||||||
|
}));
|
||||||
|
|
||||||
|
Ok(([(http::header::CONTENT_TYPE, mime_type)], body))
|
||||||
|
}
|
||||||
|
|
||||||
fn error_response(msg: &str) -> (StatusCode, Json<ConvertResponse>) {
|
fn error_response(msg: &str) -> (StatusCode, Json<ConvertResponse>) {
|
||||||
(
|
(
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
57
src/task.rs
57
src/task.rs
@ -2,10 +2,7 @@ use crate::dto::ConvertResponse;
|
|||||||
use crate::transcoder::{Transcoder, TranscoderParams};
|
use crate::transcoder::{Transcoder, TranscoderParams};
|
||||||
use ffmpeg_next::channel_layout::ChannelLayout;
|
use ffmpeg_next::channel_layout::ChannelLayout;
|
||||||
use ffmpeg_next::{format, Dictionary};
|
use ffmpeg_next::{format, Dictionary};
|
||||||
use mime_guess::from_path;
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Read;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
use ureq::Response;
|
use ureq::Response;
|
||||||
@ -28,7 +25,7 @@ impl Task {
|
|||||||
send_error(
|
send_error(
|
||||||
self.id,
|
self.id,
|
||||||
format!("Couldn't transcode: {}", err).as_str(),
|
format!("Couldn't transcode: {}", err).as_str(),
|
||||||
&self.params.upload_url,
|
&self.params.callback_url,
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
return Err(err);
|
return Err(err);
|
||||||
@ -36,26 +33,21 @@ impl Task {
|
|||||||
|
|
||||||
std::fs::remove_file(Path::new(&self.params.input_path)).ok();
|
std::fs::remove_file(Path::new(&self.params.input_path)).ok();
|
||||||
|
|
||||||
if let Err(err) = upload_file(
|
if let Err(err) = send_ok(self.id, &self.params.callback_url) {
|
||||||
&self.id.to_string(),
|
|
||||||
&self.params.output_path,
|
|
||||||
&self.params.upload_url,
|
|
||||||
) {
|
|
||||||
error!(
|
error!(
|
||||||
"couldn't upload result for job id={}, file path {}: {}",
|
"couldn't send result callback for job id={}, url {}: {}",
|
||||||
&self.id.to_string(),
|
&self.id.to_string(),
|
||||||
&self.params.output_path,
|
&self.params.callback_url,
|
||||||
err
|
err
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
debug!(
|
debug!(
|
||||||
"job id={} result was uploaded to {}",
|
"job id={} result was sent to callback {}",
|
||||||
&self.id.to_string(),
|
&self.id.to_string(),
|
||||||
&self.params.upload_url
|
&self.params.callback_url
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::fs::remove_file(Path::new(&self.params.output_path)).ok();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +185,7 @@ pub struct TaskParams {
|
|||||||
pub channel_layout: String,
|
pub channel_layout: String,
|
||||||
pub input_path: String,
|
pub input_path: String,
|
||||||
pub output_path: String,
|
pub output_path: String,
|
||||||
pub upload_url: String,
|
pub callback_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_error(
|
fn send_error(
|
||||||
@ -211,41 +203,22 @@ fn send_error(
|
|||||||
if response.status() == 200 {
|
if response.status() == 200 {
|
||||||
Ok(response)
|
Ok(response)
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Failed to send an error. Status: {}", response.status()).into())
|
Err(format!("failed to send callback to {}. Status: {}", url, response.status()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upload_file<P: AsRef<Path>>(
|
fn send_ok(id: uuid::Uuid, url: &str) -> Result<Response, Box<dyn std::error::Error>> {
|
||||||
id: &str,
|
|
||||||
file_path: P,
|
|
||||||
url: &str,
|
|
||||||
) -> Result<Response, Box<dyn std::error::Error>> {
|
|
||||||
let path = file_path.as_ref();
|
|
||||||
let file_name = path
|
|
||||||
.file_name()
|
|
||||||
.ok_or("Invalid file path")?
|
|
||||||
.to_str()
|
|
||||||
.ok_or("Invalid file name")?;
|
|
||||||
|
|
||||||
let mut file = File::open(path)?;
|
|
||||||
let mut buffer = Vec::new();
|
|
||||||
file.read_to_end(&mut buffer)?;
|
|
||||||
|
|
||||||
let mime_type = from_path(path).first_or_octet_stream();
|
|
||||||
|
|
||||||
let response = ureq::post(url)
|
let response = ureq::post(url)
|
||||||
.set("Content-Type", mime_type.as_ref())
|
.set("Content-Type", "application/json")
|
||||||
.set(
|
.send_json(ConvertResponse {
|
||||||
"Content-Disposition",
|
id: Some(id.to_string()),
|
||||||
&format!("attachment; filename=\"{}\"", file_name),
|
error: None,
|
||||||
)
|
})?;
|
||||||
.set("X-Task-Id", id)
|
|
||||||
.send_bytes(&buffer)?;
|
|
||||||
|
|
||||||
if response.status() == 200 {
|
if response.status() == 200 {
|
||||||
Ok(response)
|
Ok(response)
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Failed to upload file. Status: {}", response.status()).into())
|
Err(format!("failed to send callback to {}. Status: {}", url, response.status()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
extern crate ffmpeg_next as ffmpeg;
|
extern crate ffmpeg_next as ffmpeg;
|
||||||
|
|
||||||
use std::any::Any;
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use crate::task::params_to_avdictionary;
|
|
||||||
use ffmpeg::{codec, filter, format, frame, media};
|
use ffmpeg::{codec, filter, format, frame, media};
|
||||||
use ffmpeg_next::codec::Parameters;
|
|
||||||
use ffmpeg_next::error::EAGAIN;
|
use ffmpeg_next::error::EAGAIN;
|
||||||
use ffmpeg_next::Dictionary;
|
|
||||||
use tracing::log::debug;
|
use crate::task::params_to_avdictionary;
|
||||||
|
|
||||||
pub struct Transcoder {
|
pub struct Transcoder {
|
||||||
pub(crate) stream: usize,
|
pub(crate) stream: usize,
|
||||||
|
Loading…
Reference in New Issue
Block a user