4 Commits
8 changed files with 198 additions and 80 deletions
+20
View File
@@ -0,0 +1,20 @@
Detailed description of the issue. Put as much information as you can, potentially
with images showing the issue.
Steps to reproduce:
1. Open Hammond
2. Do an action
3. ...
## Design Tasks
* [ ] design tasks
## Development Tasks
* [ ] development tasks
## QA Tasks
* [ ] qa (quality assurance) tasks
+17
View File
@@ -0,0 +1,17 @@
Detailed description of the feature. Put as much information as you can.
Proposed Mockups:
(Add mockups of the proposed feature)
## Design Tasks
* [ ] design tasks
## Development Tasks
* [ ] development tasks
## QA Tasks
* [ ] qa (quality assurance) tasks
+46
View File
@@ -1,3 +1,5 @@
> Adapted from Gnome-TODO
> https://gitlab.gnome.org/GNOME/gnome-todo/blob/582f9a57b84f92dc629b2042b887188878578cdb/CONTRIBUTING.md
## Contributing
Contributing
@@ -22,6 +24,50 @@ It is recommended to add a pre-commit hook to run cargo test and cargo fmt
cargo test --all && cargo fmt --all -- --write-mode=diff
```
# Issues, issues and more issues!
There are many ways you can contribute to Hammond, and all of them involve creating issues
in [Hammond issue tracker](https://gitlab.gnome.org/alatiera/Hammond/issues). This is the
entry point for your contribution.
To create an effective and high quality ticket, try to put the following information on your
ticket:
1. A detailed description of the issue or feature request
- For issues, please add the necessary steps to reproduce the issue.
- For feature requests, add a detailed description of your proposal.
2. A checklist of Development tasks
3. A checklist of Design tasks
4. A checklist of QA tasks
## Issue template
```
[Title of the issue or feature request]
Detailed description of the issue. Put as much information as you can, potentially
with images showing the issue or mockups of the proposed feature.
If it's an issue, add the steps to reproduce like this:
Steps to reproduce:
1. Open Hammond
2. Do an Action
3. ...
## Design Tasks
* [ ] design tasks
## Development Tasks
* [ ] development tasks
## QA Tasks
* [ ] qa (quality assurance) tasks
```
## Pull Request Process
1. Ensure your code compiles. Run `make` before creating the pull request.
+1 -1
View File
@@ -3,7 +3,7 @@
**General:**
- [x] Add CONTRIBUTING.md
- [ ] Add Issues and Pull Request templates
- [x] Add Issues and Pull Request templates
- [ ] Write docs
+88 -77
View File
@@ -14,68 +14,77 @@ use hammond_data::index_feed::Database;
use hammond_data::models::{Episode, Podcast};
use hammond_data::{DL_DIR, HAMMOND_CACHE};
// TODO: Replace path that are of type &str with std::path.
// TODO: Have a convention/document absolute/relative paths, if they should end with / or not.
// Adapted from https://github.com/mattgathu/rget .
// I never wanted to write a custom downloader.
// Sorry to those who will have to work with that code.
// Would much rather use a crate,
// or bindings for a lib like youtube-dl(python),
// But cant seem to find one.
// TODO: Write unit-tests.
fn download_into(dir: &str, file_title: &str, url: &str) -> Result<String> {
info!("GET request to: {}", url);
let client = reqwest::Client::builder().referer(false).build()?;
let mut resp = client.get(url).send()?;
info!("Status Resp: {}", resp.status());
if resp.status().is_success() {
let headers = resp.headers().clone();
if !resp.status().is_success() {
// TODO: Return an error instead of panicking.
panic!("Bad request response.");
}
let ct_len = headers.get::<ContentLength>().map(|ct_len| **ct_len);
let ct_type = headers.get::<ContentType>();
ct_len.map(|x| info!("File Lenght: {}", x));
ct_type.map(|x| info!("Content Type: {}", x));
let headers = resp.headers().clone();
// This could be prettier.
let ext = if let Some(t) = ct_type {
let mime = mime_guess::get_extensions(t.type_().as_ref(), t.subtype().as_ref());
if let Some(m) = mime {
if m.contains(&t.subtype().as_ref()) {
t.subtype().as_ref().to_string()
} else {
m.first().unwrap().to_string()
}
let ct_len = headers.get::<ContentLength>().map(|ct_len| **ct_len);
let ct_type = headers.get::<ContentType>();
ct_len.map(|x| info!("File Lenght: {}", x));
ct_type.map(|x| info!("Content Type: {}", x));
// This could be prettier.
// Determine the file extension from the http content-type header.
let ext = if let Some(t) = ct_type {
let mime = mime_guess::get_extensions(t.type_().as_ref(), t.subtype().as_ref());
if let Some(m) = mime {
if m.contains(&t.subtype().as_ref()) {
t.subtype().as_ref().to_string()
} else {
error!("Unkown mime type. {}", t);
"unkown".to_string()
m.first().unwrap().to_string()
}
} else {
error!("Unkown mime type.");
error!("Unkown mime type. {}", t);
"unkown".to_string()
};
info!("Extension: {}", ext);
}
} else {
error!("Unkown mime type.");
"unkown".to_string()
};
info!("Extension: {}", ext);
// Construct a temp file to save desired content.
let tempdir = TempDir::new_in(dir, "")?;
let mut rng = rand::thread_rng();
// Construct a temp file to save desired content.
let tempdir = TempDir::new_in(dir, "")?;
let mut rng = rand::thread_rng();
let out_file = format!(
"{}/{}.part",
tempdir.path().to_str().unwrap(),
rng.gen::<usize>()
);
let out_file = format!(
"{}/{}.part",
tempdir.path().to_str().unwrap(),
rng.gen::<usize>()
);
save_io(&out_file, &mut resp, ct_len)?;
// Save requested content into the file.
save_io(&out_file, &mut resp, ct_len)?;
// Construct the desired path.
let target = format!("{}/{}.{}", dir, file_title, ext);
// Rename/move the tempfile into a permanent place.
rename(out_file, &target)?;
info!("Downloading of {} completed succesfully.", &target);
return Ok(target);
}
// Ok(String::from(""))
panic!("Bad request response.");
// Construct the desired path.
let target = format!("{}/{}.{}", dir, file_title, ext);
// Rename/move the tempfile into a permanent place upon success.
rename(out_file, &target)?;
info!("Downloading of {} completed succesfully.", &target);
Ok(target)
}
// TODO: Write unit-tests.
/// Handles the I/O of fetching a remote file and saving into a Buffer and A File.
fn save_io(file: &str, resp: &mut reqwest::Response, content_lenght: Option<u64>) -> Result<()> {
info!("Downloading into: {}", file);
let chunk_size = match content_lenght {
@@ -116,6 +125,7 @@ pub fn get_episode(connection: &Database, ep: &mut Episode, download_folder: &st
return Ok(());
}
// If the path is not valid, then set it to None.
ep.set_local_uri(None);
ep.save(connection)?;
};
@@ -134,50 +144,51 @@ pub fn get_episode(connection: &Database, ep: &mut Episode, download_folder: &st
}
pub fn cache_image(pd: &Podcast) -> Option<String> {
if pd.image_uri().is_some() {
let url = pd.image_uri().unwrap().to_owned();
if url == "" {
return None;
}
if pd.image_uri().is_none() {
return None;
}
let download_fold = format!(
"{}{}",
HAMMOND_CACHE.to_str().unwrap(),
pd.title().to_owned()
);
let url = pd.image_uri().unwrap().to_owned();
if url == "" {
return None;
}
// Hacky way
// TODO: make it so it returns the first cover.* file encountered.
let png = format!("{}/cover.png", download_fold);
let jpg = format!("{}/cover.jpg", download_fold);
let jpe = format!("{}/cover.jpe", download_fold);
let jpeg = format!("{}/cover.jpeg", download_fold);
if Path::new(&png).exists() {
return Some(png);
} else if Path::new(&jpe).exists() {
return Some(jpe);
} else if Path::new(&jpg).exists() {
return Some(jpg);
} else if Path::new(&jpeg).exists() {
return Some(jpeg);
};
let download_fold = format!(
"{}{}",
HAMMOND_CACHE.to_str().unwrap(),
pd.title().to_owned()
);
DirBuilder::new()
.recursive(true)
.create(&download_fold)
.unwrap();
// Hacky way
// TODO: make it so it returns the first cover.* file encountered.
let png = format!("{}/cover.png", download_fold);
let jpg = format!("{}/cover.jpg", download_fold);
let jpe = format!("{}/cover.jpe", download_fold);
let jpeg = format!("{}/cover.jpeg", download_fold);
if Path::new(&png).exists() {
return Some(png);
} else if Path::new(&jpe).exists() {
return Some(jpe);
} else if Path::new(&jpg).exists() {
return Some(jpg);
} else if Path::new(&jpeg).exists() {
return Some(jpeg);
};
let dlpath = download_into(&download_fold, "cover", &url);
if let Ok(path) = dlpath {
info!("Cached img into: {}", &path);
return Some(path);
} else {
error!("Failed to get feed image.");
error!("Error: {}", dlpath.unwrap_err());
return None;
};
DirBuilder::new()
.recursive(true)
.create(&download_fold)
.unwrap();
let dlpath = download_into(&download_fold, "cover", &url);
if let Ok(path) = dlpath {
info!("Cached img into: {}", &path);
Some(path)
} else {
error!("Failed to get feed image.");
error!("Error: {}", dlpath.unwrap_err());
None
}
None
}
#[cfg(test)]
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop">
<id>org.gnome.Hammond</id>
<name>Hammond</name>
<project_license>GPL-3.0</project_license>
<metadata_license>CC0-1.0</metadata_license>
<developer_name>Daniel García Moreno</developer_name>
<summary>Gtk+ Matrix.org client</summary>
<url type="homepage">https://gitlab.gnome.org/alatiera/Hammond</url>
<description>
Hammond is a Fast, Safe and Reliable Gtk+ Podcast client written in Rust
</description>
<screenshots>
<screenshot>
<image type="source">https://gitlab.gnome.org/alatiera/Hammond/raw/master/assets/podcasts_view.png</image>
<image type="source">https://gitlab.gnome.org/alatiera/Hammond/raw/master/assets/podcast_widget.png</image>
</screenshot>
</screenshots>
<releases>
<release version="0.1.1" date="2017-11-13"/>
</releases>
<update_contact>jordanpetridis@protonmail.com</update_contact>
</component>
+3 -2
View File
@@ -3,7 +3,7 @@
project(
'hammond', 'rust',
version: '0.1.0',
version: '0.1.1',
license: 'GPLv3',
)
@@ -16,7 +16,8 @@ hammond_version_micro = version_array[2].to_int()
hammond_prefix = get_option('prefix')
hammond_bindir = join_paths(hammond_prefix, get_option('bindir'))
install_data('assets/org.gnome.Hammond.desktop', install_dir : get_option('datadir') + '/applications')
install_data('hammond-gtk/resources/org.gnome.Hammond.desktop', install_dir : get_option('datadir') + '/applications')
install_data('hammond-gtk/resources/org.gnome.Hammond.appdata.xml', install_dir : get_option('datadir') + '/appdata')
cargo = find_program('cargo', required: false)
gresource = find_program('glib-compile-resources', required: false)