ZombieEllipses
I'm writing a Python script to do some work on my backups from TheBrain.

I've noticed that Thought Notes that contain links to other thoughts use values in the URL that can't directly be found anywhere else in the backup, so my current assumption is that these are hashed / encoded values of some kind.

How does TheBrain generate these values? I need to be able to decipher these back to the Thought Id value so I can honor these links in my output.
Quote
jupdike
If you have a long GUID string (like d6a0dd87-5892-5bb9-b097-bf44ef5464b0) you can convert it to and from these short URLs with code like this (unsupported, left as an exercise to the reader; this is C# code):

public static string ToShortGuid(this Guid newGuid) {
string modifiedBase64 = Convert.ToBase64String(newGuid.ToByteArray())
.Replace('+', '-').Replace('/', '_') // avoid invalid URL characters
.Substring(0, 22);
return modifiedBase64;
}

public static Guid ParseShortGuid(this string shortGuid) {
string base64 = shortGuid.Replace('-', '+').Replace('_', '/') + "==";
Byte[] bytes = Convert.FromBase64String(base64);
return new Guid(bytes);
}
Jared Updike
TheBrain Technologies
Engineering Team
Quote
ZombieEllipses
Strange. I feel like I'm close -- when converting from the GUID to what you call the ShortGuid, using Python, I can get part of the string to match, but not all of it.
Quote
ZombieEllipses
Ah, ok, solved. Difference in the way C# and Python natively order the elements in a GUID/UUID.
Quote
smieg
Any chance you would share your Python code?  I am working on a QGIS Plugin to map all Thoughts with a Google Map URL and it would be great to provide a link back to TheBrain Thought.

Thanks,
Andy
Quote
smieg
I figured it out :-)

If you are interested, check out wikipedia for a comparison of the guid and uuid formats.
https://en.wikipedia.org/wiki/Universally_unique_identifier#Format

I'll post my python code later today.

Andy
Quote
smieg
Here is my python code for creating TheBrain short URL's.

 

    def construct_short_url(self, brain_id, thought_id, thought_name):

        thought_brainid_guid = uuid.UUID(self.reorder_guid(brain_id))

        thought_brainid_b64 = base64.b64encode(thought_brainid_guid.bytes).decode("utf-8")

        thought_brainid_b64 = thought_brainid_b64.replace('+', '-').replace('/', '_')

        thought_brainid_b64 = thought_brainid_b64[0:22]

 

        thought_id_guid = uuid.UUID(self.reorder_guid(thought_id))

        thought_id_b64 = base64.b64encode(thought_id_guid.bytes).decode("utf-8")

        thought_id_b64 = thought_id_b64.replace('+', '-').replace('/', '_')

        thought_id_b64 = thought_id_b64[0:22]

 

        thought_short_url = "brain://api.thebrain.com/" + \

                            thought_brainid_b64 + "/" + \

                            thought_id_b64 + "/" + \

                            thought_name.replace(" ", "").replace("(", "").replace(")", "")

 

        return thought_short_url

 

    def reorder_guid(self, guid):

        guid_02 = guid[0:2]

        guid_24 = guid[2:4]

        guid_46 = guid[4:6]

        guid_68 = guid[6:8]

        guid_911 = guid[9:11]

        guid_1113 = guid[11:13]

        guid_1416 = guid[14:16]

        guid_1618 = guid[16:18]

        guid_split = guid[18:]

        guid_reordered = guid_68 + guid_46 + guid_24 + guid_02 + "-" \

                          + guid_1113 + guid_911 + "-" \

                          + guid_1618 + guid_1416 \

                          + guid_split

        return guid_reordered

Quote
eeik
Hi @smieg 

I'm curious how you derived or inferred this approach and I'm curious if there is a way to also apply a similar approach to thought "links" as well as thoughts themselves?

I've submitted the following new feature request -> Extend first class citizenship to "links" <- as I VERY OFTEN wish to capture my own contextual notes, stash relevant attachments, and retain context "between thoughts" and wish to link directly to that "relationship node" itself which I can't find a way to accomplish ... but your GUID to short-link approach may do the trick and allow for manual creation of internal brain links from other notes.html files directly to these thought links.

Any thoughts on how one might approach this in light of your own research?
Quote
mcaton
Interesting and useful code.  I'll share this with the engineering team.

Thanks Eric for bringing this thread to my attention.

Matt
Quote
smieg
Hi eeik,

My code allows for the construction of TheBrain Short URL so that I can provide a hyperlink from QGIS to Thoughts in my megabrain that have Google Places attached (QGIS Plugin written in Python).  It works, I believe, because it is the equivalent of using the Copy Local Though URL functionality in The Brain and relies on TheBrain to intercept and resolve the address of the Thought.  I do not believe nor tested whether this will work with other entities in TheBrain beyond Thoughts.  TheBrain staff would have to answer that question.

Andy
Quote
eeik
@smieg, I've leveraged your code, thank you so much btw!, and adapted it to be a parameterizable script to run from BrainID and ThoughtID source inputs, wherever these values may come from 👍

I'm curious if you or the team might be able to comment on why the thought name is actually required?

I've tested numerous links while stripping the "name" component off of the tail of the link entirely and the app resolves to the correct thought every time in my experience. Also, the "copy local thought URL" function seems to enforce camel casing of thought names, even if words within the name itself begin with lowercase letters, plus the "copy local thought URL" function also strips symbols (i.e. ?, #, $, etc.) from the name as well.

Is there any case where the name value actually does disambiguate in some way that I can't think of? Having a unique BrainID plus a unique ThoughtID seems sufficient, and more compact .. why the name?

For example, the following will always resolve regardless of what I stuff here at the end:
brain://api.thebrain.com/wTtuWGVjiOPJZVtbpZcnCw/pIpnAD-iMEy0dgMjrIvUsA/ThisDoesn'tMatter#!@
Quote
smieg
eeik,

I did some testing and I believe your are correct.  Appending the Though name seems unnecessary.  I must have been trying to replicate the results of the Copy Local Thought URL function.

Andy
Quote
eeik
@smieg, your code nestles brilliantly into a proof of concept compendium app (python, django, elasticsearch, neo4j, sqlite) that I'm exploring in my spare time at the moment ... this just got way more fun! 👍

Now I just need those pesky relationship entities to be linkable (see my previous post above for link) ... and oddly now I've also discovered that TheBrain's embedded browser doesn't actually support these "brain://api.thebrain..." structured links for some reason (#69350 logged).Recent Thoughts.png
Quote
smieg
Very cool @eeik.  I am glad you can make use of my code.  I know that this is unsupported by TheBrain folks but in the absence of an API, I do connect (read only) to my mega-brain using SQL from Excel and QGIS to generate reports and map my mega-brain spatially.  Hence the Python Thought URL code above to link back to TheBrain from QGIS.

Andy
Quote

Newsletter Signup  Newsletter        Visit TheBrain Blog   Blog       Follow us on Twitter   Twitter       Like Us on Facebook   Facebook         Circle Us on Google+  Google         Watch Us on Youtube  YouTube       

TheBrain Mind Map & Mindmapping Software     Download TheBrain Mind Mapping Software