iSpective

Igor on tech and life

Podcast generator / getid3 issue with php 7.1

Yesterday my belowed Podcast generator (where I'm hosting private audio recordings to listen to conveniently) had an issue. After uploading an episode its web interface was stuck at a blank white page and the episode was never added (though the file was actually uploaded).

ID3

After looking into nginx's error logs (nano /var/log/nginx/error.log on Ubuntu) I found this PHP Error:
Fatal error: Uncaught Error: [] operator not supported for strings in getid3/module.audio.mp3.php(1088): getid3_mp3->decodeMPEGaudioHeader

Previously, running all the same on PHP 7.0 had no issues whatsoever. But when I moved the podcast generator to another server, it had PHP 7.1 and that what caused the issue.

Thankfully I was able to overcome it by updating all the files in the getid3/ folder of the web app with a newer version of getid3 (responsible for generating id3 tags from audiofiles). So if you have the problem with podcast generator or getid3 in any other app, now you know how to fix it πŸ™‚

Tracking AVPlayer starting stream playback

If you ever worked with streaming audio on iOS you definitely used AVAudioPlayer or AVPlayer as one of the low-hanging and robust options provided by Apple.

AVPlayer

Outside of the stellar performance and versatility the problem with both of them is a weak delegation system, especially for medium/pro use. It's true that you can go far with AVPlayer and even use its basic status changes to react to its behavior but they have few limitations:

  1. You actually have to work with them via KVO (Key-Value Observation) which is not ideal
  2. KVO still doesn't give you answers to everything you need

I had this basic need for a while: do some stuff in the app once the AVPlayer starts playing music from a stream. KVO'ing 'status', 'rate' and 'currentItem.status' was advised everywhere but none of them worked properly. And relying on AVPlayer's 'readyToPlay' status was incorrect as well - the player changes its status to it as soon as possible and not when actually starting playing the stream.

Thankfully I got to know about another key to look after, the 'currentItem.loadedTimeRanges'. And here's how you use it:

First, you add the observer (and then don't forget to remove it at the end of the player's lifecycle!):

avPlayer.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)

And then you just observe it!

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if object as? AVPlayer === avPlayer && keyPath == "currentItem.loadedTimeRanges" {
            if let timeRanges = avPlayer.currentItem?.loadedTimeRanges, timeRanges.count > 0 {
                let timeRange = timeRanges.first as! CMTimeRange
                let currentBufferDuration = CMTimeGetSeconds(CMTimeAdd(timeRange.start, timeRange.duration))
                let duration = avPlayer.currentItem!.asset.duration
                let seconds = CMTimeGetSeconds(duration)
                
                if currentBufferDuration > 2 || currentBufferDuration == seconds {
                    delegate.startedPlayingStream()
                }
                
            }
}

Next the player calls its delegate when the stream actually started playing. Neat! πŸ™‚

How to search in Firebase

In case you've been wondering (I was) how to seach for stuff in Firebase's Cloud Firestore I found it randomly today and would like to share it:

Firebase-search

How to increase upload file size limit

I'm using a self-hosted podcast web app (PodcastGenerator) to serve and listen audiobooks in Overcast with Smart Speed and position tracking. In order to get new books in my podcast feed I upload them via the built-in web admin panel. But since the audiobook files can be as large as 200-300MB, uploading them via a browser hits the default upload file limit of both Nginx (my http server) and PHP (which my podcast web app uses) pretty soon.

Increase-upload-file-size-limit

In order to increase the upload file size limit, do this:

  1. Edit Nginx's config
    sudo nano /etc/nginx/nginx.conf
    and add client_max_body_size 2000M; into the 'http' block
    Save and exit

  2. Edit php's config
    sudo nano /etc/php/7.1/fpm/php.ini (or track down your php.ini file by creating a *.php file with contents and run it)
    Find and change upload_max_filesize and post_max_size as well to = 2000M

  3. Finally restart Nginx (or your http server) via
    service nginx reload
    and also you can restart php to be safe via
    service php7.0-fpm restart

All my commands are meant for Ubuntu I'm running, for your OS you'll have use your own alternatives.

