Created
November 9, 2021 17:59
-
-
Save ITotalJustice/5c1ad163b483f30c261a5ad5a04ee30f to your computer and use it in GitHub Desktop.
fs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "crunchy.hpp" | |
#include "crapi/crapi.hpp" | |
#include "../utils/logger.hpp" | |
#include <algorithm> | |
#include <ranges> | |
namespace fs { | |
static auto CheckCrDataError(const auto& a) noexcept { | |
if (a.error == true) { | |
LOG("[ERROR] %s\n", a.code.c_str()); | |
return false; | |
} | |
return true; | |
} | |
Crunchy::Crunchy() { | |
this->api = std::make_unique<cr::Crapi>(); | |
if (!this->api->StartSession()) { | |
this->good = false; | |
LOG("[CRUNCHY] failed to start session\n"); | |
} else { | |
this->good = true; | |
} | |
this->SetupFullpath(); | |
} | |
Crunchy::~Crunchy() { | |
} | |
auto Crunchy::List(std::stop_token stk, FilterFunc filter_func) -> Result { | |
try { | |
switch (this->page) { | |
case Pages::HOME: | |
return this->GetHomeOptions(stk); | |
case Pages::SERIES_LIST: | |
return this->GetSeriesList(stk); | |
case Pages::SEARCH_LIST: | |
return this->GetSearchSeriesList(stk); | |
case Pages::COLLECTIONS: | |
return this->GetCollections(stk); | |
case Pages::EPISODES: | |
return this->GetEpisodes(stk); | |
} | |
} catch (const std::exception& e) { | |
LOG("[EXCEPTION] %s\n", e.what()); | |
return e.what(); | |
} | |
__builtin_unreachable(); | |
} | |
auto Crunchy::Open(std::stop_token stk, const Entry& entry, OpenType type, FilterFunc filter_func) -> Result { | |
auto private_data = dynamic_cast<CrunchyPrivateData*>(entry.private_data.get()); | |
switch (this->page) { | |
case Pages::HOME: | |
this->series_path = private_data->id; | |
this->search_term.reset(); | |
this->page = Pages::SERIES_LIST; | |
break; | |
case Pages::SERIES_LIST: case Pages::SEARCH_LIST: | |
this->series_id = private_data->id; | |
this->page = Pages::COLLECTIONS; | |
break; | |
case Pages::COLLECTIONS: | |
this->collection_id = private_data->id; | |
this->page = Pages::EPISODES; | |
break; | |
case Pages::EPISODES: | |
// impossible! | |
throw; | |
return {"oof"}; | |
} | |
this->SetupFullpath(); | |
return this->List(stk, filter_func); | |
} | |
auto Crunchy::WalkUp(std::stop_token stk, FilterFunc filter_func) -> Result { | |
switch (this->page) { | |
case Pages::HOME: | |
break; | |
// return "Already Root!"; | |
case Pages::SERIES_LIST: | |
case Pages::SEARCH_LIST: | |
this->search_term.reset(); | |
this->page = Pages::HOME; | |
break; | |
case Pages::COLLECTIONS: | |
if (this->search_term.has_value()) { | |
this->page = Pages::SEARCH_LIST; | |
} else { | |
this->page = Pages::SERIES_LIST; | |
} | |
break; | |
case Pages::EPISODES: | |
this->page = Pages::COLLECTIONS; | |
break; | |
} | |
this->SetupFullpath(); | |
return this->List(stk, filter_func); | |
} | |
// todo: rewrite page thing to be more like funime (for now). | |
// once that's done, add search | |
auto Crunchy::Search(std::stop_token stk, const std::string& path, FilterFunc filter_func) -> Result { | |
this->search_term = path; | |
this->page = Pages::SEARCH_LIST; | |
return this->List(stk, filter_func); | |
} | |
auto Crunchy::GetMediaUri(std::stop_token stk, const Entry& entry) const -> std::optional<std::string> { | |
try { | |
auto private_data = dynamic_cast<CrunchyPrivateData*>(entry.private_data.get()); | |
auto it = std::ranges::find(this->media_data, private_data->id, &cr::MediaData::episode_number); | |
return cr::Crapi::GetStreamUrl(*it); | |
} catch (const std::exception& e) { | |
LOG("[EXCEPTION-GetMediaUri()] %s\n", e.what()); | |
} | |
return {}; | |
} | |
auto Crunchy::GetHomeOptions(std::stop_token token) const -> Result { | |
return std::vector{ | |
Entry{"Recently Updated", EntryType::FOLDER, std::make_shared<CrunchyPrivateData>(cr::FILTER_UPDATED)}, | |
Entry{"Popular Anime", EntryType::FOLDER, std::make_shared<CrunchyPrivateData>(cr::FILTER_POPULAR)}, | |
Entry{"Featured", EntryType::FOLDER, std::make_shared<CrunchyPrivateData>(cr::FILTER_FEATURED)}, | |
Entry{"Newest", EntryType::FOLDER, std::make_shared<CrunchyPrivateData>(cr::FILTER_NEWEST)}, | |
Entry{"Alpha", EntryType::FOLDER, std::make_shared<CrunchyPrivateData>(cr::FILTER_ALPHA)}, | |
Entry{"Simulcast", EntryType::FOLDER, std::make_shared<CrunchyPrivateData>(cr::FILTER_SIMULCAST)}, | |
}; | |
} | |
auto Crunchy::GetSearchSeriesList(std::stop_token stk) -> Result { | |
const auto series = this->api->SearchAsync(stk, *this->search_term).value(); | |
if (!CheckCrDataError(series)) { | |
return series.code; | |
} | |
Entries entries; | |
this->series_data = std::move(series.data); | |
LOG("\nListing series that we got\n"); | |
for (auto&p : this->series_data) { | |
entries.emplace_back(p.name, | |
EntryType::FOLDER, | |
std::make_shared<CrunchyPrivateData>(p.series_id) | |
); | |
LOG("SeriesID: %s Name: %s\n", p.series_id.c_str(), p.name.c_str()); | |
} | |
return entries; | |
} | |
auto Crunchy::GetSeriesList(std::stop_token stk) -> Result { | |
const auto series = this->api->ListSeriesAsync(stk, | |
this->series_path, 0U, 25).value(); | |
if (!CheckCrDataError(series)) { | |
return series.code; | |
} | |
Entries entries; | |
this->series_data = std::move(series.data); | |
LOG("\nListing series that we got\n"); | |
for (auto&p : this->series_data) { | |
entries.emplace_back(p.name, | |
EntryType::FOLDER, | |
std::make_shared<CrunchyPrivateData>(p.series_id) | |
); | |
LOG("SeriesID: %s Name: %s\n", p.series_id.c_str(), p.name.c_str()); | |
} | |
return entries; | |
} | |
auto Crunchy::GetCollections(std::stop_token stk) -> Result { | |
const auto collection = this->api->ListCollectionsAsync(stk, | |
this->series_id, 0U, -1).value(); | |
if (!CheckCrDataError(collection)) { | |
return collection.code; | |
} | |
Entries entries; | |
this->collection_data = std::move(collection.data); | |
LOG("\nListing collections that we got\n"); | |
for (auto&p : this->collection_data) { | |
entries.emplace_back("Season " + p.season + ": " + p.name, | |
EntryType::FOLDER, | |
std::make_shared<CrunchyPrivateData>(p.collection_id) | |
); | |
LOG("SeriesID: %s Season: %s Name: %s\n", p.series_id.c_str(), p.season.c_str(), p.name.c_str()); | |
} | |
return entries; | |
} | |
auto Crunchy::GetEpisodes(std::stop_token stk) -> Result { | |
const auto media = this->api->ListMediaFromCollectionIDAsync(stk, | |
this->collection_id, 0U, -1).value(); | |
if (!CheckCrDataError(media)) { | |
return media.code; | |
} | |
Entries entries; | |
this->media_data = std::move(media.data); | |
LOG("\nListing media episodes that we got\n"); | |
for (auto&p : this->media_data) { | |
// entries.emplace_back(p.episode_number, EntryType::FILE); | |
entries.emplace_back("Episode " + p.episode_number + ": " + p.name, | |
EntryType::FILE, | |
std::make_shared<CrunchyPrivateData>(p.episode_number) | |
); | |
// LOG("Premium: %s Episode: %s Name: %s\n", p.premium_only ? "TRUE" : "FALSE", p.episode_number.c_str(), p.name.c_str()); | |
} | |
return entries; | |
} | |
auto Crunchy::SetupFullpath() -> void { | |
// todo: rewrite this as this code was just to show something for current | |
// dir in filebrowser. | |
switch (this->page) { | |
case Pages::HOME: | |
this->fullpath = "Home"; | |
break; | |
case Pages::SERIES_LIST: | |
case Pages::SEARCH_LIST: | |
this->fullpath = "Home/" + this->series_path; | |
break; | |
case Pages::COLLECTIONS: | |
this->fullpath = "Home/" + this->series_path + "/Seasons"; | |
break; | |
case Pages::EPISODES: | |
this->fullpath = "Home/" + this->series_path + "/Seasons/Episodes"; | |
break; | |
} | |
} | |
} // namespace fs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// currently the file borwser api wants files / folders. | |
// so this class has to abstract the crapi to all options being folders. | |
#pragma once | |
#include "fsbase.hpp" | |
#include "crapi/series.hpp" | |
#include "crapi/collection.hpp" | |
#include "crapi/media.hpp" | |
#include <memory> | |
// i think this should be the ordering. | |
// home -> series list -> collections / seasons -> episodes | |
namespace cr { class Crapi; } | |
namespace fs { | |
class Crunchy final : public FsBase { | |
public: | |
enum class Pages { | |
HOME, | |
SERIES_LIST, | |
SEARCH_LIST, | |
COLLECTIONS, | |
EPISODES, | |
}; | |
public: | |
Crunchy(); | |
~Crunchy(); | |
auto List(std::stop_token token, FilterFunc filter_func = {}) -> Result override; | |
auto Open(std::stop_token token, const Entry& entry, OpenType type, FilterFunc filter_func = {}) -> Result override; | |
auto WalkUp(std::stop_token token, FilterFunc filter_func = {}) -> Result override; | |
auto Search(std::stop_token token, const std::string& path, FilterFunc filter_func = {}) -> Result override; | |
auto GetMediaUri(std::stop_token token, const Entry& entry) const -> std::optional<std::string> override; | |
auto CanSearch() const noexcept -> bool override { return true; } | |
private: | |
class CrunchyPrivateData final : public EntryPrivateData { | |
public: | |
CrunchyPrivateData(const std::string &_id) : id{_id} {} | |
std::string id; | |
}; | |
private: | |
auto GetHomeOptions(std::stop_token token) const -> Result; | |
auto GetSeriesList(std::stop_token token) -> Result; | |
auto GetSearchSeriesList(std::stop_token token) -> Result; | |
auto GetCollections(std::stop_token token) -> Result; | |
auto GetEpisodes(std::stop_token token) -> Result; | |
auto SetupFullpath() -> void; | |
private: | |
std::unique_ptr<cr::Crapi> api{nullptr}; | |
std::vector<cr::SeriesData> series_data; // popular, recent, ... | |
std::vector<cr::CollectionData> collection_data; // seasons | |
std::vector<cr::MediaData> media_data; // episodes | |
// todo: join this for fullpath. | |
std::string series_path; // popular, recent, ... | |
std::optional<std::string> search_term; | |
std::string series_id; // which season selected | |
std::string collection_id; // which episode | |
Pages page{Pages::HOME}; | |
}; | |
} // namespace fs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// simple fs base for read-only browsing. | |
// - List(): lists all entries in current path. | |
// - OpenRoot(): opens the root path. | |
// - Open(path, type): opens dir based on type. | |
// - append: is fullpath + path. | |
// - full: fullpath = path then opens. | |
// - WalkUp(): try and walk up | |
#pragma once | |
#include "../utils/thread.hpp" | |
#include <string> | |
#include <string_view> | |
#include <vector> | |
#include <functional> | |
#include <memory> | |
#include <optional> | |
namespace fs { | |
enum class EntryType { | |
FOLDER, | |
FILE, | |
// todo: file types | |
// MOVIE, | |
// MUSIC, | |
// IMAGE, | |
// ZIP, | |
}; | |
// private data for each entry type. | |
// im pretty sure this is a bad idea, but the only other altnenitive | |
// is to have each entry in a vector be a pointer, which sounds a *lot* | |
// worse to me. | |
// another option is to make the private data be a variant. | |
// this is a much nicer approach than pointers but it means having to | |
// keep adding entries to the variant <> in the base class, and logically | |
// the base class should not know about the parent classes data types, it's | |
// just meant to be abstract. | |
// i would also have to include all fs headers here in the base class which | |
// makes no sense to do, so sadly i don't think varient is a better option. | |
class EntryPrivateData { | |
public: | |
virtual ~EntryPrivateData() = default; | |
}; | |
struct Entry { | |
std::string name; | |
EntryType type; | |
std::shared_ptr<EntryPrivateData> private_data{nullptr}; | |
}; | |
using Entries = std::vector<Entry>; | |
enum class ResultType { | |
OK, | |
REQUESTED_EXIT, | |
ERROR, | |
}; | |
struct Result { | |
Result(const std::string& err) : error_message{err} { | |
type = ResultType::ERROR; | |
} | |
Result(const char* err) : error_message{err} { | |
type = ResultType::ERROR; | |
} | |
Result(Entries&& _entries) : entries{std::forward<Entries>(_entries)} { | |
type = ResultType::OK; | |
} | |
Result(ResultType _type) : type{_type} { } | |
auto Good() const noexcept { return this->type == ResultType::OK; } | |
auto ExitedEarly() const noexcept { return this->type == ResultType::REQUESTED_EXIT; } | |
std::optional<std::string> error_message; | |
Entries entries; | |
ResultType type; | |
}; | |
class FsBase { | |
public: | |
enum class OpenType { APPEND, FULL }; | |
// might remove these and just make them bools as i don't really | |
// care why something fails, just need to know if it fails or not. | |
// return boolean instead. | |
enum class /*[[nodiscard]]*/ OpenError { OK, NO_HANDLE, UNK_ERROR }; | |
enum class /*[[nodiscard]]*/ WalkError { OK, NO_HANDLE, ALREADY_ROOT, UNK_ERROR }; | |
enum class /*[[nodiscard]]*/ FilterVal { WANT, SKIP }; | |
using FilterFunc = std::function<FilterVal(std::string_view name, EntryType type)>; | |
using Then = std::function<void()>; | |
public: | |
virtual ~FsBase() = default; | |
// virtual auto connect(std::optional<std::string> user, std::optional<std::string> pass) -> bool { return true; } | |
virtual auto List(std::stop_token token, FilterFunc filter_func = {}) -> Result = 0; | |
virtual auto Open(std::stop_token token, const Entry& entry, OpenType type, FilterFunc filter_func = {}) -> Result = 0; | |
virtual auto WalkUp(std::stop_token token, FilterFunc filter_func = {}) -> Result = 0; | |
virtual auto Search(std::stop_token token, const std::string& path, FilterFunc filter_func = {}) -> Result { return {"Search Not Supported!"}; } // TODO: make = 0 soon tm | |
virtual auto GetMediaUri(std::stop_token token, const Entry& entry) const -> std::optional<std::string> = 0; | |
virtual auto CanSearch() const noexcept -> bool { return false; } | |
auto List(FilterFunc filter_func = {}) { | |
return this->List(std::stop_token{}, filter_func); | |
} | |
auto Open(const Entry& entry, OpenType type, FilterFunc filter_func = {}) { | |
return this->Open(std::stop_token{}, entry, type, filter_func); | |
} | |
auto WalkUp(FilterFunc filter_func = {}) { | |
return this->WalkUp(std::stop_token{}, filter_func); | |
} | |
auto Search(const std::string& path, FilterFunc filter_func = {}) { | |
return this->Search(std::stop_token{}, path, filter_func); | |
} | |
auto GetMediaUri(const Entry& entry) const { | |
return this->GetMediaUri(std::stop_token{}, entry); | |
} | |
// enable for testing non-async | |
#if 0 | |
auto ListAsync(FilterFunc filter_func = {}, Then then = {}) { | |
auto r = util::Async([this, filter_func, then](std::stop_token token) { | |
const auto result = this->List(token, filter_func); | |
if (then) { | |
then(); | |
} | |
return result; | |
}); | |
r.Wait(); | |
return r; | |
} | |
auto OpenAsync(const Entry& entry, OpenType type, FilterFunc filter_func = {}, Then then = {}) { | |
auto r = util::Async([this, entry, type, filter_func, then](std::stop_token token) { | |
const auto result = this->Open(token, entry, type, filter_func); | |
if (then) { | |
then(); | |
} | |
return result; | |
}); | |
r.Wait(); | |
return r; | |
} | |
auto WalkUpAsync(FilterFunc filter_func = {}, Then then = {}) { | |
auto r = util::Async([this, filter_func, then](std::stop_token token) { | |
const auto result = this->WalkUp(token, filter_func); | |
if (then) { | |
then(); | |
} | |
return result; | |
}); | |
r.Wait(); | |
return r; | |
} | |
auto SearchAsync(const std::string& path, FilterFunc filter_func = {}, Then then = {}) { | |
auto r = util::Async([this, path, filter_func, then](std::stop_token token) { | |
const auto result = this->Search(token, path, filter_func); | |
if (then) { | |
then(); | |
} | |
return result; | |
}); | |
r.Wait(); | |
return r; | |
} | |
auto GetMediaUriAsync(const Entry& entry, Then then = {}) const { | |
auto r = util::Async([this, entry, then](std::stop_token token) { | |
const auto result = this->GetMediaUri(token, entry); | |
if (then) { | |
then(); | |
} | |
return result; | |
}); | |
r.Wait(); | |
return r; | |
} | |
#else | |
auto ListAsync(FilterFunc filter_func = {}, Then then = {}) { | |
return util::Async([this, filter_func, then](std::stop_token token) { | |
const auto result = this->List(token, filter_func); | |
if (then) { | |
then(); | |
} | |
return result; | |
}); | |
} | |
auto OpenAsync(const Entry& entry, OpenType type, FilterFunc filter_func = {}, Then then = {}) { | |
return util::Async([this, entry, type, filter_func, then](std::stop_token token) { | |
const auto result = this->Open(token, entry, type, filter_func); | |
if (then) { | |
then(); | |
} | |
return result; | |
}); | |
} | |
auto WalkUpAsync(FilterFunc filter_func = {}, Then then = {}) { | |
return util::Async([this, filter_func, then](std::stop_token token) { | |
const auto result = this->WalkUp(token, filter_func); | |
if (then) { | |
then(); | |
} | |
return result; | |
}); | |
} | |
auto SearchAsync(const std::string& path, FilterFunc filter_func = {}, Then then = {}) { | |
return util::Async([this, path, filter_func, then](std::stop_token token) { | |
const auto result = this->Search(token, path, filter_func); | |
if (then) { | |
then(); | |
} | |
return result; | |
}); | |
} | |
auto GetMediaUriAsync(const Entry& entry, Then then = {}) const { | |
return util::Async([this, entry, then](std::stop_token token) { | |
const auto result = this->GetMediaUri(token, entry); | |
if (then) { | |
then(); | |
} | |
return result; | |
}); | |
} | |
#endif | |
auto FullPath() const { return this->fullpath; } // not noexcept because alloc can fail | |
auto IsGood() const noexcept { return this->good; } | |
protected: | |
std::string fullpath; | |
bool good{false}; | |
private: | |
}; | |
} // namespace fs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "funime.hpp" | |
#include "funapi/funapi.hpp" | |
#include "hlsparse/hlsparse.hpp" | |
#include "../utils/logger.hpp" | |
#include <algorithm> | |
#include <ranges> | |
#include <iostream> | |
namespace fs { | |
Funime::Funime() { | |
this->api = std::make_unique<funi::Funapi>(); | |
this->SetupFullpath(); | |
} | |
Funime::~Funime() { | |
} | |
auto Funime::List(std::stop_token stk, FilterFunc filter_func) -> Result { | |
try { | |
switch (this->page) { | |
case Pages::HOME: | |
return this->GetHomeOptions(stk); | |
case Pages::GENRE_SHOWS: | |
return this->GetGenreShows(stk); | |
case Pages::SEARCH_SHOWS: | |
return this->GetSearchShows(stk); | |
case Pages::SEASONS: | |
return this->GetSeasons(stk); | |
case Pages::EPISODES: | |
return this->GetEpisodes(stk); | |
} | |
} catch (const std::exception& e) { | |
LOG("[EXCEPTION] %s\n", e.what()); | |
return {e.what()}; | |
} | |
__builtin_unreachable(); | |
} | |
auto Funime::Open(std::stop_token stk, const Entry& entry, OpenType type, FilterFunc filter_func) -> Result { | |
auto private_data = dynamic_cast<FunimePrivateData*>(entry.private_data.get()); | |
switch (this->page) { | |
case Pages::HOME: | |
this->genre_id = private_data->id; | |
this->search_term.reset(); | |
this->page = Pages::GENRE_SHOWS; | |
break; | |
case Pages::GENRE_SHOWS: | |
case Pages::SEARCH_SHOWS: | |
this->title_id = private_data->id; | |
this->page = Pages::SEASONS; | |
break; | |
case Pages::SEASONS: | |
this->season_id = private_data->id; | |
this->page = Pages::EPISODES; | |
break; | |
case Pages::EPISODES: | |
// impossible! | |
throw; | |
return {"oof"}; | |
} | |
this->SetupFullpath(); | |
return this->List(stk, filter_func); | |
} | |
auto Funime::WalkUp(std::stop_token stk, FilterFunc filter_func) -> Result { | |
switch (this->page) { | |
case Pages::HOME: | |
// return "Already Root!"; | |
break; | |
case Pages::GENRE_SHOWS: | |
case Pages::SEARCH_SHOWS: | |
this->page = Pages::HOME; | |
break; | |
case Pages::SEASONS: | |
if (this->search_term.has_value()) { | |
this->page = Pages::SEARCH_SHOWS; | |
} else { | |
this->page = Pages::GENRE_SHOWS; | |
} | |
break; | |
case Pages::EPISODES: | |
this->page = Pages::SEASONS; | |
break; | |
} | |
this->SetupFullpath(); | |
return this->List(stk, filter_func); | |
} | |
auto Funime::Search(std::stop_token stk, const std::string& path, FilterFunc filter_func) -> Result { | |
this->page = Pages::SEARCH_SHOWS; | |
this->search_term = path; | |
return this->List(stk, filter_func); | |
} | |
auto Funime::GetMediaUri(std::stop_token stk, const Entry& entry) const -> std::optional<std::string> { | |
const auto private_data = dynamic_cast<FunimePrivateData*>(entry.private_data.get()); | |
// basically makes 1 api call and 1 download for the master.m3u8. | |
// the master m3u8 is then parsed for the actual url. | |
// this is because mpv (or ffmpeg, not sure) is slow as shit at parsing | |
// the master, and also randomly picks a stream. | |
// this way we have more control over the stream quality and speed. | |
// TODO: return struct of video url + vector<string> subs. | |
try { | |
const auto episode = this->api->GetEpisode(private_data->id).value(); | |
if (const auto streams = episode.GetMediaStreams(); !streams.empty()) { | |
for (auto& stream : streams) { | |
std::cout << "StreamID: " << stream.experience.id << '\n'; | |
} | |
auto& stream = streams[0]; | |
const auto showex = this->api->GetShowExperience(stream.experience.id).value(); | |
if (auto it = std::ranges::find(showex.items, "m3u8", &funi::ShowExperience::Item::videoType); it != showex.items.end()) { | |
curl::CurlWrapper curl; | |
if (const auto data = curl.Download(curl::Url{it->src})) { | |
if (const auto hls = hls::ParseMaster(*data); !hls.empty()) { | |
return hls[0].url; | |
} | |
} | |
} | |
} | |
} catch (const std::exception& e) { | |
LOG("[EXCEPTION] %s\n", e.what()); | |
} | |
return {}; | |
} | |
auto Funime::GetHomeOptions(std::stop_token stk) -> Result { | |
const auto data = this->api->ListGenresAsync(stk).value(); | |
Entries entries; | |
for (auto& entry : data) { | |
entries.emplace_back(entry.name, | |
EntryType::FOLDER, | |
std::make_shared<FunimePrivateData>(entry.id) | |
); | |
} | |
return entries; | |
} | |
auto Funime::GetGenreShows(std::stop_token stk) -> Result { | |
// this one is really really slow | |
const auto data = this->api->ListTitlesFromGenreAsync(stk, this->genre_id).value(); | |
// const auto data = this->api->ListTitlesFromGenre2(this->genre_id).value(); | |
Entries entries; | |
for (auto& entry : data.items) { | |
entries.emplace_back(entry.title, | |
EntryType::FOLDER, | |
std::make_shared<FunimePrivateData>(entry.id) | |
); | |
} | |
return entries; | |
} | |
auto Funime::GetSearchShows(std::stop_token stk) -> Result { | |
const auto data = this->api->SearchAsync(stk, *this->search_term).value(); | |
Entries entries; | |
for (auto& entry : data.items.hits) { | |
entries.emplace_back(entry.title, | |
EntryType::FOLDER, | |
std::make_shared<FunimePrivateData>(entry.id) | |
); | |
} | |
return entries; | |
} | |
auto Funime::GetSeasons(std::stop_token stk) -> Result { | |
const auto data = this->api->GetTitleInfoAsync(stk, this->title_id).value(); | |
Entries entries; | |
for (auto& item : data.items) { | |
for (auto& child : item.children) { | |
entries.emplace_back(child.title, | |
EntryType::FOLDER, | |
std::make_shared<FunimePrivateData>(std::stoul(child.number)) | |
); | |
} | |
} | |
return entries; | |
} | |
auto Funime::GetEpisodes(std::stop_token stk) -> Result { | |
const auto data = this->api->ListEpisodesAsync(stk, this->title_id, this->season_id).value(); | |
Entries entries; | |
for (auto& entry : data.items) { | |
entries.emplace_back( | |
entry.mediaCategory + ' ' + entry.item.episodeNum + ": " + entry.item.episodeName, | |
EntryType::FILE, | |
std::make_shared<FunimePrivateData>(entry.GetEpisodeID()) | |
); | |
} | |
return entries; | |
} | |
auto Funime::SetupFullpath() -> void { | |
// todo: rewrite this as this code was just to show something for current | |
// dir in filebrowser. | |
switch (this->page) { | |
case Pages::HOME: | |
this->fullpath = "Home/"; | |
break; | |
case Pages::GENRE_SHOWS: case Pages::SEARCH_SHOWS: | |
this->fullpath = "Home/Shows/"; | |
break; | |
case Pages::SEASONS: | |
this->fullpath = "Home/Shows/Seasons"; | |
break; | |
case Pages::EPISODES: | |
this->fullpath = "Home/Shows/Seasons/Episodes"; | |
break; | |
} | |
} | |
} // namespace fs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "fsbase.hpp" | |
#include <memory> | |
namespace funi { class Funapi; } | |
namespace fs { | |
class Funime final : public FsBase { | |
public: | |
enum class Pages { | |
HOME, | |
GENRE_SHOWS, | |
SEARCH_SHOWS, | |
SEASONS, | |
EPISODES, | |
}; | |
public: | |
Funime(); | |
~Funime(); | |
auto List(std::stop_token token, FilterFunc filter_func = {}) -> Result override; | |
auto Open(std::stop_token token, const Entry& entry, OpenType type, FilterFunc filter_func = {}) -> Result override; | |
auto WalkUp(std::stop_token token, FilterFunc filter_func = {}) -> Result override; | |
auto Search(std::stop_token token, const std::string& path, FilterFunc filter_func = {}) -> Result override; | |
auto GetMediaUri(std::stop_token token, const Entry& entry) const -> std::optional<std::string> override; | |
auto CanSearch() const noexcept -> bool override { return true; } | |
private: | |
class FunimePrivateData final : public EntryPrivateData { | |
public: | |
FunimePrivateData(std::uint32_t _id) : id{_id} {} | |
std::uint32_t id; | |
}; | |
private: | |
auto GetHomeOptions(std::stop_token token) -> Result; | |
auto GetGenreShows(std::stop_token token) -> Result; | |
auto GetSearchShows(std::stop_token token) -> Result; | |
auto GetSeasons(std::stop_token token) -> Result; | |
auto GetEpisodes(std::stop_token token) -> Result; | |
auto SetupFullpath() -> void; | |
private: | |
std::unique_ptr<funi::Funapi> api; | |
std::optional<std::string> search_term{std::nullopt}; | |
std::uint32_t genre_id{}; | |
std::uint32_t title_id{}; | |
std::uint32_t season_id{}; | |
Pages page{Pages::HOME}; | |
}; | |
} // namespace fs |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment