taw's blog

The best kittens, technology, and video games blog in the world.

Friday, March 16, 2018

Visual Studio Code first impressions

le petit chat by FranekN from flickr (CC-NC-ND)

This is a first impressions post, so not representative of editor in any serious use. Maybe it gets a lot better, maybe it gets a lot worse.

It's very important to try a lot of technologies, even if in the end, they don't matter at all. For example zsh has about one feature (multiline copy&paste support) which properly configured bash doesn't, so it's not enough to recommend it to anyone, but I kept using it after the trial.

Research stage

I tried searching the internet to figure out what's the difference between VSCode and Atom, and nobody has any good answers. Most of them were of "I already use Atom package for that" variety, or claims about performance. Atom isn't super fast, but it got a lot better since its early days. Or maybe computers got a lot faster.

Getting started

First check - Cmd-D multiple cursor selection works. OK, I might be able to use it. Any editor without it doesn't deserve a serious look. And just in case, some quick editing shortcut check - nothing is too crazy.

Second check - how do I add it to $PATH... I thought it would be in settings, but nope. Settings page wasn't very intuitive, but it actually makes a lot of sense once I got through the unusual interface. Well, this wasn't there.

I had to google that, and there were like a million irrelevant and highly convoluted answers. It turns out that correct answer is to turn on command palette with Shift-Cmd-P, and choose "Install 'code' command in PATH". Someone please tell Stack Overflow about that, it has ridiculous answers from 2015.

By the way, that's a common problem with Stack Overflow - the best answer from a few years back might be a pretty questionable today as yesterday's problems get addressed in new versions - but the answer never gets fixed, and anybody who dares ask again will get it closed as a duplicate and banned from Stack Overflow for life or something.

Maybe there should be a way to "flag as no longer best practice" or whatever.

Switching Tabs

So I opened a toy ruby repo, typed some code, and the level of git integration was pretty sweet. Staging chunks from within editor, that's definitely a nice feature.

Then I opened a second tab, and my first impressions went from 5 stars to 3 stars. Standard tab switching shortcuts like Cmd-1 Ctrl+PageDown etc. just don't work.

All right, checking the annoyingly named Extension "Marketplace", there's "Atom Keymap", that fixed it.

RSpec runner

I normally split the big screen between iTerm2 on the left and Atom on the right, so I don't usually care for in-editor runners. But sometimes I have to code on just unconnected laptop, and then it's a bit awkward.

I've heard some rumors that VSCode is good with in-editor test runner and terminal, so I checked if there's any RSpec runner. After a few tries I found "Rails Run Specs", which works for pure Ruby project, kinda.

When I right click on spec file from the file list on left sidebar, I can run the specs. But when I try to do so from command palette it just gives me "running the contributed command failed" message.

To be fair, I haven't actually checked if Atom does it any better.

So far it seems about the same

For now VSCode looks pretty much like Atom with fewer plugins. I don't actively hate it, the way I quickly hated RubyMine, Android Studio, IntelliJ IDEA, Eclipse, and a lot of such crap which never bothered to get basic usability right and just piled features on top of a really shitty base editor.

The base of VSCode is just fine. I'm just not really seeing what's the big benefit yet, but I could see using it for a bit to maybe find some.

Saturday, March 03, 2018

Volume slider's 100% limit is a massive usability failure

Headphone Kitty by Super Formosa from flickr (CC-SA)

We live in a world where all software sucks. I'm not talking about edge cases of some rarely used functionality, or compromises made for sake of backwards compatibility, or due to greed or other external constraints - we don't even get the basics right when nothing stops us.

This one is especially egregious because it affects almost all software.

Here's the problem - volume slider only going up to whatever the software considers "100%"

100% is a lie

100% means nothing. What's meaningful in audio is relative amplitude of various segments. All those ups and downs thousands of times per second are what makes the sound.

The same sound can be played at any volume you want, and it will sound just fine.

For technical reasons audio files, and that's true for both analog and digital, can be "louder" or "quieter". Does this level mean anything? No at all. The happenstance that a certain file goes from -1000 to +1000 or from -2000 to +2000 means absolutely nothing wrt best loudness to play it at.

What happened is that software decided that 1000 corresponds to some loudness at "100% volume", 2000 corresponds to some different higher loudness, and you can't do anything about it. Oh sure, you can make it quieter, but that's all.