Happy uploading! πŸ™‚

It's not worth securing in-app purchases

Few years ago, while working on one iOS app we had a problem with in-app piracy. At that time there were and now there are several ways of keeping track of users' purchases on your own server and validate each of them to stop pirates of using your app's unlockable features with their faked unauthorized purchases.

In-apps

But I remember it was a hassle to build such a validating system. And I also remember reading about even bigger companies discontining their own billing systems like that because it was not worth the hassle for dealing and supporting 95% fair users and their purchases because of 5% scammers.

In order to hack in-apps, your iPhone has to be jailbroken. Then you can use a manual like this to get an in-app for free. The latest info on the amount of jailbroken iPhones I found was from the end of 2016 with only 0.4% of iOS devices being jailbroken which might have the potential to hack anything. From what I’ve read and heard over the years that percentage may be even lower now as less and less people jailbreak, because the main reason for it (I can confirm myself) was to get new functionality. And with the latest few iOS updates we got so much new stuff, there are almost no reasons left to jailbreak, at least not for the new features.

iOS devs still can pass receipts and validate them on servers, but what would be the main reason for it? Receipts are passed and then matched for non-consumable purchases: the user unlocked a part of the functionality once and then when the user restores it, his receipt is matched with the receipt stored on the server to avoid unauthorized functionality unlocks. So if someone can fake unlocking non-consumables you either can build a wall around it... or just give it away. If a person tries to hack your purchase - he's definitely not going to buy it, so just embrace it! Replace a mandatory in-app with free with ads model, or offer even more compelling features in your app and introduce non-hackable subscriptions!

If you sell consumables - even better, there's even less reason to secure them. A person who buys a consumable uses it right away, essentially not leaving much to himself anyway.

As for Android - that’s another story. On Android I found at least one, two and three articles on ways how to hack in apps even without root (analog of jailbreaking).

Fix AirPrint -9923 scanner error on MacOS Mojave

After upgrading to MacOS Mojave last autumn I actually had no problem using my Brother DCP-L2532DW via AirPrint. But everything changed when 10.14.3 rolled out few weeks ago.

AirPrint

My Mac did see the printer on the network but when I tried to scan it gave me the -9923 error and obviously nothing happened afterwards.

The solution was actually simple: to turn off IPv6 in my printer's settings, switching it to IPv4 only. If your printer doesn't have settings, try adjusting your router's settings to disable IPv6 and have each wireless devices (including the printer) to obtain an IPv4-only IP address.

Now I wonder when IPv6 becomes reliable enough for regular household items πŸ™‚

How to merge mp3s on Mac

In case you have a bunch of mp3 files (chapters of an audiobook for example) which you want to merge and keep or listen as a single file here's a quick hint for you:

  1. First you open Terminal and cd to the folder with files
  2. Then you just type `cat file1.mp3 file2.mp3 file3.mp3 > outputfile.mp3' and hit Enter!

Merge-mp3

You will get a big mp3 file which unfortunately will have an incorrect duration from the first file of the batch. But you can easily fix that with ffmpeg or other utilities. I prefer a small GUI apps without installing any other libraries. My app of choice is the MP3 Scan + Repair App. You just open the merged mp3 file with it and push the wrench/fix button. That's about it!

Apple's March Keynote Summary

What has been announced:

Apple News+ - $9.99 subscription for accessing 300+ paper-to-digital magazines, including LA Times and WSJ.

Apple-March-Keynote

Apple Card - virtual credit card with real daily cash cash-backs and a physical credit card made out of titanium 😍 They are actually having nice promises about the low transaction and international (really interesting) fees.

Apple Arcade - gaming subscription with 100+ iOS/Mac/AppleTV exclusive games. Details and pricing info will be later.

Apple TV - new app, with better suggestions, integrations with all the content providers. Soon available on smart TVs as well.

Apple TV+ ad-free, on demand subscription with unique and exclusive content. More details soon.

Personally I was waiting for better iCloud Storage options πŸ™‚ But overall it was a good and very (next level) emotional keynote of all. The guests made the second half of the keynote very heart-warming.