File size: 4,790 Bytes
3d7d9b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
use tauri::{AppHandle, Manager};
use reqwest::Client;
use std::time::Duration;
use adblock::{Engine, FilterSet};
use adblock::lists::ParseOptions;
use super::engine::AdBlockState;

const FILTER_URLS: &[(&str, &str)] = &[
    ("easylist", "https://easylist.to/easylist/easylist.txt"),
    ("easyprivacy", "https://easylist.to/easylist/easyprivacy.txt"),
    ("fanboy-annoyances", "https://secure.fanboy.co.nz/fanboy-annoyance.txt"),
    ("ublock-filters", "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters.txt"),
    ("ublock-privacy", "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/privacy.txt"),
    ("ublock-unbreak", "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/unbreak.txt"),
    ("ublock-quick-fixes", "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/quick-fixes.txt"),
];

const SCRIPTLETS_URL: &str = "https://raw.githubusercontent.com/gorhill/uBlock/master/assets/resources/scriptlets.js";
const FALLBACK_SCRIPTLETS: &str = include_str!("../../resources/scriptlets/muse_ubo_compatible_scriptlets.js");
const UPDATE_INTERVAL: Duration = Duration::from_secs(24 * 60 * 60);

pub fn spawn_updater(app: AppHandle) {
    tauri::async_runtime::spawn(async move {
        tokio::time::sleep(Duration::from_secs(30)).await;
        loop {
            if let Err(e) = update_now(&app).await {
                eprintln!("[muse-shield] Filter list update failed: {e}");
            }
            tokio::time::sleep(UPDATE_INTERVAL).await;
        }
    });
}

fn parse_scriptlet_file(path: &std::path::Path) -> Vec<adblock::resources::Resource> {
    #[allow(deprecated)]
    adblock::resources::resource_assembler::assemble_scriptlet_resources(path)
}

pub async fn update_now(app: &AppHandle) -> Result<(), String> {
    let client = Client::builder()
        .timeout(Duration::from_secs(45))
        .user_agent("Muse/0.2 (adblock-updater)")
        .build()
        .map_err(|e| e.to_string())?;

    let mut filter_set = FilterSet::new(false);
    let mut total_rules = 0usize;

    for (name, url) in FILTER_URLS {
        match client.get(*url).send().await {
            Ok(resp) => {
                if let Ok(text) = resp.text().await {
                    let _meta = filter_set.add_filter_list(&text, ParseOptions::default());
                    let rule_count = text.lines().filter(|l| !l.trim().is_empty() && !l.starts_with('!')).count();
                    total_rules += rule_count;
                    println!("[muse-shield] Updated {name}: {rule_count} rules");
                }
            }
            Err(e) => eprintln!("[muse-shield] Failed to fetch {name}: {e}"),
        }
    }

    if total_rules == 0 { return Ok(()); }

    let mut new_engine = Engine::from_filter_set(filter_set, true);
    let cache_dir = app.path().app_data_dir().map_err(|e| e.to_string())?;
    std::fs::create_dir_all(&cache_dir).map_err(|e| e.to_string())?;

    // 1) Try upstream uBO resource source.
    let mut resources = Vec::new();
    match client.get(SCRIPTLETS_URL).send().await {
        Ok(resp) => {
            if let Ok(scriptlets_js) = resp.text().await {
                let scriptlets_path = cache_dir.join("ubo-scriptlets.js");
                std::fs::write(&scriptlets_path, &scriptlets_js).map_err(|e| e.to_string())?;
                resources = parse_scriptlet_file(&scriptlets_path);
                println!("[muse-shield] Upstream uBO scriptlets parsed {} resources", resources.len());
            }
        }
        Err(e) => eprintln!("[muse-shield] Failed to fetch upstream uBO scriptlets: {e}"),
    }

    // 2) Deterministic fallback bundle in adblock-rust legacy resource format.
    if resources.is_empty() {
        let fallback_path = cache_dir.join("muse-fallback-scriptlets.js");
        std::fs::write(&fallback_path, FALLBACK_SCRIPTLETS).map_err(|e| e.to_string())?;
        resources = parse_scriptlet_file(&fallback_path);
        println!("[muse-shield] Fallback scriptlets parsed {} resources", resources.len());
    }

    if !resources.is_empty() {
        let resource_count = resources.len();
        new_engine.use_resources(resources);
        println!("[muse-shield] Loaded {resource_count} scriptlet resources via adblock-rust resource_assembler");
    } else {
        eprintln!("[muse-shield] WARNING: scriptlet resource parsing returned 0 even for fallback bundle. Built-in early video scriptlets remain active.");
    }

    let state = app.state::<AdBlockState>();
    state.reload_engine(new_engine, total_rules);
    println!("[muse-shield] Engine reloaded with {total_rules} rules + scriptlet resources");
    Ok(())
}