mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Add segments route
This commit is contained in:
parent
2939ea0787
commit
c6edf4e2cb
@ -18,8 +18,8 @@ async fn get_movie_direct(query: web::Path<String>) -> Result<NamedFile> {
|
|||||||
Ok(NamedFile::open_async(path).await?)
|
Ok(NamedFile::open_async(path).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/movie/{quality}/{slug}/master.m3u8")]
|
#[get("/movie/{quality}/{slug}/index.m3u8")]
|
||||||
async fn get_movie_auto(
|
async fn transcode_movie(
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
query: web::Path<(String, String)>,
|
query: web::Path<(String, String)>,
|
||||||
transcoder: web::Data<Transcoder>,
|
transcoder: web::Data<Transcoder>,
|
||||||
@ -40,6 +40,32 @@ async fn get_movie_auto(
|
|||||||
.map_err(|_| ApiError::InternalError)
|
.map_err(|_| ApiError::InternalError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/movie/{quality}/{slug}/segments/{chunk}")]
|
||||||
|
async fn get_movie_chunk(
|
||||||
|
req: HttpRequest,
|
||||||
|
query: web::Path<(String, String, u32)>,
|
||||||
|
transcoder: web::Data<Transcoder>,
|
||||||
|
) -> Result<NamedFile, ApiError> {
|
||||||
|
let (quality, slug, chunk) = query.into_inner();
|
||||||
|
let quality = Quality::from_str(quality.as_str()).map_err(|_| ApiError::BadRequest {
|
||||||
|
error: "Invalid quality".to_string(),
|
||||||
|
})?;
|
||||||
|
let client_id = req.headers().get("x-client-id")
|
||||||
|
.ok_or(ApiError::BadRequest { error: String::from("Missing client id. Please specify the X-CLIENT-ID header to a guid constant for the lifetime of the player (but unique per instance)."), })?
|
||||||
|
.to_str().unwrap();
|
||||||
|
|
||||||
|
let path = paths::get_movie_path(slug);
|
||||||
|
// TODO: Handle start_time that is not 0
|
||||||
|
transcoder
|
||||||
|
.get_segment(client_id.to_string(), path, quality, chunk)
|
||||||
|
.await
|
||||||
|
.map_err(|_| ApiError::InternalError)
|
||||||
|
.and_then(|path| {
|
||||||
|
NamedFile::open(path).map_err(|_| ApiError::BadRequest {
|
||||||
|
error: "Invalid segment number.".to_string(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
@ -49,7 +75,8 @@ async fn main() -> std::io::Result<()> {
|
|||||||
App::new()
|
App::new()
|
||||||
.app_data(state.clone())
|
.app_data(state.clone())
|
||||||
.service(get_movie_direct)
|
.service(get_movie_direct)
|
||||||
.service(get_movie_auto)
|
.service(transcode_movie)
|
||||||
|
.service(get_movie_chunk)
|
||||||
})
|
})
|
||||||
.bind(("0.0.0.0", 7666))?
|
.bind(("0.0.0.0", 7666))?
|
||||||
.run()
|
.run()
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
use rand::distributions::Alphanumeric;
|
use rand::distributions::Alphanumeric;
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::atomic::AtomicI32;
|
use std::sync::atomic::AtomicI32;
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, RwLock};
|
||||||
use std::{collections::HashMap, sync::Mutex};
|
use std::collections::HashMap;
|
||||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||||
use tokio::process::{Child, Command};
|
use tokio::process::{Child, Command};
|
||||||
|
|
||||||
@ -136,6 +137,14 @@ async fn start_transcode(path: String, quality: Quality, start_time: i32) -> Tra
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_cache_path(info: &TranscodeInfo) -> PathBuf {
|
||||||
|
return get_cache_path_from_uuid(&info.uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_cache_path_from_uuid(uuid: &String) -> PathBuf {
|
||||||
|
return PathBuf::from(format!("/cache/{uuid}/stream.m3u8", uuid = &uuid));
|
||||||
|
}
|
||||||
|
|
||||||
struct TranscodeInfo {
|
struct TranscodeInfo {
|
||||||
show: (String, Quality),
|
show: (String, Quality),
|
||||||
// TODO: Store if the process as ended (probably Option<Child> for the job)
|
// TODO: Store if the process as ended (probably Option<Child> for the job)
|
||||||
@ -147,13 +156,13 @@ struct TranscodeInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Transcoder {
|
pub struct Transcoder {
|
||||||
running: Mutex<HashMap<String, TranscodeInfo>>,
|
running: RwLock<HashMap<String, TranscodeInfo>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transcoder {
|
impl Transcoder {
|
||||||
pub fn new() -> Transcoder {
|
pub fn new() -> Transcoder {
|
||||||
Self {
|
Self {
|
||||||
running: Mutex::new(HashMap::new()),
|
running: RwLock::new(HashMap::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,19 +181,38 @@ impl Transcoder {
|
|||||||
job,
|
job,
|
||||||
uuid,
|
uuid,
|
||||||
..
|
..
|
||||||
}) = self.running.lock().unwrap().get_mut(&client_id)
|
}) = self.running.write().unwrap().get_mut(&client_id)
|
||||||
{
|
{
|
||||||
if path != *old_path || quality != *old_qual {
|
if path != *old_path || quality != *old_qual {
|
||||||
job.interrupt()?;
|
job.interrupt()?;
|
||||||
} else {
|
} else {
|
||||||
let path = format!("/cache/{uuid}/stream.m3u8", uuid = &uuid);
|
return std::fs::read_to_string(get_cache_path_from_uuid(uuid));
|
||||||
return std::fs::read_to_string(path);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let info = start_transcode(path, quality, start_time).await;
|
let info = start_transcode(path, quality, start_time).await;
|
||||||
let path = format!("/cache/{uuid}/stream.m3u8", uuid = &info.uuid);
|
let path = get_cache_path(&info);
|
||||||
self.running.lock().unwrap().insert(client_id, info);
|
self.running.write().unwrap().insert(client_id, info);
|
||||||
std::fs::read_to_string(path)
|
std::fs::read_to_string(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_segment(
|
||||||
|
&self,
|
||||||
|
client_id: String,
|
||||||
|
_path: String,
|
||||||
|
_quality: Quality,
|
||||||
|
chunk: u32,
|
||||||
|
) -> Result<PathBuf, SegmentError> {
|
||||||
|
let hashmap = self.running.read().unwrap();
|
||||||
|
let info = hashmap.get(&client_id).ok_or(SegmentError::NoTranscode)?;
|
||||||
|
|
||||||
|
// TODO: Check if ready_time is far enough for this fragment to exist.
|
||||||
|
let mut path = get_cache_path(&info);
|
||||||
|
path.push(format!("segments-{0:02}.ts", chunk));
|
||||||
|
Ok(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SegmentError {
|
||||||
|
NoTranscode,
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user