Greasy Fork is available in English.

HLTB in Backloggd Lists

HLTB times in Backloggd

// ==UserScript==
// @name         HLTB in Backloggd Lists
// @namespace    http://tampermonkey.net/
// @version      0.3.1
// @description  HLTB times in Backloggd
// @author       Siev
// @license      MIT
// @match        *://www.backloggd.com/*
// @match        *://backloggd.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=backloggd.com
// @grant        GM_xmlhttpRequest
// ==/UserScript==

// TODO: fix the sorting in lists to be across pages

let hltbUrl = "https://howlongtobeat.com/api/search/";

function sortList() {
    let getNodeTime = node => $(node).data('hltbTime');
    let sortFunc = $("#sort-direction-btn > .fad").hasClass('fa-sort-down') ?
        (a,b) => getNodeTime(b) - getNodeTime(a)
      : (a,b) => getNodeTime(a) - getNodeTime(b);
    let arr = $('.toggle-fade').children().toArray().toSorted(sortFunc);
    $('.toggle-fade').append($(arr));
}

// add the options for sorting the list by time
$('[id=nav-interactables]').eq(1)
    .find('li.nav-item').last()
    .after(function() { return $(this).clone() })
    .next()
    .click(function () {
        $("#sort-direction-btn")
            .click(function () {
                $('i', this).toggleClass(['fa-sort-up', 'fa-sort-down']);
                sortList();
            })
            .attr('href','javascript: void(0);');
       $(this).parent().prev().contents().first()[0].data = ' HLTB Time ';
       sortList();
    })
    .find('a')
    .text('HLTB Time')
    .attr('href','javascript: void(0);');


// get time from HLTB
function lookupGame(gameName, context, callback) {
    let hltbQuery = '{"searchType":"games","searchTerms":["'+gameName+'"],"size":100}';
    GM_xmlhttpRequest({
        context: context,
        method: "POST",
        url: hltbUrl,
        data: hltbQuery,
        headers: {
            "Content-Type": "application/json",
            "origin": "https://howlongtobeat.com",
            "referer": "https://howlongtobeat.com"
        },
        onload: function (response) {
            var rawTime;
            var hltbTime;
            let game = response.context;
            // Grab response
            let hltb = JSON.parse(response.responseText);

                //Determine if data is present in response by checking the page count.  If no data, set default HLTB button.
                let hltbPages = hltb['pageTotal'];
                if(hltbPages == 0) {
                    hltbTime = "?";
                } else {
                    let hltbstring = JSON.stringify(hltb);

                    //Make sure you have the right game_name (if possible, otherwise just use first result from response)
                    let n = 0;
                    let loop = hltb['count'];
                    for (let i = 0; i < loop; i++) {
                        let hltbName = hltb['data'][i]['game_name'];
                        if (hltbName.toLowerCase() == gameName.toLowerCase()) {
                            n = i;
                            break;
                        }
                    }

                    // convert and format time
                    rawTime = hltb['data'][n]['comp_plus'];
                    hltbTime = rawTime;
                    hltbTime = hltbTime/60/60;                            // Convert to hours
                    hltbTime = Math.round(hltbTime*2)/2;                  // Round to closes .5
                    hltbTime = hltbTime.toString().replace(".5","½");    // Convert .5 to ½ to be consistent with HLTB
                    if (hltbTime[0] == '0') hltbTime = hltbTime.substr(1);
                }

            callback(rawTime, hltbTime, response.context);
        }
    });
}

// showing the time under game cards
$('.card').not('.overlay-hide').filter(function (i) {
    return !$(this).parent().parent().next().hasClass('coming-details')
}).each(function (i, game) {
    let gameName = $('img', this).attr('alt');
    lookupGame(gameName, game, function (raw, fmt, game) {
        //$(game).parent().parent().parent().data('hltbTime', raw);
        $(game).parents('.toggle-fade > div').data('hltbTime', raw);
        $(game).after("<div>" + fmt + " Hours</div>").next().css({ fontSize: '0.9rem', paddingLeft: '0.15rem' });
    })
});

// showing the time in the game page
let gameName = $("#title > div.col-12.pr-0 > div > div > h1").text();
lookupGame(gameName, 0, function (raw, fmt, context) {
    // clone the average score panel and change it to hltb time
    var panel = $('.side-section').eq(1).children().first().before(function(i) { return $(this).clone() }).prev();
    panel.find('p').text('HLTB Time');
    panel.find('h1').text(fmt);
});