:- 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. % Interface. 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) :- update, !, episode_name_data(Ep, Name, Data). retract_episode(Ep) :- ( episode_name_data(Ep, _, _) -> retractall_episode_name_data(Ep, _, _) ; true ). % Parsing. padding(Ep) --> { Ep < 10 }, "00". padding(Ep) --> { Ep >= 10, Ep < 100 }, "0". padding(Ep) --> { Ep >= 100 }. episode_number(Ep) --> padding(Ep), integer(Ep). % Database updating. update :- remote(R0), !, findall(Ep-Name-Data, (xpath(R0, //tr, R), remote_episode(R, Ep), remote_episode_name_data(R, Ep, Name, Data)), ENDs), !, maplist(update, ENDs). update(Ep-Name-Data) :- episode_name_data(Ep, Name, Data), !. update(Ep-Name-Data) :- assert_episode_name_data(Ep, Name, Data). % Remote retrieval. remote(R) :- catch(http_load_html( 'https://www.detectiveconanworld.com/wiki/Next_Conan%27s_Hint', R), _, fail). remote_episode(R, Ep) :- xpath(R, td(index(1),text), T), atom_number(T, Ep). remote_episode_name_data(R, Ep, Name, Data) :- xpath(R, td(index(1),text), T), atom_phrase(episode_number(Ep), T), xpath(R, td(index(2),text), Name), xpath(R, td(index(3),text), Hint), Data = ['Hint'(Hint)]. 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)).