Case studiesOTT & distribution platform

Pocket Films: the platform behind India's largest short-film distributor.

Pocket Films licenses independent short films to partner platforms and streams them on its own app. Apexnova built everything both businesses run on, from the filmmaker's upload to the viewer's play button.

The challenge

Two businesses had to run on one platform. A distributor that signs filmmakers and licenses their work to partners, field by field and territory by territory. And a consumer streaming service that sells memberships, coins, day passes and whole shows. One catalogue, one source of truth, and no room for a mistake that costs a filmmaker, a partner or a viewer real money.

What Apexnova built
  • Backend
  • Android, iOS & web apps
  • Partner distribution API
  • Coin economy & payments
  • Ad integration
  • Analytics pipeline
  • Recommendation engine
At a glance
Client
Pocket Films
Industry
Short-film distribution & OTT
Content
Short films, series, microdramas, reels
Platforms
Android, iOS, web
Stack
Python & Django on PostgreSQL, background workers; Flutter apps
Monetisation
Memberships, coins, day passes, show purchases, ads

One catalogue, four jobs

  1. Filmmakers

    Submissions

    Upload work, describe it, sign an agreement and track its status.

  2. Partner platforms

    Distribution

    Receive exactly the titles, fields and playback rights they have licensed.

  3. Viewers

    Streaming

    Watch films, series, microdramas and reels, and pay for them.

  4. The Pocket Films team

    Operations

    Review, curate, support and measure, from one admin.

Most streaming projects are an app with a catalogue behind it. Pocket Films is a supply chain.

A film arrives from the person who made it. It is reviewed, covered by a signed agreement and prepared for streaming. It is delivered to partner platforms under the exact terms each one has licensed. Then it is streamed to viewers who pay for it in several different ways.

Every link in that chain has its own rules, and a mistake in any of them costs somebody real money. Apexnova built the whole chain, from the filmmaker's upload to the viewer's play button. Here is how, one link at a time.

01

A film arrives

The chain starts with someone who has made a film and wants it seen. Their first experience of Pocket Films decides whether they trust it with their work. That experience starts at distribute.pocketfilms.in, the portal where filmmakers submit their work.

A submission is a dossier, not a file. It carries cast and crew, awards, artwork, genres, languages, certification, the territories it may be shown in and the ways it may be monetised. Films, series with seasons and episodes, microdramas and reels each have their own shape.

Uploads that survive bad connections. Masters run to gigabytes and filmmakers' internet does not always cooperate. Files go up in parts, straight to storage, so the application servers never carry them and a dropped connection never means starting again from zero.

Nothing polls. When a file lands, storage announces it on a queue. A worker picks it up, works out which title it belongs to and what it is (feature or trailer, film, episode or reel) and starts it through encoding. Files that are not video, or are already archived, are recognised and skipped.

Commercial fields need a second pair of eyes. Every submission is reviewed before it goes anywhere. Changes to territory or monetisation do not simply save: they go through approval.

Contracts, signed in the platform. Each distributed title is covered by an agreement generated and signed electronically by both sides. The platform tracks the term, the effective and expiry dates and the renewal type, and a nightly job handles renewals as agreements reach their end.

Masters go cold. Once a master has been processed, the original moves to low-cost archival storage. It is kept safe without paying streaming-grade prices for a file nobody plays.

Filmmakers can also raise support tickets and talk to the team, attachments included, without leaving the platform.

02

Every partner sees only what it licensed

Distribution is where a mistake is most expensive. Showing a partner a title it has not licensed, or letting it play one in a territory it has no rights to, is not a bug report. It is a contractual problem.

So the distribution API was built around the licence, not around the catalogue.

The default is to withhold. A partner with nothing configured receives identifiers and nothing else.

Titles are released in batches. Each batch has a start date, an end date and a status. A whole batch can be revoked, or single titles withdrawn from it.