Software doesn't even know how that 1000 translates to in any human units. It just sends some voltages to the speakers, and lets the speakers figure it out.

This usability failure was sort of tolerable back in the desktop era, because most desktop speakers have far more power than you'd normally use, so if you normally listen at 30%, then you can just bump it to 60% if software failure sends something particularly quiet to the speakers.

Unfortunately this is not true for shitty laptop speakers or shitty portable earphones. "100%" volume, very low power, and quiet audio, and it's barely possible to hear anything. Then add a crowded train to the mix...

Hall of Shame

  • Apple - 100% guilty
  • Windows - 100% guilty
  • Android - 100% guilty
  • Youtube - guilty (they at least apply server-side audio normalization for music videos, so they're less affected, but all other content has this problem)
  • VLC - desktop version amazingly goes to 200%, but mobile version (which needs it a lot more) just as guilty anyway

Dynamic range adjustment

For a bonus related failure, there's a problem of dynamic range. Movie tend to have much louder (usually action) and much quieter (usually dialogue) parts - which is perfectly fine when watching in otherwise quiet cinema.

Unfortunately at home your choices are:
  • only watch with headphones, alone
  • adjust volume up and down a lot
  • watch it at high volume so you can actually hear the dialogue, and screw the neighbours
  • watch it at low volume so you your neighbours don't complain during action sections, and use subtitles to not miss anything in the the dialogue
  • have software which flattens dynamic range to more reasonable values
At least in this case the excuse is that this kind of adjustment is not quite as trivial as bumping the volume to 100%.

Saturday, February 24, 2018

Opal - Ruby on Javascript VM - What's still missing

Freya by CarlosDemarte from flickr (CC-NC-ND)

Javascript VMs are everywhere, and environments running them such as browsers and phones look likely to remain the dominant way for users to interact with software for very long time. This led to explosion of popularity of Javascript - the native language of that environment.

But Javascript is a dreadful language. It started a dirty hack, which just so happened to be positioned right and benefited from browsers becoming universal user interface. It can't even ever get properly fixed like let's say Python 1, due to unique backwards compatibility cost.

Fortunately just as you don't need to write Java to run on JVM, or write x86 assembly to run on x86 hardware, it should in principle be possible to run on Javascript VM without writing any Javascript.

So far it's been mostly "Javascript++" style languages like CoffeeScript, JSX, TypeScript, transpiling to older versions of JS and so on. It's not nothing, but all of them are trying to make Javascript at best barely tolerable.

So why not just write in a real language, and compile it to whatever Javascript VM wants?

Opal attempts to do just such a thing, letting you run Ruby in a browser. It's a somewhat different dialect of Ruby, most notably with immutable Strings, Symbol/String unifications, single numeric floating-point type, and a few other compromises to make it run better on Javascript VM, but it's mostly good enough.

Unfortunately I don't think it's ready for serious use just yet. Here's the top 10 things it's missing, in roughly the order of importance.

Out of the box sourcemaps

When an exception happens, the most important information is where it's coming from, and the second most important is the entire stack trace. Exception type and message are usually not terribly informative anyway. With Opal, you get none of that, just some pointer to compiled code which looks nothing like what you wrote.

It's sort of true that it's also a problem with CoffeeScript, or JSX, but with such languages you can usually figure out what's up from compiled Javascript with some practice. Opal to JS mapping is too big of a change, and lack of source maps makes debugging extremely tedious.

Opal absolutely needs sourcemaps, out of the box, in every environment. That's the single barrier to productive use.

Decent test framework

Opal has opal-rspec and it's just not good enough. It doesn't work with latest version, doesn't give any meaningful information why a test failed, doesn't have any mode other than "run all tests", and is weirdly slow on top of it all.

This isn't even all that surprising, as Javascript testing is decade behind Ruby, and sourcemap issue on top of that makes it even worse. It's a huge productivity killer.


One of best features of ruby is binding.pry. For that matter one of the best features of Javascript is debugger, which is basically less powerful version of binding.pry.

With Opal, you get none of that. You can use `debugger` and try to figure out things from compiled Javascript code, but that's not exactly a pleasant experience.

Support for Ruby regular expressions

Opal doesn't support Ruby regular expressions, just throws whatever you write onto JS regexp engine, and it's not even close to good enough. Even something is simple as /\A\d+\z/ will completely fail - and even worse it will fail quietly without raising an exception.

Of course you can write JS regexps, but then you lose any hope of sharing same codebase between regular Ruby and Opal Ruby.

As an absolute minimum it should raise a damn exception when it sees something it can't deal with.

Support for Time.parse

Another thing which turned out to be far more annoying than expected is dealing with timestamps. Ruby has Time.parse, which is pretty decent at handling most commonly used timestamp formats. Opal will instead happily use raw Javascript constructor, and quietly give you NaN timestamp object.

Like with previous point, the absolute minimum is to raise a damn exception here, but something resembling Ruby's Time.parse would go a long way towards making Opal useful.


As followup to some previous points, something that lets you jump straight into pry on unhandled exception would be pretty damn good. Especially if it also worked in the test suite.

Precompilation of libraries for better start time

Ruby doesn't have separate compile phase - it just executes code on a fresh VM, and that code setups various methods and classes via metaprogramming. Then it runs the application itself.

That works fine on Ruby VM, but most environments perform really poorly in this mode. Startup time of Ruby on JVM with JRuby or Javascript VM with Opal is thus fairly bad.

This is probably the most difficult thing to do, but right now any Opal code using a medium-sized wrapper like opal-d3 will have a lot worse startup time than native javascript version.
Once it starts, overhead isn't too bad, but this really needs to be solved for production use.

Better interoperability with plain Javascript objects

This is another point about debugging, but let's say I get a Javascript object somewhere, and I want to figure out what the hell it is, or just dump it to the console. Currently it's going to crash with a helpful message telling me that Javascript objects don't implement inspect method. Well, true, but it's awful debugging experience, as realistically we'll be mixing Opal Ruby code with native Javascript libraries in most use cases.

A site like codepen.io

One of the nicest things about frontend coding is that it's possible to quickly code something in the browser, without going through all the hassle of setting up local environment, and share that with others. There's a lot of sites like codepen.io which help with this low-overhead quick coding.

Opal will definitely need a site like that, or be supported by one of them.

A Killer App

And finally we get to the usual "Killer App" point. I put it last intentionally - because unless most of the previous points are addressed, there's no killer app which could possibly work.

Saturday, February 17, 2018

Review of Star Trek: The Orville

It's usually not worth writing reviews of works with a lot of existing reviews. Either you agree with the consensus, and so provide very little information beyond what's already there, or you disagree, and the review probably says more about your taste than about the subject.

Lack of consensus

Not this time. There's no consensus at all. Critics absolutely hate The Orville:
While the audience loves it:
This is extremely unusual. Audiences and critics usually agree very closely.

Meanwhile, Star Trek: Discovery has far higher critics score:
and very mediocre audience score:

So what's the deal here?

It's simple, the critics are total idiots, and the audience is completely right. Star Trek: The Orville is awesome.

The review

The Orville is 90% classic Star Trek and 10% Family Guy. This combination is just too sophisticated for the stupid critics, but it works really well.

Star Trek was always a very optimistic and lighthearted world. Even its "dark" parts like DS9 would look like a comedy by standard of today's grimdark TV. Dark moments like In the Pale Moonlight were only so effective because they were used to sparingly against the backdrop of endless Ferengi get rich schemes, Odo and Quark's cat and mouse games, shenanigans by the station's kids, Kai Winn's pettiness, and so many other ways which made the future look so much brighter than world of today.

And it couldn't have worked any other way. Star Trek has to be bright for moral dilemmas to be engaging. If the whole world is a grimdark shithole, moral issues become irrelevant, and only survival matters.

The Orville continues doing what Star Trek was great at. Its universe, while technically not in Star Trek canon, is just like it - full of imperfect people still trying to be their best. They explore the universe, run into interesting aliens of the week, and seriously deal with new moral dilemmas just like in previous Star Trek series. It feels like it's actually doing a better job, with choices characters make having far more serious consequences, instead of being promptly forgotten by the next episode.

Sure, previous Star Trek series didn't employ Family Guy style humor, but 80s'/90s' sensibilities wouldn't work on today's TV, and this change is far less drastic than turning Star Trek into another grimdark series like everything else.

It's a must-watch for every Star Trek lover. It's still trying to perfect its formula, but it might very well end up as the best Star Trek series ever.

The bonus review of Star Trek: Discovery

To be fair, I only watched the first episode, but that should be quite telling as I watched every other Star Trek series and movies, many multiple times.

Discovery is simply not Star Trek. Maybe I'd have liked it if it didn't pretend to be one, but it's too late now.

It's just ridiculously grimdark. The characters, the antagonists, the plot, everything on the screen is just ridiculously dark. It hurts to even look at the screen. There's not one scene in the whole episode where the lights are properly on - it's all dark greys and blacks everywhere.

It's so ridiculously Not Star Trek that there's a damn mutiny in the first episode, by the first officer who wants to start a war with the "Klingons"!

As if that's not enough, for some reason it ditches Star Trek races - Discovery "Klingons" look and act nothing like Star Trek Klingons, there's a few Vulcans in the background, but they decided to ignore whole Star Trek Vulcan canon anyway, and there's some humans and some weird new types nobody cares for.

As far as I can tell - and maybe that changes later - it's doesn't even follow the crew ensemble formula, instead focusing hard on single character, who's a woman most annoyingly named Michael.

So why does it even call itself Star Trek? That sets up expectations it's completely unwilling to meet. Perhaps it could be a decent space show in its unique universe, instead it decided to steal the name and then do something completely unrelated with it.

There was nothing redeeming about the whole episode, and I doubt very much it gets any better later.

Just don't bother watching it.

Saturday, February 10, 2018

Let's Play XCOM: Enemy Unknown

It's time for another classic game - XCOM: Enemy Unknown!

The Long War mod would make it last about 200-300 episodes instead of more reasonable number like 40 or so, and let's be honest - there's no way in hell I'd be able to finish it. Even people with a lot more time than me left a lot of Long War let's plays incomplete.

I got a bunch of minor mods, two most interesting are:

  • Tweaking perk tree, and perk pool for training roulette. This mostly fixes Snipers by making Squadsight the first perk, and delaying Headshot until major-tier perk. It also makes Supports into reliable medics by removing medic skills from the pool.
  • Making alien line of sight a bit shorter to occasionally enable ambushes. This could probably be cheesed by the kind of people who like counting tiles, but I never felt like doing so.
The rest is largely UI. Playing on Classic difficulty, so there should be some challenge, especially since I probably forgot everything about the game by now.

The let's play is almost exclusively battles, with base management in between cut out, as you probably don't want to see me alt-tab to spreadsheet and wiki and counting how much power I need to build when and how many corpses I need to sell on grey market to get necessary space bucks for foundry upgrades.

Here's the first episode:

As usual, episodes coming out once a day, same time of the day, until we win or aliens do.

Monday, January 29, 2018

London cycle hire adventures

Lilith on bike seat by catmom42 from flickr (CC-NC-ND)
Today I felt like coming home with a cycle hire (commonly known as "Boris Bikes", officially now "Santanders Cycles" after a rebrand).

I went to a nearest dock, which the app claimed to be in perfectly fine order, and tried to get a bike. Error. Tried a few more times just in case, still error. I thought that's just app error, so I tried to use the terminal instead - which is really painful as it involves clicking same stupid confirmation dialogs about 25 times before it lets you rent the damn bike - but it was just as broken as the app.

Could the app somehow mark the dock as not working? Right...

So I walked to the next dock, that somehow wasn't broken, so I got a bike, cycled to Aldgate, and from there to the end of pompously named "Cycle Superhighway 2" at Stratford.

Stratford is a traffic hell, with no docks near the "Cycle Superhighway 2" or the station. The app claimed the nearest two docks had zero spaces left.

There was one about 10 minutes away from the station which claimed to have 4 free spaces. I went there - wasting tons of time going in circles because direct route is not possible anywhere near Stratford - and there was only one space, which didn't take the bike in spite of repeated attempts, for whichever reason. First it flashed red, then it just gave up.

After a lot of loud cursing, I had to cycle to the next dock, which fortunately somehow had free spaces, probably because it was really damn far from the station.

The cycling itself was fine, just everything else about the experience was totally miserable, and of course they charged me extra for wasting my time due to broken docking stations.

Which is pretty much what I learned to expect from the TfL. Unionized 💩💩💩.

I really hope some competition with dockless bikes comes to London. Right now none of these new schemes allow realistic commuting between Central London and zones 2/3, but there's some hope for the future.

Tuesday, January 23, 2018

Let's Play Civilization 5 as Rome

It's been a long while since I last recorded any Let's Plays, so here's a new one!

It turns out not everybody switched to Civilization 6, and in fact new mods for Civilization 5 keep coming out. This campaign features one of them - 5th Unique Component. Other mods just as before.

As Rome, our unique abilities are:

  • 25% faster production in other cities of every building which is already built in our capital
  • Ballista - unique Catapult replacement - slightly stronger
  • Legion - unique Swordsman replacement - slightly stronger, can build roads and forts
  • Forum - unique Market replacement - +10% Great Person
  • Thermae - unique Garden replacement - +1 Science, +1 Culture, +1 Food.
  • Aedes - unique Temple replacement - +1 Global Happiness, cheaper to build, costs no maintenance, and doesn't require a Shrine.
Overall these feel like mid-tier or a bit below abilities. Faster production scales with number of cities, and Aedes gives us a bit of happiness breathing room, so together they support a bit of expansion. Unique units come too early and are of wrong kind to have much impact, early wars are dominated by Composite Bowmen supported by Horsemen.

I'd definitely love to hear feedback, especially technical feedback - I'm recording at higher resolution, and I'm not totally sure if I set OBS correctly for it.

One video a day, full playlist going to be here.

Saturday, January 20, 2018

New Hash methods in Ruby 2.5 and hash-polyfill

The Cat by marcinlachowicz.com from flickr (CC-NC-ND)

Ruby 2.5 includes a bunch of new Hash methods:
  • Hash#slice
  • Hash#transform_keys
  • Hash#transform_keys!
The first two do exactly what you'd expect them - and the same ActiveSupport's methods with the same names do. In case of key conflict Hash#transform_keys will quietly overwrite keys, which is somewhat questionable behaviour, but it's not like there's obvious better way.

Unfortunately Hash#transform_keys! took some shortcuts, resulting in rather questionable behaviour. I submitted it as a bug report, and hope they fix it soon, but to be honest track record of my Open Source bug submissions is rather poor.

I'm really surprised Hash#compact wasn't included.

If you want to use these new methods in older Ruby versions, or if you want to use methods from future rubies like Hash#compact, Hash#select_values, Hash#select_keys etc., I updated hash-polyfill gem too.

I did not include Hash#transform_keys! in the gem as its unclear if it will have corrected or current questionable behaviour long term.

Tuesday, December 19, 2017

Challenges for November 2017 SecTalks London

Christmas Luke by Nicholas Erwin from flickr (CC-NC-ND)

Following highly successful September round of London SecTalks, I ran another round in November.

The round consisted of 8 tasks, and they were a bit harder this time, with even the winner only finishing 7 in time - a few people completing the challenges only after time.

You can find challenges and code used to generate them in this spoiler-free repository.

This post doesn't contain answers, but it might spoil a bit.

Archive (5 points)

It was nearly identical to previous round's archive challenge - 16-level nested archive, with 1 real and 15 fake archives on every level. The only difference was that distraction files were 0-padded to have same size as the real file, which forced smarter strategy than simply going for the largest file every level.

Of course MD5ing to find unique file, or just unpacking them all and removing duplicate files still worked.

CSS (10 points)

The password was encoded within CSS rules. I've never seen this kind of challenge anywhere, so maybe it's the world's first?

It was very short, and every character was independent, so it seems that everyone just manually brute forced it.

Secret Message (15 points)

The answer was written in one color on background of another extremely similar color. Everybody managed to finish it so quickly, I didn't even have a chance to see what kind of tools they used to solve it.

EDIT: Oops, it seems that I messed up ImageMagick options and also accidentally left the answer in EXIF.

Python (20 points)

As we all know Python is a whitespace-sensitive language. So I encoded some secrets in the whitespace.

Quite a few people used editors which cleaned up whitespace automatically, messing up with the file. Once a person figured out what the challenge is about, it wasn't usually too hard to solve it.

Ruby (25 points)

Obfuscated Ruby challenge was the hardest one of the round. It used two layers of Unicode obfuscation, first with emoji, and then with CJK characters. Other than using unusual characters, obfuscations applied weren't particularly hard.

ECB BMP (30 points)

This was a fun one. It was basically a version of the famous ECB penguin from Wikipedia.

People had a lot of trouble figuring out dimensions and bit depth of the image, which had to be given as a hint, even thought they were fairly usual.

XOR GIF (35 points)

This was a two step challenge. A GIF file was xor-encrypted with a word from a dictionary.

The challenge was then to find out which Twitter account the image is from.

Since GIF header is known, it was very easy to figure out the first few letters of the key. However, people had a lot of trouble completing it, as the word I've chosen was only in some dictionaries. This wasn't intentional.

After getting the image, it turns out only some reverse image search could find it properly, and others returned bogus matches.

ROT Word (40 points)

I wanted to have a task for statistical analysis of some classical cipher, but all the real ones have online tools you can use to solve them in a few seconds.

So I made up one - it's like rot cipher with multi-letter key, except each letter is used for whole word, not for one letter.

encrypt("All your base are belong to us!", "omg") == "ozz kagd hgyk ofs nqxazs zu ig"

For a bit of extra challenge the message was in English, but contained a bunch of non-English proper names.

Final Thoughts

I made this one just a bit harder, and maybe it was a tiny bit too much.

Overall, a lot of fun happened.

I'd definitely recommend CTFd server for this.

Tuesday, November 28, 2017

How to watch high speed let's plays on London Underground

le petit chat by FranekN from flickr (CC-NC-ND)

Apparently the idea that some places - like London trains - are offline - never occurred to anyone in California or Seattle or wherever people who write mobile software tend to live. And even support for high speed playback is not quite as common as it should be.

So I came up with a process to deal with it - which even with all the scripts to automate it still has far too many steps. I'm not saying I recommend it to anyone, but maybe someone needs to do something similar, and they might use it as a starting point.

Download let's plays

First, let's find a bunch of let's plays we want to watch, it's best to use playlists instead of individual videos to reduce URL copy and pasting time, but it works for both.

To download them we can use youtube-dl, which is available as a homebrew package (brew install youtube-dl), or you can get it from here.

$ youtube-dl -t -f "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best"
  "url1" "url2" "url3"

Youtube offers videos in many formats, and arguments above are what I found to result in highest quality and best compatibility with various video players. Default settings often ended up causing issues.

Speed up the videos

There's plenty of command line tools to manipulate audio and video, and they tend to have ridiculously complicated interfaces.

I wrote speedup_mp3 script (available in my unix-utilities repository) which wraps all such tools to provide easy speedup of various formats - including of video files.

The script uses ffmpeg to speedup videos - as well as sox and id3v2 to deal with audio files if you need to do so to some podcasts. All those dependencies you can satisfy with brew install ffmpeg sox id3v2.

The script can speedup a whole directory of downloaded videos at once by 2.0x factor:

$ speedup_mp3 -2.0 downloaded_videos/ fast_videos/

Adjust that number to your liking. Factors higher than 2.0 are not currently supported, as ffmpeg requires multiple speedup rounds in such case. I plan to add support for that later.

The process takes a lot of time, so it's best left overnight to do its thing.

The script already skips videos which exist in target directory, so you can add more videos to the source, run it again, and it won't redo videos it already converted.

Put them on Dropbox

Infuriatingly there doesn't seem to be any good way to just send files to Android tablet from a laptop over WiFi. There used to be some programs, but they got turned into microtransaction nonsense.

If you already use Dropbox, the easiest way is to put those sped up files there. This step is a bit awkward of course, as video files are big, people's upload speeds are often low, and free Dropbox plan is pretty small.

If that doesn't discourage you, open Dropbox app on your tablet, and use checkbox to make your files available offline. You don't need to wait for it to finish syncing, Dropbox should keep updating files as they get uploaded.

After that any video player works. I use VLC. Just open Dropbox folder, click on the video, it will open in VLC and play right away. First time you do it, make sure to set VLC as default app to avoid an extra dialog.

Isn't this ridiculously overcomplicated?

Yeah, it sort of is. Some parts of it will probably get better - for example speed controls on video/audio playback are getting more common, so you could skip that part (watching at 100% speed is of course totally silly). It still makes some sense to pre-speedup to save space and battery on the device, as faster files are proportionally smaller, but if you feel it's not worth the hassle, you can probably find a video player with appropriate functionality.

TfL showed zero interest in fixing lack of connectivity on London underground, and mobile ecosystem assumes you're always online or everything breaks, so this part will probably be a major pain point for very long time.

The part I find most embarrassing is lack of any builtin way to just send files over to a device. Hopefully this gets fixed soon.