:- module(episode_data, [retract_episode/1, lookup_episode/3, lookup_episode_local/3, lookup_episode_remote/3]). :- use_module(library(dcg/basics)). :- use_module(library(http/http_open)). :- use_module(library(sgml)). :- use_module(library(xpath)). :- use_module(library(persistency)). :- use_module(atom_dcg). :- persistent episode_name_data(episode:integer, name:atom, data:list). attach :- absolute_file_name('episode_data.db', F, [access(write)]), db_attach(F, []). detach :- db_detach. retract_episode(Ep) :- ( episode_name_data(Ep, _, _) -> retractall_episode_name_data(Ep, _, _) ; true ). padding(Ep) --> { Ep < 10 }, "00". padding(Ep) --> { Ep >= 10, Ep < 100 }, "0". padding(Ep) --> { Ep >= 100 }. episode_number(Ep) --> padding(Ep), integer(Ep). lookup_episode(Ep, Name, Data) :- lookup_episode_local(Ep, Name, Data), !. lookup_episode(Ep, Name, Data) :- lookup_episode_remote(Ep, Name, Data). lookup_episode_local(Ep, Name, Data) :- episode_name_data(Ep, Name, Data). lookup_episode_remote(Ep, Name, Data) :- catch(http_load_html( 'https://www.detectiveconanworld.com/wiki/Next_Conan%27s_Hint', D), _, fail), xpath(D, //tr, D1), xpath(D1, td(index(1),text), T), atom_phrase(episode_number(Ep), T), xpath(D1, td(index(2),text), Name), xpath(D1, td(index(3),text), Hint), Data = ['Hint'(Hint)], assert_episode_name_data(Ep, Name, Data). http_load_html(URL, DOM) :- setup_call_cleanup(http_open(URL, In, [ timeout(60) ]), ( dtd(html, DTD), load_structure(stream(In), DOM, [ dtd(DTD), dialect(sgml), shorttag(false), max_errors(-1), syntax_errors(quiet) ]) ), close(In)).