Every field is filtered, per partner. What a partner sees about a title is configured for that partner, and every response is checked against it field by field, down to nested details like cast, crew and awards.

Playback rights are set per partner too. Whether a partner may request a playable link, for which kinds of content, and how long that link lives are all individual settings.

Territory travels with the title. Each title carries the countries it may be shown in, wherever it goes.

Partners sync changes, not catalogues. They ask for everything that changed since their last sync, withdrawals included, instead of downloading everything again.

Every playback link is on the record. Each signed link issued, to a partner, a signed-in viewer or an anonymous device, is logged with who asked and for what. The logging runs in the background so it never slows a request.

There is even a layer that cleans titles and descriptions for partners whose systems accept only a limited set of characters, so one strict downstream system cannot reject an entire delivery.

03

The app viewers actually see

All of this reaches viewers as one app: films and series in a full player, microdramas and reels in vertical feeds, search, a watchlist, ratings, offline downloads and a library of everything they have unlocked or bought.

The home screen is assembled from shelves defined on the backend, in several layouts, so the editorial team rearranges it without an app release. New viewers pick what they like on first launch, and the app remembers playback preferences title by title.

Thousands of signatures nobody needed

Every playable link is cryptographically signed and expires. The original catalogue response signed a link for every episode of every microdrama it listed.

A viewer opening the app might watch three episodes. The server was producing thousands of signatures, on every request, for episodes nobody was about to play.

Now the catalogue sends only what the app needs to draw the screen: identifiers, episode numbers, durations and subtitles. A separate endpoint signs a small window of episodes around the one being watched, and the app asks for the next window as the viewer moves on.

Signing work now follows what viewers watch, not the size of the catalogue.

The Films tab: a featured title up top and Continue Watching below.
Search with genre filters over a poster grid of the catalogue.
04

A currency, built like a ledger

Microdramas are paid for by the episode, with coins. Coins can be bought or earned, earned coins can expire, and an unlocked episode stays unlocked for good.

That is a small currency. It was built with the care a currency needs.

There is no balance stored anywhere. So there is no balance that can drift out of step.

Balances are calculated, never stored. A viewer's balance is always worked out from the coins they hold that have not been spent or expired. A stored balance has to be read, changed and written back, and with money that sequence is a bug waiting for two taps to land at once.

History is append-only. Every movement of coins is a new entry. Nothing is edited, nothing is deleted. A correction is a new entry that reverses the old one, so the history always explains the present.

Coins come in lots. Coins granted together share an expiry, and each spend records exactly which lots it drew from. That is what makes expiry, refunds and support questions answerable.

Deadlocks are impossible by design. Every operation that changes a wallet starts by locking that viewer's own record. Everything a wallet touches is reachable only through its owner, so two operations can never hold conflicting locks. The only contention is one viewer's own taps, so it can never slow down anyone else.

One function decides access. A single function answers "may this viewer watch this episode?", and every screen and endpoint asks it. A gate that says "locked" on the episode list and "unlocked" in the player is worse than no gate at all.

Entitle first, charge second. An unlock grants access and then takes the coins, inside one transaction. If anything fails, nobody is left having paid for something they cannot watch.

The whole economy can be switched on, off and tuned from the server.

The "Keep watching" sheet on a locked episode: buy coins or go premium.
05

Every payment arrives twice

Viewers can buy a membership, a pack of coins, a day pass or a whole show outright, through more than one payment channel. The payment code is organised around the ways payments really go wrong.

The order exists before the money moves. An order is recorded first and its reference travels with the payment. If the phone kills the app mid-payment and the payment succeeds, the confirmation that arrives later still finds its order. No payment is ever left belonging to nobody.

Verifying and granting are separate. One step proves the money is real. Another decides what the buyer receives. A grant can be safely re-run after a crash without charging twice, and a fault in a payment integration cannot mint coins on its own.

