The Plan — Building HomeStream
Why this project, why now?
Real talk for a sec — almost every PHP tutorial in the world ends at "build a todo list" or "build a blog." Both fine. Both forgettable. We're going to build something you'll genuinely use after the tutorial ends. Picture it: you've got a stack of MP3s from your old iTunes library, maybe some DVDs you ripped a decade ago, definitely a folder called "movies" that's been quietly migrating between drives for years. Right now it just sits there. After this module, you'll pull up a clean web page on your phone or laptop, hit play, and your laptop in the cupboard becomes the source of the music or movie. From any room in the house. That's the whole pitch.
And here's the deeper thing — building HomeStream teaches you a stack of patterns that show up in basically every real backend job. Reading files off disk. Streaming bytes with HTTP Range requests so video seeking works. Background scans of a filesystem into a database. Shelling out to external tools (ffmpeg). Caching generated thumbnails. Authentication. Pagination. Search. Every one of those is a transferable skill. Netflix's actual engineers worry about the same problems, just at a different scale. Today, we worry about them at "one cupboard laptop" scale.
What we're building, in five layers
I want to give you the mental model up front so the chapters that follow click into place. HomeStream has five layers, and we'll build them one at a time:
- The library database. One MariaDB table that knows about every audio and video file you own — paths, sizes, titles, durations. This is the brain.
- The scanner. A PHP script you run from the command line that walks your media folders and updates the database. Run it whenever you add new files.
- The library page. A web UI listing your media — filter by type, search, paginate. The browse experience.
- The streaming endpoint. A PHP file that sends the actual bytes of a media file to the browser, with proper support for "seeking" partway through a video. This is the technically juicy chapter.
- The polish layer. Thumbnails, metadata extraction, login, search, recently-added, a tiny play queue. The stuff that turns "works on my machine" into "actually feels like a real app."
That's the journey. Don't worry if any of those words feel abstract right now — each chapter takes one layer and walks you through it, end to end.
Where the files live
Time to plan the folder layout. Good early decisions here save you so much friction later. Take a minute and we'll set it up properly.
/home/erictey/server/homestream/
├── public/ # everything web-accessible lives here
│ ├── index.php # library page (the home screen)
│ ├── stream.php # streaming endpoint
│ ├── play.php # player page (audio + video)
│ ├── login.php
│ ├── logout.php
│ └── assets/
│ └── style.css # HomeStream-specific styling
├── src/ # PHP classes (autoloaded by Composer)
│ ├── MediaItem.php
│ └── MediaRepository.php
├── lib/ # plain include files
│ ├── db.php
│ └── auth.php
├── bin/ # CLI scripts you run by hand
│ ├── scan.php # walk filesystem → DB
│ ├── thumbs.php # generate poster frames
│ └── metadata.php # extract ID3 + ffprobe info
├── storage/ # generated stuff (not in git)
│ └── thumbs/ # cached video posters
└── composer.json
A couple of choices worth calling out. Notice how only public/ is meant to be served by Apache. Everything else — your PHP classes, your CLI scripts, your storage folder — sits one level up where the web can't touch it. That's deliberate. If a security bug ever exposes "list files in this folder," you want the answer to be a short list of intentional public endpoints, not your entire codebase. Putting non-web stuff outside the document root is a habit every real PHP framework follows. We're following it from day one.
Also: separating src/ (Composer-autoloaded classes) from lib/ (plain require files) feels like overkill on day one but pays off when you start importing third-party packages. src/ is "code that uses namespaces and follows PSR-4." lib/ is "quick globals like the db() helper." Both legit, both useful, just different jobs.
Where YOUR media lives
One more important question before we start: where does your actual music and video sit on disk? You probably have it somewhere already — maybe ~/Music/ and ~/Videos/, maybe an external drive plugged into the Lubuntu server, maybe a NAS. Wherever it is, write the path down because we'll plug it into the scanner. For this walkthrough I'm going to assume:
/home/erictey/media/music/
/home/erictey/media/movies/
If yours lives elsewhere, sub in your real paths whenever you see those. The scanner doesn't care, as long as the path is readable by the user Apache runs as (which is usually www-data). We'll handle permissions when we get there.
Quick set-up to do right now so future you can just code. SSH into Lubuntu and:
mkdir -p /home/erictey/server/homestream/{public,src,lib,bin,storage/thumbs}— creates the entire folder tree in one shot. The-pmeans "create parents as needed" and the{...}is shell brace expansion for multiple folders.cd /home/erictey/server/homestreamcomposer init --no-interaction --name=eric/homestream— sets up Composer like you did at the end of Part 2.- Open
composer.jsonin VS Code and add the autoload entry:"autoload": { "psr-4": { "Homestream\\\\": "src/" } } composer dump-autoload— registers the autoloader.mkdir -p /home/erictey/media/{music,movies}— even if you don't have media to drop in yet, creating the folders means the scanner won't crash when we test it.
Run ls /home/erictey/server/homestream. You should see five folders (public, src, lib, bin, storage), the vendor folder Composer just made, and composer.json + composer.lock. That's your skeleton. No code in it yet, but every chapter from here on adds one more piece. Promise — by chapter 10, this folder is a real, working, streaming app.
What the next chapter delivers
Chapter 2 is the database. We'll design and create the media table that's going to know about every file in your library. Lots of small but interesting decisions in there — what type each column should be, which indexes matter, how to handle UTF-8 in filenames (Japanese album names, accented French titles, you know how it goes). Quick chapter, but the choices we make there ripple through everything else, so we'll take it seriously.
Quick reassurance before we dive in: if any of this feels intimidating, that's normal. You're about to build something a lot of working developers haven't built. Take it one chapter at a time. The mini-projects compound — by chapter 5, you'll be wiring real video bytes through your own code, and by chapter 8, you'll have a polished little media server. You got this.