Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab002ebcd7 | ||
|
|
1e45adc034 | ||
|
|
4bf84ed170 | ||
|
|
e3f0263dc0 |
@@ -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
|
||||||
@@ -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
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
> Adapted from Gnome-TODO
|
||||||
|
> https://gitlab.gnome.org/GNOME/gnome-todo/blob/582f9a57b84f92dc629b2042b887188878578cdb/CONTRIBUTING.md
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
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
|
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
|
## Pull Request Process
|
||||||
|
|
||||||
1. Ensure your code compiles. Run `make` before creating the pull request.
|
1. Ensure your code compiles. Run `make` before creating the pull request.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
**General:**
|
**General:**
|
||||||
|
|
||||||
- [x] Add CONTRIBUTING.md
|
- [x] Add CONTRIBUTING.md
|
||||||
- [ ] Add Issues and Pull Request templates
|
- [x] Add Issues and Pull Request templates
|
||||||
- [ ] Write docs
|
- [ ] Write docs
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,68 +14,77 @@ use hammond_data::index_feed::Database;
|
|||||||
use hammond_data::models::{Episode, Podcast};
|
use hammond_data::models::{Episode, Podcast};
|
||||||
use hammond_data::{DL_DIR, HAMMOND_CACHE};
|
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 .
|
// Adapted from https://github.com/mattgathu/rget .
|
||||||
// I never wanted to write a custom downloader.
|
// I never wanted to write a custom downloader.
|
||||||
// Sorry to those who will have to work with that code.
|
// Sorry to those who will have to work with that code.
|
||||||
// Would much rather use a crate,
|
// Would much rather use a crate,
|
||||||
// or bindings for a lib like youtube-dl(python),
|
// or bindings for a lib like youtube-dl(python),
|
||||||
// But cant seem to find one.
|
// But cant seem to find one.
|
||||||
|
// TODO: Write unit-tests.
|
||||||
fn download_into(dir: &str, file_title: &str, url: &str) -> Result<String> {
|
fn download_into(dir: &str, file_title: &str, url: &str) -> Result<String> {
|
||||||
info!("GET request to: {}", url);
|
info!("GET request to: {}", url);
|
||||||
let client = reqwest::Client::builder().referer(false).build()?;
|
let client = reqwest::Client::builder().referer(false).build()?;
|
||||||
let mut resp = client.get(url).send()?;
|
let mut resp = client.get(url).send()?;
|
||||||
info!("Status Resp: {}", resp.status());
|
info!("Status Resp: {}", resp.status());
|
||||||
|
|
||||||
if resp.status().is_success() {
|
if !resp.status().is_success() {
|
||||||
let headers = resp.headers().clone();
|
// TODO: Return an error instead of panicking.
|
||||||
|
panic!("Bad request response.");
|
||||||
|
}
|
||||||
|
|
||||||
let ct_len = headers.get::<ContentLength>().map(|ct_len| **ct_len);
|
let headers = resp.headers().clone();
|
||||||
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.
|
let ct_len = headers.get::<ContentLength>().map(|ct_len| **ct_len);
|
||||||
let ext = if let Some(t) = ct_type {
|
let ct_type = headers.get::<ContentType>();
|
||||||
let mime = mime_guess::get_extensions(t.type_().as_ref(), t.subtype().as_ref());
|
ct_len.map(|x| info!("File Lenght: {}", x));
|
||||||
if let Some(m) = mime {
|
ct_type.map(|x| info!("Content Type: {}", x));
|
||||||
if m.contains(&t.subtype().as_ref()) {
|
|
||||||
t.subtype().as_ref().to_string()
|
// This could be prettier.
|
||||||
} else {
|
// Determine the file extension from the http content-type header.
|
||||||
m.first().unwrap().to_string()
|
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 {
|
} else {
|
||||||
error!("Unkown mime type. {}", t);
|
m.first().unwrap().to_string()
|
||||||
"unkown".to_string()
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error!("Unkown mime type.");
|
error!("Unkown mime type. {}", t);
|
||||||
"unkown".to_string()
|
"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.
|
// Construct a temp file to save desired content.
|
||||||
let tempdir = TempDir::new_in(dir, "")?;
|
let tempdir = TempDir::new_in(dir, "")?;
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
let out_file = format!(
|
let out_file = format!(
|
||||||
"{}/{}.part",
|
"{}/{}.part",
|
||||||
tempdir.path().to_str().unwrap(),
|
tempdir.path().to_str().unwrap(),
|
||||||
rng.gen::<usize>()
|
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.
|
// Construct the desired path.
|
||||||
let target = format!("{}/{}.{}", dir, file_title, ext);
|
let target = format!("{}/{}.{}", dir, file_title, ext);
|
||||||
// Rename/move the tempfile into a permanent place.
|
// Rename/move the tempfile into a permanent place upon success.
|
||||||
rename(out_file, &target)?;
|
rename(out_file, &target)?;
|
||||||
info!("Downloading of {} completed succesfully.", &target);
|
info!("Downloading of {} completed succesfully.", &target);
|
||||||
return Ok(target);
|
Ok(target)
|
||||||
}
|
|
||||||
// Ok(String::from(""))
|
|
||||||
panic!("Bad request response.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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<()> {
|
fn save_io(file: &str, resp: &mut reqwest::Response, content_lenght: Option<u64>) -> Result<()> {
|
||||||
info!("Downloading into: {}", file);
|
info!("Downloading into: {}", file);
|
||||||
let chunk_size = match content_lenght {
|
let chunk_size = match content_lenght {
|
||||||
@@ -116,6 +125,7 @@ pub fn get_episode(connection: &Database, ep: &mut Episode, download_folder: &st
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the path is not valid, then set it to None.
|
||||||
ep.set_local_uri(None);
|
ep.set_local_uri(None);
|
||||||
ep.save(connection)?;
|
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> {
|
pub fn cache_image(pd: &Podcast) -> Option<String> {
|
||||||
if pd.image_uri().is_some() {
|
if pd.image_uri().is_none() {
|
||||||
let url = pd.image_uri().unwrap().to_owned();
|
return None;
|
||||||
if url == "" {
|
}
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let download_fold = format!(
|
let url = pd.image_uri().unwrap().to_owned();
|
||||||
"{}{}",
|
if url == "" {
|
||||||
HAMMOND_CACHE.to_str().unwrap(),
|
return None;
|
||||||
pd.title().to_owned()
|
}
|
||||||
);
|
|
||||||
|
|
||||||
// Hacky way
|
let download_fold = format!(
|
||||||
// TODO: make it so it returns the first cover.* file encountered.
|
"{}{}",
|
||||||
let png = format!("{}/cover.png", download_fold);
|
HAMMOND_CACHE.to_str().unwrap(),
|
||||||
let jpg = format!("{}/cover.jpg", download_fold);
|
pd.title().to_owned()
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
DirBuilder::new()
|
// Hacky way
|
||||||
.recursive(true)
|
// TODO: make it so it returns the first cover.* file encountered.
|
||||||
.create(&download_fold)
|
let png = format!("{}/cover.png", download_fold);
|
||||||
.unwrap();
|
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);
|
DirBuilder::new()
|
||||||
if let Ok(path) = dlpath {
|
.recursive(true)
|
||||||
info!("Cached img into: {}", &path);
|
.create(&download_fold)
|
||||||
return Some(path);
|
.unwrap();
|
||||||
} else {
|
|
||||||
error!("Failed to get feed image.");
|
let dlpath = download_into(&download_fold, "cover", &url);
|
||||||
error!("Error: {}", dlpath.unwrap_err());
|
if let Ok(path) = dlpath {
|
||||||
return None;
|
info!("Cached img into: {}", &path);
|
||||||
};
|
Some(path)
|
||||||
|
} else {
|
||||||
|
error!("Failed to get feed image.");
|
||||||
|
error!("Error: {}", dlpath.unwrap_err());
|
||||||
|
None
|
||||||
}
|
}
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[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
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
project(
|
project(
|
||||||
'hammond', 'rust',
|
'hammond', 'rust',
|
||||||
version: '0.1.0',
|
version: '0.1.1',
|
||||||
license: 'GPLv3',
|
license: 'GPLv3',
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -16,7 +16,8 @@ hammond_version_micro = version_array[2].to_int()
|
|||||||
hammond_prefix = get_option('prefix')
|
hammond_prefix = get_option('prefix')
|
||||||
hammond_bindir = join_paths(hammond_prefix, get_option('bindir'))
|
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)
|
cargo = find_program('cargo', required: false)
|
||||||
gresource = find_program('glib-compile-resources', required: false)
|
gresource = find_program('glib-compile-resources', required: false)
|
||||||
|
|||||||
Reference in New Issue
Block a user