comparison src/library/library.cc @ 382:0265e125f680

filesystem: implement filesystem watcher I also ported the library code to use it as well. Once we implement proper directory watching on Windows (and maybe others) this will be fairly useful :)
author Paper <paper@tflc.us>
date Thu, 06 Nov 2025 03:16:55 -0500
parents 47c9f8502269
children 27c462bc7815
comparison
equal deleted inserted replaced
381:5beae59cf042 382:0265e125f680
11 11
12 #include <iostream> 12 #include <iostream>
13 13
14 namespace Library { 14 namespace Library {
15 15
16 Database::Database()
17 {
18 /* Do this immediately :) */
19 UpdateWatchers();
20 }
21
22 void Database::UpdateWatchers()
23 {
24 /* TODO also need to remove unused watchers */
25 for (const auto &p : session.config.library.paths) {
26 if (watchers_.count(p))
27 continue;
28
29 watchers_[p].reset(Filesystem::GetRecursiveFilesystemWatcher(reinterpret_cast<void *>(this), p, Database::StaticEventHandler));
30 }
31 }
32
33 bool Database::GetPathAnimeAndEpisode(const std::string &basename, int *aid, int *ep)
34 {
35 anitomy::Anitomy anitomy;
36 anitomy.Parse(basename);
37
38 const auto &elements = anitomy.elements();
39
40 const std::string title = Strings::ToUtf8String(elements.get(anitomy::kElementAnimeTitle));
41
42 const int id = Anime::db.LookupAnimeTitle(title);
43 if (id <= 0 || (find_id_ && find_id_.value() != id))
44 return false;
45
46 const int episode =
47 Strings::ToInt<int>(Strings::ToUtf8String(elements.get(anitomy::kElementEpisodeNumber)));
48
49 *aid = id;
50 *ep = episode;
51 return true;
52 }
53
54 void Database::EventHandler(const std::filesystem::path &path, Filesystem::IWatcher::Event event)
55 {
56 std::string bname = path.filename().u8string();
57 int aid, ep;
58
59 std::cout << path << '\n';
60
61 if (!GetPathAnimeAndEpisode(bname, &aid, &ep))
62 return;
63
64 switch (event) {
65 case Filesystem::IWatcher::Created:
66 items[aid][ep] = path;
67 break;
68 case Filesystem::IWatcher::Deleted:
69 /* kill it off */
70 items[aid].erase(ep);
71 if (items[aid].empty())
72 items.erase(aid);
73 break;
74 }
75 }
76
77 void Database::StaticEventHandler(void *opaque, const std::filesystem::path &path, Filesystem::IWatcher::Event event)
78 {
79 /* Forward to class function */
80 reinterpret_cast<Database *>(opaque)->EventHandler(path, event);
81 }
82
16 std::optional<std::filesystem::path> Database::GetAnimeFolder(int id) 83 std::optional<std::filesystem::path> Database::GetAnimeFolder(int id)
17 { 84 {
18 // this function sucks, but it's the most I can really do for now. 85 // this function sucks, but it's the most I can really do for now.
19 // 86 //
20 // in the future the Refresh() function should look for directories 87 // in the future the Refresh() function should look for directories
25 92
26 for (const auto &[anime_id, episodes] : items) { 93 for (const auto &[anime_id, episodes] : items) {
27 if (id != anime_id) 94 if (id != anime_id)
28 continue; 95 continue;
29 96
30 for (const auto &[episode, path] : episodes) { 97 for (const auto &[episode, path] : episodes)
31 return path.parent_path(); 98 return path.parent_path();
32 break;
33 }
34 99
35 break; 100 break;
36 } 101 }
37 102
38 return std::nullopt; 103 return std::nullopt;
39 } 104 }
40 105
106 /* TODO shove this into a separate thread; currently it blocks */
41 void Database::Refresh(std::optional<int> find_id) 107 void Database::Refresh(std::optional<int> find_id)
42 { 108 {
43 items.clear(); 109 find_id_ = find_id;
44 110
45 for (const auto &folder : session.config.library.paths) { 111 UpdateWatchers();
46 for (const auto &entry : std::filesystem::recursive_directory_iterator(folder)) {
47 const std::filesystem::path path = entry.path();
48 if (!std::filesystem::is_regular_file(path))
49 continue;
50 112
51 const std::string basename = path.filename().u8string(); 113 for (const auto &w : watchers_)
52 114 w.second->Process();
53 anitomy::Anitomy anitomy;
54 anitomy.Parse(basename);
55
56 const auto &elements = anitomy.elements();
57
58 const std::string title = Strings::ToUtf8String(elements.get(anitomy::kElementAnimeTitle));
59
60 const int id = Anime::db.LookupAnimeTitle(title);
61 if (id <= 0 || (find_id && find_id.value() != id))
62 continue;
63
64 const int episode =
65 Strings::ToInt<int>(Strings::ToUtf8String(elements.get(anitomy::kElementEpisodeNumber)));
66
67 // we have an ID now!
68 items[id][episode] = path;
69 }
70 }
71 } 115 }
72 116
73 void Database::Refresh() 117 void Database::Refresh()
74 { 118 {
75 Refresh(std::nullopt); 119 Refresh(std::nullopt);