Skip to content
Snippets Groups Projects
Commit 73f798b2 authored by Falk Rehse's avatar Falk Rehse
Browse files

Serve thumbnails and frontend

parent 4d1c370e
No related branches found
No related tags found
No related merge requests found
Pipeline #118405 failed
......@@ -2,5 +2,5 @@
path = cc-player
url = git@gitlab.fachschaften.org:spoccify/spoccify-cc-player.git
[submodule "frontend"]
path = frontend
path = svelte-frontend
url = git@gitlab.fachschaften.org:spoccify/spoccify-frontend.git
......@@ -205,6 +205,12 @@ dependencies = [
"serde",
]
[[package]]
name = "futures-sink"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
[[package]]
name = "futures-task"
version = "0.3.25"
......@@ -377,6 +383,16 @@ version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "mime_guess"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "mio"
version = "0.8.5"
......@@ -656,6 +672,8 @@ dependencies = [
"serde",
"serde_json",
"tokio",
"tower",
"tower-http",
"tungstenite",
"url",
"uuid",
......@@ -744,6 +762,19 @@ dependencies = [
"syn",
]
[[package]]
name = "tokio-util"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tower"
version = "0.4.13"
......@@ -773,10 +804,17 @@ dependencies = [
"http",
"http-body",
"http-range-header",
"httpdate",
"mime",
"mime_guess",
"percent-encoding",
"pin-project-lite",
"tokio",
"tokio-util",
"tower",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
......@@ -843,6 +881,15 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.8"
......
......@@ -14,6 +14,8 @@ rand = "0.8.5"
serde = { version = "1.0.147", features = ["derive"] }
serde_json = "1.0.88"
tokio = { version = "1", features = ["full"] }
tower = { version = "0.4.13", features = ["util"] }
tower-http = { version = "0.3.4", features = ["fs", "trace"] }
tungstenite = "0.17.3"
url = { version = "2.3.1", features = ["serde"] }
uuid = { version = "1.2.2", features = ["v4"] }
......@@ -8,7 +8,6 @@ use url::Url;
use uuid::Uuid;
use crate::song::Song;
use crate::util;
const TMP_DOWNLOAD_DIR: &str = "./tmp";
......@@ -167,25 +166,28 @@ pub async fn manage_downloads(playlist: &Mutable<Vec<Song>>) -> () {
}
fn fetch_info(playlist: Mutable<Vec<Song>>, uuid: &Uuid, url: &Url) {
println!("Fetching info for {}!", url);
println!("Fetching info for {}!", uuid);
let tmp_file_name = util::random_base64();
let tmp_file_path = Utf8Path::new(TMP_DOWNLOAD_DIR).join(tmp_file_name);
let base_path = Utf8Path::new(TMP_DOWNLOAD_DIR).join(uuid.to_string());
let info_json_path = base_path.join("audio");
std::fs::create_dir(&base_path)
.expect(&format!("Failed to create directory for song {}!", uuid));
process::Command::new("yt-dlp")
.args([
"--output",
tmp_file_path.as_str(),
info_json_path.as_str(),
"--write-info-json",
"--write-thumbnail",
"--skip-download",
url.as_str(),
])
.output()
.expect("Failed to fetch info!");
let info_json_file = format!("{}.info.json", tmp_file_path);
let data = std::fs::read_to_string(info_json_file).expect("Could not find info.json!");
let info_json_path = base_path.join("audio.info.json");
let data = std::fs::read_to_string(&info_json_path).expect("Could not find info.json!");
let info_json: Value = serde_json::from_str(&data).expect("Could not parse info.json!");
......@@ -220,9 +222,27 @@ fn fetch_info(playlist: Mutable<Vec<Song>>, uuid: &Uuid, url: &Url) {
} else {
eprintln!("Could not fetch artist_url!");
}
println!("Fetched all info!");
let thumbnail_file = format!("{}.webm", tmp_file_path);
let thumbnail_path = base_path.join("thumbnail");
process::Command::new("yt-dlp")
.args([
"--load-info-json",
info_json_path.as_str(),
"--output",
thumbnail_path.as_str(),
"--write-thumbnail",
"--skip-download",
url.as_str(),
])
.output()
.expect("Failed to download thumbnail!");
let thumbnail_path = base_path.join("thumbnail.webp");
println!("Downloaded thumbnail!");
let mut playlist = playlist.lock_mut();
if let Some(index) = (*playlist).iter().position(|song| song.equals_uuid(uuid)) {
......@@ -233,7 +253,7 @@ fn fetch_info(playlist: Mutable<Vec<Song>>, uuid: &Uuid, url: &Url) {
artist: artist,
artist_url: artist_url,
length: length,
thumbnail_file: thumbnail_file.to_owned(),
thumbnail_file: thumbnail_path.to_string(),
};
}
drop(playlist);
......@@ -249,19 +269,26 @@ fn download_song(
length: &Option<u64>,
thumbnail_file: &str,
) {
let audio_file_name = util::random_base64();
let audio_file_path = Utf8Path::new(TMP_DOWNLOAD_DIR).join(audio_file_name);
let base_path = Utf8Path::new(TMP_DOWNLOAD_DIR).join(uuid.to_string());
let info_json_path = base_path.join("audio.info.json");
let audio_file_path = base_path.join("audio");
println!("Downloading from {} to {}!", url, audio_file_path);
process::Command::new("yt-dlp")
.args(["--output", audio_file_path.as_str(), url.as_str()])
.args([
"--load-info-json",
info_json_path.as_str(),
"--output",
audio_file_path.as_str(),
url.as_str(),
])
.output()
.expect("Failed to download audio!");
println!("Download successful!");
let audio_file_path = base_path.join("audio.webm");
let audio_file_path = format!("{}.webm", audio_file_path);
println!("Download successful!");
let mut playlist = playlist.lock_mut();
if let Some(index) = (*playlist).iter().position(|song| song.equals_uuid(uuid)) {
......@@ -273,7 +300,7 @@ fn download_song(
artist_url: artist_url.to_owned(),
length: length.to_owned(),
thumbnail_file: thumbnail_file.to_owned(),
audio_file: audio_file_path.to_owned(),
audio_file: audio_file_path.to_string(),
};
}
drop(playlist);
......@@ -290,17 +317,16 @@ fn convert_song(
thumbnail_file: &str,
audio_file: &str,
) {
let audio_file = Utf8Path::new(audio_file);
let raw_file_name = format!("{}.raw", util::random_base64());
let raw_file_path = Utf8Path::new(TMP_DOWNLOAD_DIR).join(raw_file_name);
let base_path = Utf8Path::new(TMP_DOWNLOAD_DIR).join(uuid.to_string());
let audio_file_path = base_path.join("audio.webm");
let raw_file_path = base_path.join("audio.raw");
println!("Converting from {} to {}!", audio_file, raw_file_path);
process::Command::new("ffmpeg")
.args([
"-i",
audio_file.as_str(),
audio_file_path.as_str(),
"-f",
"u8",
"-acodec",
......@@ -313,7 +339,8 @@ fn convert_song(
])
.output()
.expect("Failed to convert audio!");
std::fs::remove_file(audio_file).expect("Failed to remove audio file!");
std::fs::remove_file(&audio_file_path).expect("Failed to remove audio file!");
println!("Conversion successful!");
......@@ -327,7 +354,7 @@ fn convert_song(
artist_url: artist_url.to_owned(),
length: length.to_owned(),
thumbnail_file: thumbnail_file.to_owned(),
raw_file: raw_file_path.as_str().to_owned(),
raw_file: raw_file_path.to_string(),
};
}
drop(playlist);
......
use axum::handler::HandlerWithoutStateExt;
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::routing::get_service;
use axum::Router;
use axum::routing::get;
// use futures_signals::signal::Mutable;
use std::io;
use tower::util::ServiceExt;
use tower_http::services::ServeDir;
pub async fn handle_http(http_bind_address: &str) {
let app = Router::new().route("/", get(|| async { "Hello, World!" }));
// use crate::song::Song;
axum::Server::bind(&http_bind_address.to_string().parse().expect("Invalid http bind address!"))
pub async fn handle_http(http_bind_address: &str, /* playlist_websocket: &Mutable<Vec<Song>> */) {
println!("Listening for http connections on {}!", http_bind_address);
async fn handle_404() -> (StatusCode, &'static str) {
(StatusCode::NOT_FOUND, "Not found")
}
let handle_404_service = handle_404
.into_service()
.map_err(|err| -> std::io::Error { match err {} });
let serve_frontend = ServeDir::new("frontend").not_found_service(handle_404_service.clone());
let serve_frontend = get_service(serve_frontend).handle_error(handle_error);
let serve_thumbnails = ServeDir::new("tmp").not_found_service(handle_404_service.clone());
let serve_thumbnails = get_service(serve_thumbnails).handle_error(handle_error);
let app = Router::new()
.nest_service("/", serve_frontend)
.nest_service("/thumbnail", serve_thumbnails);
axum::Server::bind(
&http_bind_address
.to_string()
.parse()
.expect("Invalid http bind address!"),
)
.serve(app.into_make_service())
.await
.expect("Failed to bind http port!");
}
async fn handle_error(_err: io::Error) -> impl IntoResponse {
(StatusCode::INTERNAL_SERVER_ERROR, "Internal server error")
}
......@@ -15,32 +15,30 @@ use song::Song;
#[tokio::main]
async fn main() {
let playlist = Mutable::new(Vec::<Song>::new());
println!("Starting!");
let http_bind_host = env::var("SPOCCIFY_HTTP_BIND_HOST").unwrap_or("localhost".to_owned());
let playlist = Mutable::new(Vec::<Song>::new());
// let playlist_http = Mutable::clone(&playlist);
let playlist_websockets = Mutable::clone(&playlist);
let playlist_downloader = Mutable::clone(&playlist);
let http_bind_host = env::var("SPOCCIFY_HTTP_BIND_HOST").unwrap_or("127.0.0.1".to_owned());
let http_bind_port = env::var("SPOCCIFY_HTTP_BIND_PORT").unwrap_or("9000".to_owned());
let http_bind_address = format!("{}:{}", http_bind_host, http_bind_port);
// tokio::spawn(async move {
// http::handle_http(&http_bind_address).await;
// });
let player_token =
Arc::new(env::var("SPOCCIFY_PLAYER_TOKEN").expect("No player token provided!"));
let client_token =
Arc::new(env::var("SPOCCIFY_CLIENT_TOKEN").expect("No client token provided!"));
let websocket_bind_host =
env::var("SPOCCIFY_WEBSOCKET_BIND_HOST").unwrap_or("localhost".to_owned());
env::var("SPOCCIFY_WEBSOCKET_BIND_HOST").unwrap_or("127.0.0.1".to_owned());
let websocket_bind_port = env::var("SPOCCIFY_WEBSOCKET_BIND_PORT").unwrap_or("9001".to_owned());
let websocket_bind_address = format!("{}:{}", websocket_bind_host, websocket_bind_port);
let playlist_websockets = Mutable::clone(&playlist);
tokio::spawn(async move {
let websockets_task = tokio::task::spawn(async move {
websocket::handle_websockets(
&player_token,
&client_token,
......@@ -50,11 +48,20 @@ async fn main() {
.await;
});
let playlist_downloader = Mutable::clone(&playlist);
tokio::spawn(async move {
let downloader_task = tokio::task::spawn(async move {
downloader::manage_downloads(&playlist_downloader).await;
});
loop {}
tokio::join!(
http::handle_http(&http_bind_address /* , &playlist_http */),
/* websocket::handle_websockets(
&player_token,
&client_token,
&websocket_bind_address,
&playlist_websockets,
), */
// websockets_task.await.unwrap(),
/* downloader::manage_downloads(&playlist_downloader) */
// downloader_task.await.unwrap(),
);
}
use rand::random;
pub fn random_base64() -> String {
pub fn _random_base64() -> String {
base64::encode_config(
random::<[u8; 8]>(),
base64::Config::new(base64::CharacterSet::UrlSafe, false),
......
......@@ -12,9 +12,10 @@ use crate::client;
use crate::player;
pub async fn handle_websockets(player_token: &Arc<String>, client_token: &Arc<String>, websocket_bind_address: &str, playlist_websocket: &Mutable<Vec<Song>>) {
let server = TcpListener::bind(websocket_bind_address).expect("Failed to bind websocket port!");
println!("Listening for websocket connections on {}!", websocket_bind_address);
let server = TcpListener::bind(websocket_bind_address).expect("Failed to bind websocket port!");
for stream in server.incoming() {
let playlist_clone = Mutable::clone(playlist_websocket);
let player_token_clone = Arc::clone(player_token);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment