Compare commits

..

2 Commits

Author SHA1 Message Date
Leonora Tindall d38429390c
Polish and publish sigweb 2021-10-18 20:22:26 -05:00
Leonora Tindall a9acc17c79
feat: sigweb basic setup 2021-10-18 19:24:21 -05:00
11 changed files with 168 additions and 13 deletions

6
.gitignore vendored
View File

@ -1,3 +1,3 @@
libsig/target/ libsig/target
sigweb/target/ sigweb/target
target/ target

30
Cargo.lock generated
View File

@ -585,6 +585,35 @@ dependencies = [
"regex-automata", "regex-automata",
] ]
[[package]]
name = "maud"
version = "0.22.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59d449907de7d1ae5b290cbaf9ea34a8a7df3fa5db027664bb55bb2b0fc1407c"
dependencies = [
"maud_htmlescape",
"maud_macros",
]
[[package]]
name = "maud_htmlescape"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0555daa37f94b5ebb206faf8cdc7b260c2aa371b509e929de9a1e37416d97fa6"
[[package]]
name = "maud_macros"
version = "0.22.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6896f8e8cdcea80b99ac0f1f7a233708e640737a8517448f50500e401bb8d76"
dependencies = [
"maud_htmlescape",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.4.1" version = "2.4.1"
@ -1162,6 +1191,7 @@ name = "sigweb"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"libsig", "libsig",
"maud",
"rocket", "rocket",
"serde", "serde",
"serde_json", "serde_json",

View File

@ -4,6 +4,8 @@ Consisting of a Rust library for generating song ideas `libsig`,
a command line tool for calling `libsig`, a command line tool for calling `libsig`,
and a web interface with a JSON API for getting structured results from `libsig`. and a web interface with a JSON API for getting structured results from `libsig`.
![an example of sigweb in use](sigweb.png)
## Usage ## Usage
The primary type of `libsig` is the `SongIdea`. You can generate human-readable representations with `Display` (so, `println!("{}", my_song_idea)`). The primary type of `libsig` is the `SongIdea`. You can generate human-readable representations with `Display` (so, `println!("{}", my_song_idea)`).
@ -19,4 +21,8 @@ buzzy pad, glassy vocals, plonky kick, plucky snare, and dry tom at 63 bpm, with
surfy lead, blownout pad, light vocals, acidic snare, sonorous hat, and digital tom at 33 bpm, with no swing, through lofi surfy lead, blownout pad, light vocals, acidic snare, sonorous hat, and digital tom at 33 bpm, with no swing, through lofi
``` ```
It also takes a parameter `--ambient` to avoid suggesting percussion parts. It also takes a parameter `--ambient` to avoid suggesting percussion parts.
`sigweb` is a very basic Rocket app. I suggest building it with `x86_64-unknown-linux-musl`. Once built take the `sigweb` binary and put it in a folder with an appropriate `Rocket.toml`. You can set a base URL with the environment variable `SIGWEB_BASE_URL`. See the provided `sigweb.service` for a sample SystemD unit.
It provides a JSON API with the endpoints `/api/v1/{generate, generate_ambient}`.

2
libsig/.gitignore vendored
View File

@ -1,2 +0,0 @@
/target
Cargo.lock

7
libsig/Cargo.lock generated Normal file
View File

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "libsig"
version = "0.1.0"

BIN
sigweb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -6,8 +6,9 @@ edition = "2018"
[dependencies] [dependencies]
serde = "1" serde = "1"
serde_json = "1" serde_json = "1"
maud = "0.22"
libsig = { path = "../libsig" } libsig = { path = "../libsig" }
[dependencies.rocket] [dependencies.rocket]
version = "0.5.0-rc.1" version = "0.5.0-rc.1"
features = ["json"] features = ["json"]

12
sigweb/sigweb.service Normal file
View File

@ -0,0 +1,12 @@
[Unit]
Description=Song Idea Generator Web Service
[Service]
Type=simple
User=nora
WorkingDirectory=/home/nora/sigweb
ExecStart=/home/nora/sigweb/sigweb
Environment=SIGWEB_BASE_URL="https://nora.codes/sigweb/"
[Install]
WantedBy=multi-user.target

View File

@ -1,9 +1,16 @@
#[macro_use] #[macro_use]
extern crate rocket; extern crate rocket;
use libsig; use libsig;
use serde::{Serialize, Deserialize}; use rocket::response::content::{Css, Html};
use rocket::serde::json::Json; use rocket::serde::json::Json;
use rocket::State;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value}; use serde_json::{json, Value};
mod templates;
struct BaseUrl {
url: String,
}
#[get("/api")] #[get("/api")]
fn api_version_available() -> Json<Value> { fn api_version_available() -> Json<Value> {
@ -26,24 +33,64 @@ struct Output {
} }
#[get("/api/v1/generate")] #[get("/api/v1/generate")]
fn generate() -> Json<Output> { fn api_v1_generate() -> Json<Output> {
let idea = libsig::SongIdea::generate(); let idea = libsig::SongIdea::generate();
Json(Output { Json(Output {
data: idea.clone(), data: idea.clone(),
text: idea.to_string() text: idea.to_string(),
}) })
} }
#[get("/api/v1/generate_ambient")] #[get("/api/v1/generate_ambient")]
fn generate_ambient() -> Json<Output> { fn api_v1_generate_ambient() -> Json<Output> {
let idea = libsig::SongIdea::generate_ambient(); let idea = libsig::SongIdea::generate_ambient();
Json(Output { Json(Output {
data: idea.clone(), data: idea.clone(),
text: idea.to_string() text: idea.to_string(),
}) })
} }
#[get("/")]
fn index(base: &State<BaseUrl>) -> Html<String> {
Html(templates::main_page_template(None, &base.url))
}
#[get("/generate")]
fn generate(base: &State<BaseUrl>) -> Html<String> {
Html(templates::main_page_template(Some(
libsig::SongIdea::generate(),
), &base.url))
}
#[get("/generate_ambient")]
fn generate_ambient(base: &State<BaseUrl>) -> Html<String> {
Html(templates::main_page_template(Some(
libsig::SongIdea::generate_ambient(),
), &base.url))
}
#[get("/style.css")]
async fn style_css() -> Css<String> {
Css(include_str!("../static/style.css").into())
}
#[launch] #[launch]
fn rocket() -> _ { fn rocket() -> _ {
rocket::build().mount("/", routes![api_version_available, api_v1_index, generate, generate_ambient]) rocket::build()
.manage(BaseUrl {
url: std::env::var("SIGWEB_BASE_URL").unwrap_or("/".to_string()),
})
.mount(
"/",
routes![
api_version_available,
api_v1_index,
api_v1_generate,
api_v1_generate_ambient,
generate,
generate_ambient,
index,
style_css,
],
)
} }

35
sigweb/src/templates.rs Normal file
View File

@ -0,0 +1,35 @@
use maud::{html, DOCTYPE};
use libsig::SongIdea;
pub fn main_page_template(idea: Option<SongIdea>, base_url: &str) -> String {
let html = html! {
(DOCTYPE)
head {
link rel="stylesheet" href="style.css";
base href=(base_url);
}
body {
h1 { "Song Idea Generator" }
(match idea {
Some(idea) => html!{
p { "Why don't you make a song like this: " }
p.content { (idea) }
},
None => html! { p { "Generate a new idea with the links below!" } p.content {} }
})
footer {
p {
a href="./generate" { "generate new" }
" | "
a href="./generate_ambient" { "generate new (ambient)" }
}
p {
a href="http://nora.codes" { "my blog" }
" | "
a href="https://git.nora.codes/nora/song-idea-generator" { "code" }
}
}
}
};
html.into()
}

19
sigweb/static/style.css Normal file
View File

@ -0,0 +1,19 @@
body {
background-color: bisque;
color: darkblue;
font-size: 12pt;
font-family: sans-serif;
max-width: 300pt;
margin-left: auto;
margin-right: auto;
}
p.content {
padding-top: auto;
min-height: 100pt;
}
footer {
font-size: 10pt;
text-align: center;
}