Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/deven96/mythra
Browse files Browse the repository at this point in the history
  • Loading branch information
wizzywit committed Apr 16, 2021
2 parents 0a73931 + e17b774 commit f6aadb0
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 98 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/gcp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
username: ${{secrets.USERNAME}}
script: |
cd /opt
tag=`curl "https://api.github.com/repos/deven96/mythra/releases/latest" | jq '.tag_name' | tr -d \"`
tag=`curl -L "https://api.github.com/repos/deven96/mythra/releases/latest" | jq '.tag_name' | tr -d \"`
sudo curl -L https://github.com/deven96/mythra/releases/download/$tag/mythra-ubuntu-18.04-$tag > mythra-replace
sudo chmod 755 ./mythra-replace
sudo cp -f mythra-replace mythra
Expand Down
7 changes: 3 additions & 4 deletions .github/workflows/releases.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@ jobs:
- uses: actions/checkout@v2
- run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- run: |
tag=${{env.RELEASE_VERSION}}
if [ "$CURRENT_OS" == "ubuntu-18.04" ]; then
sudo apt-get install libncursesw5-dev
alias sedi="sed -i"
sed -i 's/version\:.*/version\: "${tag:1}"/' src/cli.yaml
elif [ "$CURRENT_OS" == "macos-latest" ]; then
brew install ncurses gsed
alias sedi="gsed -i"
gsed -i 's/version\:.*/version\: "${tag:1}"/' src/cli.yaml
fi
tag=${{env.RELEASE_VERSION}}
sedi 's/version\:.*/version\: "${tag:1}"/' src/cli.yaml
env:
CURRENT_OS: ${{matrix.os}}
- run: cargo build --release --locked && chmod +x target/release/mythra
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mythra"
version = "0.1.9"
version = "2.1.3"
authors = ["Diretnan Domnan <[email protected]>"]
edition = "2018"

Expand Down
17 changes: 11 additions & 6 deletions src/api.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use crate::engines::get_engine;
use crate::types::{
MusicRequest
};
use crate::types::MusicRequest;

use actix_web::{http::StatusCode, web, App, HttpResponse, HttpServer};
use actix_cors::Cors;
use actix_web::middleware::Logger;
use actix_web::{http::StatusCode, web, App, HttpResponse, HttpServer};
use log::{debug, error};

