mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-31 12:14:46 -04:00
Add openapi spec for the transcoder
This commit is contained in:
parent
ea52ce1c65
commit
c96db3a3dc
58
transcoder/Cargo.lock
generated
58
transcoder/Cargo.lock
generated
@ -640,6 +640,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -678,6 +679,12 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
|
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.141"
|
version = "0.2.141"
|
||||||
@ -839,6 +846,30 @@ version = "0.2.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.56"
|
version = "1.0.56"
|
||||||
@ -1263,6 +1294,7 @@ dependencies = [
|
|||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"utoipa",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1324,6 +1356,32 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utoipa"
|
||||||
|
version = "3.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68ae74ef183fae36d650f063ae7bde1cacbe1cd7e72b617cbe1e985551878b98"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"utoipa-gen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utoipa-gen"
|
||||||
|
version = "3.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ea8ac818da7e746a63285594cce8a96f5e00ee31994e655bd827569cb8b137b"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"syn 2.0.13",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
|
@ -11,3 +11,4 @@ serde = { version = "1.0.159", features = ["derive"] }
|
|||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
derive_more = "0.99.17"
|
derive_more = "0.99.17"
|
||||||
reqwest = { version = "0.11.16", default_features = false, features = ["json", "rustls-tls"] }
|
reqwest = { version = "0.11.16", default_features = false, features = ["json", "rustls-tls"] }
|
||||||
|
utoipa = { version = "3", features = ["actix_extras"] }
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize, ToSchema)]
|
||||||
pub struct MediaInfo {
|
pub struct MediaInfo {
|
||||||
container: String,
|
container: String,
|
||||||
video_codec: String,
|
video_codec: String,
|
||||||
@ -10,7 +11,7 @@ pub struct MediaInfo {
|
|||||||
chapters: Vec<Chapter>,
|
chapters: Vec<Chapter>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize, ToSchema)]
|
||||||
pub struct Track {
|
pub struct Track {
|
||||||
/// The index of this track on the media.
|
/// The index of this track on the media.
|
||||||
index: u32,
|
index: u32,
|
||||||
@ -26,7 +27,7 @@ pub struct Track {
|
|||||||
forced: bool,
|
forced: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize, ToSchema)]
|
||||||
pub struct Chapter {
|
pub struct Chapter {
|
||||||
/// The start time of the chapter (in second from the start of the episode).
|
/// The start time of the chapter (in second from the start of the episode).
|
||||||
start: f32,
|
start: f32,
|
||||||
@ -37,6 +38,6 @@ pub struct Chapter {
|
|||||||
// TODO: add a type field for Opening, Credits...
|
// TODO: add a type field for Opening, Credits...
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn identify(path: String) -> Result<MediaInfo, std::io::Error> {
|
pub fn identify(_path: String) -> Result<MediaInfo, std::io::Error> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,10 @@ use actix_web::{
|
|||||||
App, HttpRequest, HttpServer, Result,
|
App, HttpRequest, HttpServer, Result,
|
||||||
};
|
};
|
||||||
use error::ApiError;
|
use error::ApiError;
|
||||||
|
use utoipa::OpenApi;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
identify::{identify, MediaInfo},
|
identify::{identify, MediaInfo, Track, Chapter},
|
||||||
transcode::{Quality, Transcoder},
|
transcode::{Quality, Transcoder},
|
||||||
};
|
};
|
||||||
mod error;
|
mod error;
|
||||||
@ -24,7 +25,21 @@ fn get_client_id(req: HttpRequest) -> Result<String, ApiError> {
|
|||||||
.map(|x| x.to_str().unwrap().to_string())
|
.map(|x| x.to_str().unwrap().to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/{resource}/{slug}/direct{extension}")]
|
/// Direct video
|
||||||
|
///
|
||||||
|
/// Retrieve the raw video stream, in the same container as the one on the server. No transcoding or
|
||||||
|
/// transmuxing is done.
|
||||||
|
#[utoipa::path(
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "The item is returned"),
|
||||||
|
(status = NOT_FOUND, description = "Invalid slug.")
|
||||||
|
),
|
||||||
|
params(
|
||||||
|
("resource" = String, Path, description = "Episode or movie"),
|
||||||
|
("slug" = String, Path, description = "The slug of the movie/episode."),
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
#[get("/{resource}/{slug}/direct")]
|
||||||
async fn get_direct(query: web::Path<(String, String)>) -> Result<NamedFile> {
|
async fn get_direct(query: web::Path<(String, String)>) -> Result<NamedFile> {
|
||||||
let (resource, slug) = query.into_inner();
|
let (resource, slug) = query.into_inner();
|
||||||
let path = paths::get_path(resource, slug).await.map_err(|e| {
|
let path = paths::get_path(resource, slug).await.map_err(|e| {
|
||||||
@ -35,6 +50,23 @@ async fn get_direct(query: web::Path<(String, String)>) -> Result<NamedFile> {
|
|||||||
Ok(NamedFile::open_async(path).await?)
|
Ok(NamedFile::open_async(path).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Transcode video
|
||||||
|
///
|
||||||
|
/// Transcode the video to the selected quality.
|
||||||
|
/// This route can take a few seconds to respond since it will way for at least one segment to be
|
||||||
|
/// available.
|
||||||
|
#[utoipa::path(
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Get the m3u8 playlist."),
|
||||||
|
(status = NOT_FOUND, description = "Invalid slug.")
|
||||||
|
),
|
||||||
|
params(
|
||||||
|
("resource" = String, Path, description = "Episode or movie"),
|
||||||
|
("slug" = String, Path, description = "The slug of the movie/episode."),
|
||||||
|
("quality" = Quality, Path, description = "Specify the quality you want"),
|
||||||
|
("x-client-id" = String, Header, description = "A unique identify for a player's instance. Used to cancel unused transcode"),
|
||||||
|
)
|
||||||
|
)]
|
||||||
#[get("/{resource}/{slug}/{quality}/index.m3u8")]
|
#[get("/{resource}/{slug}/{quality}/index.m3u8")]
|
||||||
async fn get_transcoded(
|
async fn get_transcoded(
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
@ -60,6 +92,22 @@ async fn get_transcoded(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get transmuxed chunk
|
||||||
|
///
|
||||||
|
/// Retrieve a chunk of a transmuxed video.
|
||||||
|
#[utoipa::path(
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Get a hls chunk."),
|
||||||
|
(status = NOT_FOUND, description = "Invalid slug.")
|
||||||
|
),
|
||||||
|
params(
|
||||||
|
("resource" = String, Path, description = "Episode or movie"),
|
||||||
|
("slug" = String, Path, description = "The slug of the movie/episode."),
|
||||||
|
("quality" = Quality, Path, description = "Specify the quality you want"),
|
||||||
|
("chunk" = u32, Path, description = "The number of the chunk"),
|
||||||
|
("x-client-id" = String, Header, description = "A unique identify for a player's instance. Used to cancel unused transcode"),
|
||||||
|
)
|
||||||
|
)]
|
||||||
#[get("/{resource}/{slug}/{quality}/segments-{chunk}.ts")]
|
#[get("/{resource}/{slug}/{quality}/segments-{chunk}.ts")]
|
||||||
async fn get_chunk(
|
async fn get_chunk(
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
@ -89,6 +137,19 @@ async fn get_chunk(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Identify
|
||||||
|
///
|
||||||
|
/// Identify metadata about a file
|
||||||
|
#[utoipa::path(
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Ok", body = MediaInfo),
|
||||||
|
(status = NOT_FOUND, description = "Invalid slug.")
|
||||||
|
),
|
||||||
|
params(
|
||||||
|
("resource" = String, Path, description = "Episode or movie"),
|
||||||
|
("slug" = String, Path, description = "The slug of the movie/episode."),
|
||||||
|
)
|
||||||
|
)]
|
||||||
#[get("/{resource}/{slug}/identify")]
|
#[get("/{resource}/{slug}/identify")]
|
||||||
async fn identify_resource(
|
async fn identify_resource(
|
||||||
query: web::Path<(String, String)>,
|
query: web::Path<(String, String)>,
|
||||||
@ -104,6 +165,19 @@ async fn identify_resource(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/openapi.json")]
|
||||||
|
async fn get_swagger() -> String {
|
||||||
|
#[derive(OpenApi)]
|
||||||
|
#[openapi(
|
||||||
|
info(description = "Transcoder's open api."),
|
||||||
|
paths(get_direct, get_transcoded, get_chunk, identify_resource),
|
||||||
|
components(schemas(MediaInfo, Track, Chapter))
|
||||||
|
)]
|
||||||
|
struct ApiDoc;
|
||||||
|
|
||||||
|
ApiDoc::openapi().to_pretty_json().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
let state = web::Data::new(Transcoder::new());
|
let state = web::Data::new(Transcoder::new());
|
||||||
@ -115,6 +189,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.service(get_transcoded)
|
.service(get_transcoded)
|
||||||
.service(get_chunk)
|
.service(get_chunk)
|
||||||
.service(identify_resource)
|
.service(identify_resource)
|
||||||
|
.service(get_swagger)
|
||||||
})
|
})
|
||||||
.bind(("0.0.0.0", 7666))?
|
.bind(("0.0.0.0", 7666))?
|
||||||
.run()
|
.run()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user