mirror of
https://github.com/lifegpc/pixiv_downloader.git
synced 2026-07-08 01:32:41 +08:00
Update push
This commit is contained in:
@@ -76,6 +76,9 @@ pub struct EveryPushConfig {
|
||||
#[serde(default = "default_true")]
|
||||
/// Whether to filter author name
|
||||
pub filter_author: bool,
|
||||
#[serde(default = "default_true")]
|
||||
/// Whether to add artwork link
|
||||
pub add_link: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::error::PixivDownloaderError;
|
||||
use crate::gettext;
|
||||
use crate::pixiv_link::remove_track;
|
||||
use html_parser::Dom;
|
||||
@@ -31,7 +32,23 @@ impl DescriptionNode {
|
||||
self.attrs.insert(String::from(k), String::from(v))
|
||||
}
|
||||
|
||||
pub fn is_link(&self) -> bool {
|
||||
pub fn is_em(&self) -> bool {
|
||||
self.tag == "em" || self.tag == "i"
|
||||
}
|
||||
|
||||
pub fn is_headline(&self) -> bool {
|
||||
match self.tag.as_str() {
|
||||
"h1" => true,
|
||||
"h2" => true,
|
||||
"h3" => true,
|
||||
"h4" => true,
|
||||
"h5" => true,
|
||||
"h6" => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_link(&self, md_mode: bool) -> bool {
|
||||
if self.tag != "a" {
|
||||
return false;
|
||||
}
|
||||
@@ -39,12 +56,36 @@ impl DescriptionNode {
|
||||
return false;
|
||||
}
|
||||
let href = self.attrs.get("href").unwrap();
|
||||
if href.as_str() == self.data.as_str() {
|
||||
if !md_mode && href.as_str() == self.data.as_str() {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn is_paragraph(&self) -> bool {
|
||||
self.tag == "p" || self.tag == "paragraph"
|
||||
}
|
||||
|
||||
pub fn is_strong(&self) -> bool {
|
||||
self.tag == "strong" || self.tag == "b"
|
||||
}
|
||||
|
||||
pub fn to_em(&self) -> String {
|
||||
format!("*{}*", self.data.as_str())
|
||||
}
|
||||
|
||||
pub fn to_headline(&self) -> String {
|
||||
let mut s = String::from("#");
|
||||
let level = self.tag.chars().last().unwrap().to_digit(10).unwrap();
|
||||
for _ in 1..level {
|
||||
s += "#";
|
||||
}
|
||||
s += " ";
|
||||
s += self.data.as_str();
|
||||
s += "\n";
|
||||
s
|
||||
}
|
||||
|
||||
pub fn to_link(&self) -> String {
|
||||
format!(
|
||||
"[{}]({})",
|
||||
@@ -52,6 +93,16 @@ impl DescriptionNode {
|
||||
self.attrs.get("href").unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_paragraph(&self) -> String {
|
||||
let mut s = self.data.clone();
|
||||
s += "\n\n";
|
||||
s
|
||||
}
|
||||
|
||||
pub fn to_strong(&self) -> String {
|
||||
format!("**{}**", self.data.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
/// A simple HTML parser to parse description HTML
|
||||
@@ -60,13 +111,16 @@ pub struct DescriptionParser {
|
||||
nodes: Vec<DescriptionNode>,
|
||||
/// Output
|
||||
pub data: String,
|
||||
/// Markdown mode
|
||||
md_mode: bool,
|
||||
}
|
||||
|
||||
impl DescriptionParser {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(md_mode: bool) -> Self {
|
||||
Self {
|
||||
nodes: Vec::new(),
|
||||
data: String::from(""),
|
||||
md_mode,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,10 +139,11 @@ impl DescriptionParser {
|
||||
if tag == "script" || tag == "style" {
|
||||
return;
|
||||
} else if tag == "br" {
|
||||
let br = if self.md_mode { " \n" } else { "\n" };
|
||||
if self.nodes.len() == 0 {
|
||||
self.data += "\n";
|
||||
self.data += br;
|
||||
} else {
|
||||
self.nodes.last_mut().unwrap().data += "\n";
|
||||
self.nodes.last_mut().unwrap().data += br;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -109,56 +164,86 @@ impl DescriptionParser {
|
||||
self.iter(n);
|
||||
}
|
||||
let node = self.nodes.pop().unwrap();
|
||||
let s = if node.is_link() {
|
||||
let mut is_paragraph = false;
|
||||
let s = if node.is_link(self.md_mode) {
|
||||
node.to_link()
|
||||
} else if self.md_mode && node.is_headline() {
|
||||
node.to_headline()
|
||||
} else if self.md_mode && node.is_paragraph() {
|
||||
is_paragraph = true;
|
||||
node.to_paragraph()
|
||||
} else if self.md_mode && node.is_strong() {
|
||||
node.to_strong()
|
||||
} else if self.md_mode && node.is_em() {
|
||||
node.to_em()
|
||||
} else {
|
||||
node.data
|
||||
};
|
||||
if self.nodes.len() == 0 {
|
||||
while self.md_mode && is_paragraph && !self.data.ends_with("\n\n") {
|
||||
self.data += "\n";
|
||||
}
|
||||
self.data += s.as_str();
|
||||
} else {
|
||||
self.nodes.last_mut().unwrap().data += s.as_str();
|
||||
let n = self.nodes.last_mut().unwrap();
|
||||
while self.md_mode && is_paragraph && !n.data.ends_with("\n\n") {
|
||||
n.data += "\n";
|
||||
}
|
||||
n.data += s.as_str();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(&mut self, desc: &str) -> Result<(), ()> {
|
||||
let r = Dom::parse(desc);
|
||||
pub fn parse<S: AsRef<str> + ?Sized>(&mut self, desc: &S) -> Result<(), PixivDownloaderError> {
|
||||
let r = Dom::parse(desc.as_ref());
|
||||
if r.is_err() {
|
||||
println!("{} {}", gettext("Failed to parse HTML:"), r.unwrap_err());
|
||||
return Err(());
|
||||
return Err(format!("{} {}", gettext("Failed to parse HTML:"), r.unwrap_err()).into());
|
||||
}
|
||||
let dom = r.unwrap();
|
||||
if dom.errors.len() > 0 {
|
||||
println!("{}", gettext("Some errors occured during parsing:"));
|
||||
let mut s = String::from(gettext("Some errors occured during parsing:"));
|
||||
for i in dom.errors.iter() {
|
||||
println!("{}", i);
|
||||
s += "\n";
|
||||
s += i;
|
||||
}
|
||||
return Err(s.into());
|
||||
}
|
||||
for node in dom.children.iter() {
|
||||
self.iter(node)
|
||||
}
|
||||
if self.nodes.len() != 0 {
|
||||
println!(
|
||||
println!();
|
||||
return Err(format!(
|
||||
"{} {:?}",
|
||||
gettext("There are some nodes still in stack:"),
|
||||
self.nodes
|
||||
);
|
||||
return Err(());
|
||||
)
|
||||
.into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_description(desc: &str) -> Option<String> {
|
||||
let mut p = DescriptionParser::new();
|
||||
pub fn parse_description<S: AsRef<str> + ?Sized>(desc: &S) -> Option<String> {
|
||||
let mut p = DescriptionParser::new(false);
|
||||
match p.parse(desc) {
|
||||
Ok(_) => Some(p.data),
|
||||
Err(_) => None,
|
||||
Err(e) => {
|
||||
println!("{}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_description_to_md<S: AsRef<str> + ?Sized>(
|
||||
desc: &S,
|
||||
) -> Result<String, PixivDownloaderError> {
|
||||
let mut p = DescriptionParser::new(true);
|
||||
p.parse(desc)?;
|
||||
Ok(p.data)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_description() {
|
||||
assert_eq!(
|
||||
@@ -178,3 +263,26 @@ fn test_parse_description() {
|
||||
parse_description("<a href=\"/jump.php?https%3A%2F%2Fa.com\">https://a.com</a>")
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_convert_description_to_md() {
|
||||
assert_eq!(
|
||||
String::from("test \n[https://a.com](https://a.com)"),
|
||||
convert_description_to_md(
|
||||
"test<br/><a href=\"/jump.php?https%3A%2F%2Fa.com\">https://a.com</a>"
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
String::from("# He\n## Be\ntest"),
|
||||
convert_description_to_md("<h1>He</h1><h2>Be</h2>test").unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
String::from("D\n\nHe\n\nBe\n\ntest"),
|
||||
convert_description_to_md("D<p>He</p><p>Be</p>test").unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
String::from("# Head\nD\n\nHe\n\nBe\n\nt***e**s*t\n\n[Link](https://a.com)\n\n"),
|
||||
convert_description_to_md("<h1>Head</h1>D<p>He</p><p>Be</p>t<em><strong>e</strong>s</em>t<p><a href=\"/jump.php?https%3A%2F%2Fa.com\">Link</a></p>").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use super::super::super::preclude::*;
|
||||
use crate::db::push_task::{AuthorLocation, EveryPushConfig, PushConfig};
|
||||
use crate::error::PixivDownloaderError;
|
||||
use crate::get_helper;
|
||||
use crate::opt::author_name_filter::AuthorFiler;
|
||||
use crate::parser::description::DescriptionParser;
|
||||
use crate::pixivapp::illust::PixivAppIllust;
|
||||
use crate::push::every_push::{EveryPushClient, EveryPushTextType};
|
||||
use crate::{get_helper, gettext};
|
||||
use json::JsonValue;
|
||||
|
||||
struct RunContext<'a> {
|
||||
@@ -29,6 +30,25 @@ impl<'a> RunContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> Option<u64> {
|
||||
match self.illust {
|
||||
Some(i) => match i.id() {
|
||||
Some(i) => return Some(i),
|
||||
None => {}
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
match self.data {
|
||||
Some(d) => d["illustId"]
|
||||
.as_u64()
|
||||
.or_else(|| match d["illustId"].as_str() {
|
||||
Some(s) => s.parse().ok(),
|
||||
None => None,
|
||||
}),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn _author(&self) -> Option<&str> {
|
||||
match self.illust {
|
||||
Some(i) => match i.user_name() {
|
||||
@@ -58,6 +78,23 @@ impl<'a> RunContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn user_id(&self) -> Option<u64> {
|
||||
match self.illust {
|
||||
Some(i) => match i.user_id() {
|
||||
Some(u) => return Some(u),
|
||||
None => {}
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
match self.data {
|
||||
Some(d) => d["userId"].as_u64().or_else(|| match d["userId"].as_str() {
|
||||
Some(s) => s.parse().ok(),
|
||||
None => None,
|
||||
}),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether to filter author name
|
||||
pub fn filter_author(&self) -> bool {
|
||||
match self.cfg {
|
||||
@@ -104,11 +141,137 @@ impl<'a> RunContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_author<S: AsRef<str> + ?Sized>(&self, text: &mut String, author: &S) {
|
||||
text.push_str(gettext("by "));
|
||||
let author = author.as_ref();
|
||||
if let Some(uid) = self.user_id() {
|
||||
text.push_str(&format!(
|
||||
"[{}](https://www.pixiv.net/users/{})",
|
||||
author, uid
|
||||
));
|
||||
} else {
|
||||
text.push_str(author);
|
||||
}
|
||||
text.push_str(" \n");
|
||||
}
|
||||
|
||||
pub fn desc(&self) -> Option<&str> {
|
||||
match self.illust {
|
||||
Some(i) => {
|
||||
if !i.caption_is_empty() {
|
||||
return i.caption();
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
match self.data {
|
||||
Some(d) => d["description"]
|
||||
.as_str()
|
||||
.or_else(|| d["illustComment"].as_str()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_every_push(&self, cfg: &EveryPushConfig) -> Result<(), PixivDownloaderError> {
|
||||
let client = EveryPushClient::new(&cfg.push_server);
|
||||
match cfg.typ {
|
||||
EveryPushTextType::Text => {}
|
||||
EveryPushTextType::Markdown => {}
|
||||
EveryPushTextType::Text => {
|
||||
let mut title = self.title().map(|s| s.to_owned());
|
||||
let author = self.author();
|
||||
if cfg.author_locations.contains(&AuthorLocation::Title) {
|
||||
if let Some(t) = &title {
|
||||
if let Some(a) = &author {
|
||||
title = Some(format!("{} - {}", t, a));
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut text = String::new();
|
||||
if cfg.author_locations.contains(&AuthorLocation::Top) {
|
||||
if let Some(a) = &author {
|
||||
text.push_str(gettext("by "));
|
||||
text.push_str(a);
|
||||
text.push_str("\n");
|
||||
}
|
||||
}
|
||||
if cfg.add_link {
|
||||
if let Some(id) = self.id() {
|
||||
text.push_str(&format!("https://www.pixiv.net/artworks/{}\n", id));
|
||||
}
|
||||
}
|
||||
if let Some(desc) = self.desc() {
|
||||
let mut p = DescriptionParser::new(false);
|
||||
p.parse(desc)?;
|
||||
text.push_str(&p.data);
|
||||
text.push_str("\n");
|
||||
}
|
||||
if cfg.author_locations.contains(&AuthorLocation::Bottom) {
|
||||
if let Some(a) = &author {
|
||||
text.push_str(gettext("by "));
|
||||
text.push_str(a);
|
||||
text.push_str("\n");
|
||||
}
|
||||
}
|
||||
client
|
||||
.push_message(
|
||||
&cfg.push_token,
|
||||
&text,
|
||||
title.as_ref(),
|
||||
cfg.topic_id.as_ref(),
|
||||
Some(EveryPushTextType::Text),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
EveryPushTextType::Markdown => {
|
||||
let mut title = self.title().map(|s| s.to_owned());
|
||||
let author = self.author();
|
||||
if cfg.author_locations.contains(&AuthorLocation::Title) {
|
||||
if let Some(t) = &title {
|
||||
if let Some(a) = &author {
|
||||
title = Some(format!("{} - {}", t, a));
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut text = String::new();
|
||||
let len = self.len().unwrap_or(1);
|
||||
for i in 0..len {
|
||||
if let Some(url) = self.get_image_url(i).await? {
|
||||
text.push_str(&format!("", url));
|
||||
}
|
||||
}
|
||||
if cfg.author_locations.contains(&AuthorLocation::Top) {
|
||||
if let Some(a) = &author {
|
||||
self.add_author(&mut text, a);
|
||||
}
|
||||
}
|
||||
if cfg.add_link {
|
||||
if let Some(id) = self.id() {
|
||||
let link = format!("https://www.pixiv.net/artworks/{}", id);
|
||||
text.push_str(&format!("[{}]({}) \n", link, link));
|
||||
}
|
||||
}
|
||||
if let Some(desc) = self.desc() {
|
||||
let mut p = DescriptionParser::new(true);
|
||||
p.parse(desc)?;
|
||||
text.push_str(&p.data);
|
||||
if !p.data.ends_with("\n\n") {
|
||||
text.push_str("\n\n");
|
||||
}
|
||||
}
|
||||
if cfg.author_locations.contains(&AuthorLocation::Bottom) {
|
||||
if let Some(a) = &author {
|
||||
self.add_author(&mut text, a);
|
||||
}
|
||||
}
|
||||
client
|
||||
.push_message(
|
||||
&cfg.push_token,
|
||||
&text,
|
||||
title.as_ref(),
|
||||
cfg.topic_id.as_ref(),
|
||||
Some(EveryPushTextType::Markdown),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
EveryPushTextType::Image => {
|
||||
let mut title = self.title().map(|s| s.to_owned());
|
||||
if cfg.author_locations.contains(&AuthorLocation::Title) {
|
||||
|
||||
Reference in New Issue
Block a user