44
55#include " ../../incl/Avalanche.hpp"
66
7+ #include < iomanip>
78#include < sstream>
89
910#include < fmt/core.h>
2122using namespace geode ::prelude;
2223using namespace avalanche ;
2324
24- std::string findYouTubeID (std::string const & url, Project const & avalProject) {
25- std::string videoId = " coCcCJYLVRk" ; // extract from url
26- constexpr size_t idSize = 11 ; // default length for yt video ids
27-
28- // list of possible yt url prefixes
29- const std::pair<std::string, size_t > prefixes[] = {
30- {" https://youtu.be/" , idSize},
31- {" https://www.youtube.com/watch?v=" , idSize},
32- {" https://www.youtube.com/embed/" , idSize},
33- {" https://www.youtube.com/v/" , idSize},
34- {" https://youtube.com/watch?v=" , idSize},
35- {" https://youtube.com/embed/" , idSize},
36- {" https://youtube.com/v/" , idSize}
37- };
38-
39- for (const auto & [prefix, idLen] : prefixes) {
40- auto url = avalProject.showcase ;
25+ Handler* avalHandler = Handler::get();
4126
42- if (url.find (prefix) == 0 ) {
43- AVAL_LOG_INFO (" Found YouTube URL prefix '{}'" , prefix);
44- videoId = url.substr (prefix.length (), idLen);
45- break ;
27+ inline std::string url_encode (const std::string& value) {
28+ std::ostringstream escaped;
29+ escaped.fill (' 0' );
30+ escaped << std::hex;
31+ for (char c : value) {
32+ if (isalnum (static_cast <unsigned char >(c)) || c == ' -' || c == ' _' || c == ' .' || c == ' ~' ) {
33+ escaped << c;
4634 } else {
47- AVAL_LOG_DEBUG (" Skipped URL format '{}'" , url);
48- };
49- };
50-
51- return videoId;
52- };
35+ escaped << ' %' << std::setw (2 ) << int ((unsigned char )c);
36+ }
37+ }
38+ return escaped.str ();
39+ }
5340
5441ProjectInfoPopup* ProjectInfoPopup::create () {
5542 auto ret = new ProjectInfoPopup;
@@ -194,7 +181,7 @@ ProjectInfoPopup* ProjectInfoPopup::setProject(GJGameLevel* level) {
194181 setZOrder (10 );
195182
196183 m_level = level;
197- m_avalProject = Handler::get () ->GetProject (m_level->m_levelID .value ());
184+ m_avalProject = avalHandler ->GetProject (m_level->m_levelID .value ());
198185
199186 if (m_avalProject.type == Project::Type::NONE) {
200187 AVAL_LOG_ERROR (" Avalanche project type is NONE" );
@@ -417,8 +404,7 @@ ProjectInfoPopup* ProjectInfoPopup::setProject(GJGameLevel* level) {
417404 isCustomThumbnail = true ;
418405 };
419406
420- std::string videoId = isCustomThumbnail ? " " : findYouTubeID (m_avalProject.showcase , m_avalProject); // extracted video id from showcase url
421- std::string projThumbURL = isCustomThumbnail ? m_avalProject.thumbnail : fmt::format (" https://img.youtube.com/vi/{}/maxresdefault.jpg" , (std::string)videoId); // custom thumbnail or formatted yt url
407+ std::string projThumbURL = isCustomThumbnail ? m_avalProject.thumbnail : fmt::format (" https://api.cubicstudios.xyz/avalanche/v1/fetch/thumbnails?id={}" , (int )m_level->m_levelID .value ()); // custom thumbnail or default
422408
423409 AVAL_LOG_DEBUG (" Getting thumbnail at {}..." , projThumbURL);
424410
@@ -454,7 +440,7 @@ ProjectInfoPopup* ProjectInfoPopup::setProject(GJGameLevel* level) {
454440
455441 if (m_avalProject.link_to_main .enabled ) {
456442 AVAL_LOG_DEBUG (" Project '{}' has a link to the main project" , m_avalProject.name );
457- auto linkedProj = Handler::get () ->GetProject (m_avalProject.link_to_main .level_id );
443+ auto linkedProj = avalHandler ->GetProject (m_avalProject.link_to_main .level_id );
458444
459445 if (linkedProj.type == Project::Type::NONE) {
460446 AVAL_LOG_ERROR (" Failed to get linked project with ID {}" , m_avalProject.link_to_main .level_id );
@@ -567,18 +553,18 @@ ProjectInfoPopup* ProjectInfoPopup::setProject(GJGameLevel* level) {
567553 linkedProjThumb->setLoadCallback ([linkedProjThumb, linkedProjClippingNode](Result<> res) {
568554 if (res.isOk ()) {
569555 AVAL_LOG_INFO (" Linked project thumbnail loaded successfully" );
556+
557+ linkedProjThumb->setScale (1 .f );
558+ linkedProjThumb->setScale (linkedProjClippingNode->getScaledContentHeight () / linkedProjThumb->getScaledContentHeight ());
559+
560+ linkedProjThumb->setPosition (linkedProjClippingNode->getPosition ());
561+ linkedProjThumb->ignoreAnchorPointForPosition (false );
562+ linkedProjThumb->setColor ({ 250 , 250 , 250 });
563+ linkedProjThumb->setOpacity (250 );
570564 } else {
571565 AVAL_LOG_ERROR (" Failed to load linked project thumbnail: {}" , res.unwrapErr ());
572- linkedProjThumb->initWithSpriteFrameName ( " unavailable.png " _spr );
566+ linkedProjThumb->removeMeAndCleanup ( );
573567 };
574-
575- linkedProjThumb->setScale (1 .f );
576- linkedProjThumb->setScale (linkedProjClippingNode->getScaledContentHeight () / linkedProjThumb->getScaledContentHeight ());
577-
578- linkedProjThumb->setPosition (linkedProjClippingNode->getPosition ());
579- linkedProjThumb->ignoreAnchorPointForPosition (false );
580- linkedProjThumb->setColor ({ 250 , 250 , 250 });
581- linkedProjThumb->setOpacity (250 );
582568 });
583569
584570 bool isCustomThumbnail = false ; // whether the thumbnail is custom
@@ -592,12 +578,12 @@ ProjectInfoPopup* ProjectInfoPopup::setProject(GJGameLevel* level) {
592578 isCustomThumbnail = true ;
593579 };
594580
595- std::string linkedVideoId = isCustomThumbnail ? " " : findYouTubeID (linkedProj.showcase , m_avalProject ); // extracted video id from showcase url
596- std::string linkedProjThumbURL = isCustomThumbnail ? linkedProj .thumbnail : fmt::format (" https://img.youtube.com/vi/{}/maxresdefault.jpg " , (std::string)linkedVideoId ); // custom thumbnail or formatted yt url
581+ std::string encodedShowcaseUrl = url_encode (linkedProj.showcase ); // encode the showcase url for use in the thumbnail url
582+ std::string linkedProjThumbURL = isCustomThumbnail ? m_avalProject .thumbnail : fmt::format (" https://api.cubicstudios.xyz/avalanche/v1/fetch/thumbnails?id={} " , (int )m_avalProject. link_to_main . level_id ); // custom thumbnail or default
597583
598584 AVAL_LOG_DEBUG (" Getting linked project thumbnail at {}..." , linkedProjThumbURL);
599585 linkedProjThumb->loadFromUrl (linkedProjThumbURL, LazySprite::Format::kFmtUnKnown , false );
600- linkedProjClippingNode->addChild (linkedProjThumb);
586+ if (linkedProjThumb) linkedProjClippingNode->addChild (linkedProjThumb);
601587
602588 // set border
603589 auto linkedProjBorder = CCScale9Sprite::create (" GJ_square07.png" );
0 commit comments