Repeats do nothing. The same payment genuinely arrives more than once: from the payment channel's notification, from the app checking in, and again when a notification is retried. The second and third arrivals are no-ops.

Orders are history, not state. An order is never deleted or rewritten. Everything that happens to it is appended to an audit log.

Cancellation is honest. Some memberships can be cancelled by the platform. Others were bought through an app store and can only be managed there. The app tells viewers which case they are in and sends them to the right place, instead of showing a button that cannot work.

Two smaller decisions say a lot about the build. App stores do not allow in-app prices to be set freely, so whole-show purchases use a small set of fixed price tiers that each title points to. And the legal text on the purchase page comes from the backend, because it will be reworded and should never need an app release to change.

The library then pulls both halves together. Coin-unlocked episodes and outright purchases live in different parts of the system, but viewers see one list of titles, with their progress in each.

Go Premium: monthly, quarterly and annual plans, with coins as the alternative.
06

Ads that never break the scroll

The reels and microdrama feeds carry video ads between items. Vertical feeds are unforgiving: viewers expect the next thing instantly, and an ad that stutters in is an ad that sends them away.

The work was about hiding cost where nobody would feel it.

The ad player is built before it is needed. Setting it up is expensive on a phone, so the app does it once, off screen, while the feed is loading and the viewer is already looking at a loading state. No later item pays that price.

Requests wait for the thumb to stop. An ad is fetched only once the viewer has settled on an item, so fast scrolling does not fire a stream of wasted requests.

Never two at once. Two ad requests in flight together can break playback on some devices, so the app guarantees there is never a second.

A watchdog for silence. Occasionally an ad request simply never answers. A timer notices and lets the feed carry on.

Every stage of an ad's life (request, response, impression, completion, skip and error) is reported alongside the content it appeared next to. An audit of that reporting found and closed gaps, including impressions that were being shown but never counted.

07

Two sources of truth that have to agree

A distributor has to be able to say what was watched, where and for how long. Pocket Films answers that from two independent sources, both built in-house.

What the app says. The apps collect events and send them in batches, holding them while the device is offline. On the server, raw batches are stored exactly as received, then sorted into typed tables by a job that keeps a checkpoint. It can stop at any moment and resume without losing or repeating a single event.

What the network saw. The second source does not depend on the app at all: the content delivery network's access logs. The platform imports them, groups them into playback sessions and rolls them up by hour and by day.

Import the same log file twice and you double its numbers. Here, that cannot happen.

Exactly-once imports. Every import records the files it consumed in the same transaction that writes their data. A file can never be counted twice, and an interrupted backfill picks up exactly where it stopped.

The database does the heavy lifting. Roll-ups run entirely inside the database. The rows never travel to the application and back, which turned a slow full rebuild into a fast one.

On top sit daily summaries by title, country, screen, search term and more, feeding the dashboards the team works from every day.

08

Recommendations with no blank-screen failure mode

Apexnova also built the recommendation engine that decides what each viewer sees. It runs as its own service beside the main backend, and it was designed around one question before any other: what happens when it is slow?

The worst case is a row of popular titles. Never an error, never an empty screen.

It holds itself to a deadline. The engine gives itself a strict budget of a few hundred milliseconds. If it cannot answer with personalised results in time, it answers with popular titles and says so. The app also keeps a last-known-good set, and a small evergreen list ships inside the app for the rare moment when everything else is unreachable.

The expensive work happens in advance. Personalised candidates are worked out for each viewer on a schedule and cached, so serving a request means assembling a page, not computing one.

Cold starts are handled honestly. A brand-new viewer has no history to learn from. The engine tracks when someone has watched enough to personalise for, and serves popular and new titles until then.

It notices what viewers avoid. Abandoning a title within seconds, or repeatedly, counts against it. Finished titles are not offered again.

It crosses formats carefully. A short-film viewer can be shown a microdrama, but only when it is genuinely similar to what they like, and only up to a limited share of the page.

