teddycloud icon indicating copy to clipboard operation
teddycloud copied to clipboard

Expansion of tonie.json with API data from your own Tonie account

Open ageof opened this issue 6 months ago • 11 comments

📌 Goal The local file tonie.json currently contains only limited information about Tonie figures. I suggest automatically expanding or replacing this file by obtaining missing data directly from your own Tonie account.

🔍 Background

All of your Tonies are listed with an image, title, and description at https://my.tonies.com/content-tonies. This data is provided via a GraphQL API: API endpoint: https://api.prod.tcs.toys/v2/graphql The JSON there contains:

  • Title
  • Description
  • Cover image

Tag ID (e.g. E0040350178864AE), which can be linked to the local tonie.json and the tag ID.

🔐 Authentication

Login is easy to implement with Python (session + token). Once logged in, you can retrieve the content and process it locally. Implementation ideaPython script for authentication and data retrieval Linking API data with local tag IDs Extension or regeneration of tonie.json Optional: Integration as a CLI tool or web module in TeddyCloud Advantages Automatic addition of missing contentUniform display of all Tonies No more manual maintenance of JSON files

❓Open questions

Where and how should the script be integrated into TeddyCloud? (e.g., as a module, hook, admin tool) How do we handle authentication and token storage? What do you think of the idea? I welcome feedback and am happy to contribute an initial prototype.

ageof avatar Oct 08 '25 12:10 ageof

What is the advantage regards the current automatic data scraping implemented? Data is fetched from tonies also?

henryk86 avatar Oct 08 '25 13:10 henryk86

It connects the figurine and the content without the way over the audio-id. Using this, the quality of the tonies-json could be enhanced and automatically crowdsourced.

SciLor avatar Nov 10 '25 07:11 SciLor

As a first step the tonies.custom.json could be used for that purpose. But it would be nicer to generate YAMLs as https://github.com/toniebox-reverse-engineering/tonies-json does and then generate the JSON itself out of it (I have a python script doing that).

I would see, as a first step, a Python based prototype would be interesting. How it could be integrated is another step to be solved. Especially the UX Part. If there is any API missing within teddyCloud let me know.

There will be several edge cases to be checked: Creative Tonies, Tonies with Tunes Content from the shop, Custom content, those combined cheap book Tonies.

SciLor avatar Nov 11 '25 10:11 SciLor

@SciLor I think that's great. Would you share your script with me so I don't have to rewrite it myself?

ageof avatar Nov 11 '25 13:11 ageof

This is the raw Version. You'll need to fix it manually, as the Config is missing. You may delete the YamlStruct entirely out of the script. I may make a repo with it public someday. But this should work for you. Feel free to contact me via Telegram

import os
import yaml
import json
import subprocess
import argparse

from article_yaml_helpers import YamlStruct
from tonies_json_config import Config

def main():
    parser = argparse.ArgumentParser(description="Export YAML files to JSON")
    parser.add_argument(
        "--simulate", "-s",
        action="store_true",
        help="Run in simulate mode (no output files are written)"
    )
    args = parser.parse_args()

    yaml_dir = Config.yaml_dir
    tonies_file = Config.export_tonies_file
    toniesV2_file = Config.export_toniesV2_file

    tonies_json = []
    toniesV2_json = []

    for filename in sorted(os.listdir(yaml_dir)):
        if filename.endswith('.yaml'):
            yaml_path = f"{yaml_dir}{filename}"
            with open(yaml_path, 'r') as yaml_file:
                base = yaml.safe_load(yaml_file)

            toniesV2_base = {
                "article": str(base["article"]),
                "data": []
            }
            no = 0
            for item in base["data"]:
                audio_ids = []
                hashes = []
                for id in item["ids"]:
                    audio_ids.append(str(id["audio-id"]))
                    hashes.append(id["hash"].upper())
                tonies_element = {
                    "no": str(no),
                    "model": str(base["article"]),
                    "audio_id": audio_ids,
                    "hash": hashes,
                    "title": f'{item["series"]} - {item["episode"]}',
                    "series": item["series"],
                    "episodes": item["episode"],
                    "tracks": item["track-desc"],
                    "release": str(item["release"]),
                    "language": item["language"],
                    "category": item["category"],
                    "pic": item["image"]
                }
                no += 1
                tonies_json.append(tonies_element)

                toniesV2_element = {
                    "series": item["series"],
                    "episode": item["episode"],
                    "release": item["release"],
                    "language": item["language"],
                    "category": item["category"],
                    "runtime": item["runtime"],
                    "age": item["age"],
                    "origin": item["origin"],
                    "image": item["image"],
                    "sample": item["sample"],
                    "web": item["web"],
                    "shop-id": item["shop-id"],
                    "track-desc": item["track-desc"],
                    "ids": item["ids"]
                }
                toniesV2_base["data"].append(toniesV2_element)
            toniesV2_json.append(toniesV2_base)

    if args.simulate:
        print(f"[SIMULATE] Would export {len(tonies_json)} v1 elements to {tonies_file}")
        print(f"[SIMULATE] Would export {len(toniesV2_json)} v2 elements to {toniesV2_file}")
    else:
        with open(tonies_file, "w", encoding="utf-8") as json_file:
            json.dump(tonies_json, json_file, ensure_ascii=False)
            print(f"Exported {len(tonies_json)} v1 elements")

        with open(toniesV2_file, "w", encoding="utf-8") as json_file:
            json.dump(toniesV2_json, json_file, ensure_ascii=False)
            print(f"Exported {len(toniesV2_json)} v2 elements")

if __name__ == "__main__":
    main()

SciLor avatar Nov 11 '25 14:11 SciLor

@SciLor Thanks, I saw that you implemented a bug fix in the method for updating the JSON. Can't we implement a method there directly that calls your script so that we don't need to make any changes to the UX?

ageof avatar Nov 12 '25 07:11 ageof

I meant with the UX Part: How the user can enter his credentials and how we can ask the user to provide the data to fill the tonies-json (globally) with that data in the future...

Thanks, I saw that you implemented a bug fix in the method for updating the JSON Sorry I don't understand that.

SciLor avatar Nov 12 '25 08:11 SciLor

I think that’s a good part for the web Ui of tc.

Credentials can be stored locally in a semi safe way, and then we can fetch the data using the tonies account of the user. The so fetched data can be send to a to be defined api endpoint which sorts the data and filter bullshit requests.

It could be fully automated so the enduser has no chance to break the data. Kind of Ui: enter tonies credentials (so even without storing the credentials), fetch data from tonies and submit data to the to be tonies.json api.

henryk86 avatar Nov 16 '25 07:11 henryk86

It could be added as a menu entry in community —> contribution or directly on the home section. Contribute your toniemetadata from tonies. ( no auth codes).

Could be a page with credential input fields (allow user to store them locally)

A button fetch data, then a list with all fetched data (so he sees what data will be submitted) where user can select the entries he want to submit (all preselected) and a submit to tonies.json button.

henryk86 avatar Nov 16 '25 07:11 henryk86

ok. pure frontend is not possible as they do not use graphql for authentication. they are using an other service instead which can not be called from frontend (XCORS)... too bad...

henryk86 avatar Nov 16 '25 11:11 henryk86

so we can just use the frontend to get the user credentials, pass them to backend, backend does the magic (maybe for now just returns the authorization bearer token) and then we can proceed in frontend (select what we want to submit) and send the selected data to the tonies.json api

henryk86 avatar Nov 16 '25 11:11 henryk86