objective-capplescriptinstagramapplescript-objc

Trying to translate Object-C into Applescriptobjc for instagram post finder


So I have this Objective-C code it does something that I had been trying to wrap my head around with plain Applescript, and also tried and failed with some python that I tried (and failed at). I'd post the Applescript I have already tried, but it is essentially worthless. So I am turning to the AppleScript/ASOBJC gurus here to help with a solution. The code is to reverse engineer an instagram media ID to a post ID (so if you have a photo that you know is from IG you can find the post ID for that photo).

-(NSString *) getInstagramPostId:(NSString *)mediaId {
NSString *postId = @"";
@try {
    NSArray *myArray = [mediaId componentsSeparatedByString:@"_"];
    NSString *longValue = [NSString stringWithFormat:@"%@",myArray[0]];
    long itemId = [longValue longLongValue];
    NSString *alphabet = @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
    while (itemId > 0) {
        long remainder = (itemId % 64);
        itemId = (itemId - remainder) / 64;
        unsigned char charToUse = [alphabet characterAtIndex:(int)remainder];
        postId = [NSString stringWithFormat:@"%c%@",charToUse , postId];
    }
} @catch(NSException *exception) {
    NSLog(@"%@",exception);
}
return postId;}

The code above comes from an answer on another SO question, which can be found here: Link

I realize it is probably asking a lot but I suck at math so I don't really "get" this code, which is probably why I can't translate it to some form of Applescript myself! Hopefully I will learn something in this process.

Here is an example of the media ID the code is looking for: 45381714_262040461144618_1442077673155810739_n.jpg And here is the post ID that the code above is supposed to translate into BqvS62JHYH3

A lot of the research that went into these "calculators" is from this post from 5 years ago. It looks like the 18 digit to 10 digit ratio that they point out in the post is now an 11 to 19 ratio. I tried to test the code in Xcode but got an build error when I attempted to run it. Given that I am an Xcode n00b that is not surprising.

Thanks for your help with this!


Solution

  • Here's an (almost) "word-for-word" translation of your Objective-C code into ASObjC:

    use framework "Foundation"
    use scripting additions
    
    on InstagramPostIdFromMediaId:mediaId
        local mediaId
    
        set postId to ""
        set mediaId to my (NSString's stringWithString:mediaId)
        set myArray to mediaId's componentsSeparatedByString:"_"
        set longValue to my NSString's stringWithFormat_("%@", myArray's firstObject())
        set itemId to longValue's longLongValue()
        set alphabet to my (NSString's stringWithString:(("ABCDEFGHIJKLMNOPQRSTUVWXYZ" & ¬
            "abcdefghijklmnopqrstuvwxyz0123456789-_")))
    
        repeat while (itemId > 0)
            set remainder to itemId mod 64
            set itemId to itemId div 64
            set unichar to (alphabet's characterAtIndex:remainder) as small integer
            set postId to character id unichar & postId
        end repeat
    
        return postId
    end InstagramPostIdFromMediaId:
    

    By "almost", I mean that every Objective-C method utilised in the original script has been utilised by an equivalent call to the same Objective-C method by way of the ASObjC bridge, with two exceptions. I also made a trivial edit of a mathematical nature to one of the lines. Therefore, in total, I made three operational changes, two of these technically being functional changes but which end up to yielding identical results:

    1. to replace (itemId - remainder) / 64 with itemId div 64

      • The AppleScript div command performs integer division, which is where a number given by regular division is truncated to remove everything after the decimal point. This is mathematically identical to what is being done when the remainder is subtracted from itemId before performing regular dividing.
    2. to avoid the instance where stringWithFormat: is used to translate a unicode character index to a string representation

      • NSString objects store strings as a series of UTF-16 code points, and characterAtIndex: will retrieve a particular code point from a string, e.g. 0x0041, which refers to the character "A". stringWithFormat: uses the %c format specifier to translate an 8-bit unsigned integer (i.e. those in the range 0x0000 to 0x00FF) into its character value. AppleScript bungles this up, although I'm uncertain how or why this presents a problem. Unwrapping the value returned by charactertAtIndex: yields an opaque raw AppleScript data object that, for example, looks like «data ushr4100». This can happily be coerced into a small integer type, correctly returning the number 65 in denary. Therefore, whatever goes wrong is likely something stringWithFormat: is doing, so I used AppleScript's character id ... function to perform the same operation that stringWithFormat: was intended to do.
    3. myArray[0] was replaced with myArray's firstObject()

      • Both of these are used in Objective-C to retrieve the first element in an array. myArray[0] is the very familiar C syntax that can happily be used in native Objective-C programming, but is not available to AppleScript. firstObject is an Objective-C method wrapping the underlying function and making it accessible for use in any Objective-C context, but also likely performs some additional checks to make it suitably safe to use without too much thought. As far as we're concerned in the AppleScript context, the result is identical.

    With all that being said, supplying a mediaId of "45381714_262040461144618_1442077673155810739_n.jpg" to our new ASObjC handler gives this result:

    "CtHhS"
    

    rather than what you stated as the expected result, namely "BqvS62JHYH3". However, it's easy to see why. Both scripts are splitting the mediaId into components ("text items") at every occurrence of an underscore. Then only the first of these goes on to be used by either script to determine the postId. With the given mediaId above, the first text item is "45381714", which is far too short to be valid for our needs, hence the short length of the erroneous result above. The second text item is only 15 digits (characters) long so, too, is not viable. The third text item is 19 characters long, which is of the correct length.

    Therefore, I replaced firstObject() in the script with item 3. As you can guess, instead of retrieving the first item from the array of text items (components) stored in myArray, it retrieves the third, namely "1442077673155810739". This produces the following result:

    "BQDSgDW-VYA"
    

    Similar, but not the identical to what you were expecting.


    For now, I'll leave this with you. At this point, I would usually have compared this with your own previous attempts, but you said they were "worthless" so I'm assuming that this at least provides you with a piece of translated code that works in so far as it performs the same operations as its Objective-C counterpart. If you tell us what the nature of the actual hurdles you were facing are, that potentially lets me or someone else help further.

    But since I can say with confidence that these two scripts are doing the same thing, then if the original is producing a different output with identical input, then that tells us that the data must be mutating at some point during its processing. Given that we are dealing with a number with an order of magnitude of 10¹⁹, I think it's very likely that the error is a result of floating-point precision. AppleScript stores any integers with absolute value up to and including 536870911 as type class integer, and anything exceeding this as type class real (floating point), so will be subject to floating-point errors.