async fn search(web::Query(info): web::Query<MusicRequest>) -> HttpResponse {
Expand All @@ -21,7 +19,7 @@ async fn search(web::Query(info): web::Query<MusicRequest>) -> HttpResponse {
Ok(actual) => {
let res = actual.search(query).await.ok();
HttpResponse::Ok().json(res.unwrap())
},
}
Err(_) => {
error!("Error {} is unsupported", engine_match);
HttpResponse::new(StatusCode::NOT_FOUND)
Expand Down Expand Up @@ -53,7 +51,14 @@ mod tests {

#[actix_rt::test]
async fn test_api_with_fake_engine_returns_not_found() {
let query: web::Query<MusicRequest> = web::Query::from_query("engine=fake&query=real").unwrap();
let query: web::Query<MusicRequest> =
web::Query::from_query("engine=fake&query=real").unwrap();
assert_eq!(search(query).await.status(), StatusCode::NOT_FOUND);
}
//#[actix_rt::test]
//async fn test_api_with_myfreemp3() {
// let query: web::Query<MusicRequest> =
// web::Query::from_query("engine=myfreemp3&query=real").unwrap();
// search(query).await;
//}
}
2 changes: 1 addition & 1 deletion src/cli.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: mythra
version: "2.1.2"
version: "2.1.3"
author: Domnan Diretnan <[email protected]>
about: Search and Download music easily without ads and redirects
args:
Expand Down
15 changes: 8 additions & 7 deletions src/engines/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::types::{
use crate::utils::render_select_music;
use log::error;

pub fn get_engine(engine: &str) -> Result<Box<dyn EngineTraits>, MythraError>{
pub fn get_engine(engine: &str) -> Result<Box<dyn EngineTraits>, MythraError> {
match engine {
"mp3s" => {
Ok(Box::new(mp3s::MP3S{}))
Expand All @@ -32,11 +32,9 @@ pub async fn cli(engine_name: &str, query: &str) {
let engine = get_engine(engine_name);
match engine {
Ok(actual) => {
let results = actual.search(
String::from(query)
).await.unwrap();
let results = actual.search(String::from(query)).await.unwrap();
render_select_music(results, title);
},
}
Err(_) => {
error!("Error {} is unsupported", engine_name);
}
Expand All @@ -46,12 +44,15 @@ pub async fn cli(engine_name: &str, query: &str) {
#[cfg(test)]
mod tests {
use super::*;

#[actix_rt::test]
async fn test_cli_with_fake_engine_returns_not_found() {
let mut mock = MockEngineTraits::new();
mock.expect_search().times(0);
cli("fake", "query").await;
}

#[actix_rt::test]
async fn test_cli_with_myfreemp3_engine() {
let engine: &str = "myfreemp3";
get_engine(engine).unwrap();
}
}
111 changes: 68 additions & 43 deletions src/engines/myfreemp3.rs
Original file line number Diff line number Diff line change
@@ -1,85 +1,110 @@
use async_trait::async_trait;
use crate::types::{Engine, EngineTraits, Music, MythraResult};
use crate::utils::{
cached_reqwest,
get_element_attribute
};
use crate::utils::cached_reqwest;
use async_trait::async_trait;
use serde_json::Map;

use indicatif::ProgressBar;
use log::debug;
use scraper::{Html, Selector};
use regex::Regex;
use serde_json::Result;
use serde_json::Value;

pub struct MyFreeMP3;
pub static CONFIG: Engine = Engine {
name: "myfreemp3",
base_url: "http://mp3clan.top/",
search_url: "http://mp3clan.top/mp3/",
base_url: "http://myfreemp3music.com/",
search_url: "https://myfreemp3music.com/api/search.php",
};

#[async_trait]
impl EngineTraits for MyFreeMP3 {
async fn search(&self, _query: String) -> MythraResult<Vec<Music>> {
let mut _append = str::replace(&_query[..], " ", "_");
_append = format!("{}{}.html", CONFIG.search_url,_append);
let mut _query= String::from(&_append[..]);

let res = cached_reqwest::get(&_query, false).await;
let selector = Selector::parse("#mp3list").unwrap();
let size = Html::parse_document(res.as_str())
.select(&selector).count();
let mut _query = String::from(&_query[..]);
let bar = ProgressBar::new(100);
let other_elems: Vec<String> = Html::parse_document(res.as_str())
.select(&selector)
.by_ref().map(|x| x.html()).collect();
let full_url: String = CONFIG.search_url.to_owned();
let form_data = [("q", _query.as_str())];
let res = cached_reqwest::post(&full_url, &form_data)
.await
.ok()
.unwrap();
let v: Value = self.format_response(&res).ok().unwrap().clone();
//println!("Retrieving song with data -> {:?}", v);
let mut vec: Vec<Music> = Vec::new();
let elems = v["response"].as_array().unwrap();
let other_elems = elems.clone();
let size = other_elems.len();
for el in 0..size {
let element = &other_elems[el];
let single_music = self.parse_single_music(el, element.to_string()).await;
let single_music = self.parse_single_music(el, &element.as_object()).await;
match single_music {
Some(music) => vec.push(music),
_ => (),
}
// increment progress bar
// increment progress bar
let inc: u64 = (100 / size) as u64;
bar.inc(inc);
};
}
bar.finish();
Ok(vec)
}

}

impl MyFreeMP3 {
pub async fn parse_single_music(&self, ind: usize, element: String) -> Option<Music> {
let title_full = get_element_attribute(&element, ".unselectable", "text");
let bitrate = get_element_attribute(&element, ".mp3list-bitrate", "text");
let title_rg = Regex::new(r"(?P<artiste>.*) - (?P<title>.*)")
.unwrap();
let bitrate_rg = Regex::new(r"Check (?P<duration>.*) min")
.unwrap();
let title = String::from(title_rg.captures(&title_full[..])
pub fn format_response(&self, data: &String) -> Result<Value> {
let new_data = data.as_str();
let new_data = &new_data.replace("\"apple\",", "");
//println!("Data response: {:?}", new_data);
let re = Regex::new(r"^(?P<last>[(])(?P<content>.*)(?P<first>[)][;]$)").unwrap();
let result = re.replace(new_data.as_str(), "$content");
debug!("{:?}", result);
//println!("Data response: {:?}", result);
let d: Value = serde_json::from_str(result.into_owned().as_str()).unwrap();
// Do things just like with any other Rust data structure.
Ok(d)
}

pub async fn parse_single_music(
&self,
ind: usize,
element: &Option<&Map<String, Value>>,
) -> Option<Music> {
let title = element
.unwrap()
.name("title")
.get("title")
.unwrap()
.as_str()
);
let artiste = String::from(title_rg.captures(&title_full[..])
.unwrap()
.name("artiste")
.to_owned();
let artiste = element
.unwrap()
.get("artist")
.unwrap()
.as_str()
);
let duration = String::from(bitrate_rg.captures(&bitrate[..])
.unwrap()
.name("duration")
.to_owned();
let data = r#" { "album": {"title": "-"} } "#;
let def_album: Value = serde_json::from_str(data).unwrap();
let album = element
.unwrap()
.get("album")
.unwrap_or(&def_album["album"])
.as_object();
let collection = album
.unwrap()
.get("title")
.unwrap()
.as_str()
);
let selector = Selector::parse(".mp3list-download").unwrap();
let size = Html::parse_fragment(element.as_str())
.select(&selector).next().unwrap().inner_html();
let download_link = get_element_attribute(&size, "a", "href");
.unwrap()
.to_owned();
let duration_i64 = element.unwrap().get("duration").unwrap().as_i64().unwrap();
let duration = (format!("{}:{}", duration_i64 / 60, duration_i64 % 60)).into();
let download_link = element
.unwrap()
.get("url")
.unwrap()
.as_str()
.unwrap()
.to_owned();
debug!("Retrieving song with title -> {}", title);

Some(Music {
Expand Down
Loading

0 comments on commit f6aadb0

Please sign in to comment.