It keeps room for new work. A set share of every page goes to titles outside a viewer's usual taste and to new arrivals. On a platform built on independent filmmakers, that is how a new film finds its audience.

It understands the films themselves. Each description is turned into a numerical fingerprint by a multilingual language model running on the platform's own servers, so titles can be compared by meaning across languages. No viewer data is sent to an outside AI service.

Editors stay in charge. The content team can pin, boost or remove titles and see the change within seconds. The engine supports experiments on its own behaviour, and its tuning lives in configuration that takes effect without a redeploy.

Feeding it without dropping a thing

The main backend supplies the engine with the catalogue and with what viewers watched, liked and skipped. The full history is far too large to send in one go, so it is walked page by page in a stable order, one page in memory at a time. Every event has a stable identifier, so a batch that times out can simply be resent. Progress is saved only once a batch is accepted, so a crash resends a little and never skips anything. And when someone signs in on a device they were already watching on anonymously, that earlier history joins their account.

Consent and erasure, built in

Personalisation is the viewer's choice. Their answer is recorded with a full audit trail, carried from device to account when they sign in, and acted on within about a minute. Where a viewer has not answered, the system assumes no consent and serves popular titles only.

Viewers can also ask to be forgotten. An erasure request removes them from every store the engine keeps, and the record that it was honoured holds no identifying detail. Profiles from anonymous devices that never signed in are purged automatically after a set period.

09

Fixing data without deleting anyone's work

Any platform that has lived a while collects duplicate accounts. Here the cause was ordinary: the same phone number written three ways (with a country code, without one, with a leading zero). Three spellings, three accounts.

One definition of a phone number. Every path that reads or writes a number now goes through a single piece of code that normalises it and resolves it to one account, the same way every time.

Merge, never delete. On this platform an account may belong to a filmmaker with live, earning titles, so duplicates could not simply be removed. Each account in a duplicate group is graded by what it actually holds, from an unpublished draft up to a live distribution. Where exactly one is clearly the real account, everything the others own moves onto it. Where it is not clear, the system refuses and leaves the case for a person.

Nothing is deleted at any point, and every move can be previewed before it is made.

The same caution shows up in poster artwork. Different surfaces need different shapes, and filmmakers do not always supply every one. The platform can produce a portrait poster from the artwork a title does have: it picks the source image closest in shape, finds the visually prominent region and crops around it. The method is deliberately modest and says so. It does not claim to recognise faces or text, and every generated poster waits for a person to approve it. Regeneration is tied to a fingerprint of the source image, so the same input is never processed twice.

10

Built to keep changing

The platform handles money, contracts and other people's work, and it is still growing. What makes that safe is more than 1,200 automated tests across the backend and the app, covering the coin ledger, payment renewals, the partner API contract, account consolidation and watch history, among much else.

New parts of the system, the coin economy among them, were added as self-contained modules and switched on from the server only once they had proven themselves.

Lessons

What we would tell a team building something like this.

  1. 01

    Model the licence, not just the catalogue

    In distribution, who may see and play what is the product. Make withholding the default.

  2. 02

    Treat in-app currency as money

    Calculate balances from a ledger, never store them, and never edit history.

  3. 03

    Assume every payment arrives twice

    Record the order first, separate verifying from granting, and make every step safe to repeat.

  4. 04

    Measure from more than one source

    What the app says and what the delivery network saw should agree. Build both.

  5. 05

    Design recommendations for the bad day

    A deadline and a fallback at every layer, so the worst case is popular titles, never a blank screen.

  6. 06

    Never delete to fix data

    Move, grade and preview. The account you remove may be somebody's business.

Building a streaming app or distribution platform?

Apexnova builds OTT platforms, streaming apps, monetisation, analytics, recommendations and content management end to end. You own the code, the brand and the data.

Book a call See Pocket Films for yourself:Google PlayApp Store
Next case studyBinj TV Microdrama app