WatchTower - Video Sharing banner

WatchTower - Video Sharing

46 devlogs
89h 28m 39s

An open source video sharing app with recommendations, transcoding and more

Updated Project: - wrote an admin dashboard

  • added live streaming, live chat
  • improved UI/UX
  • added an appeal/report system
  • added suggested videos
  • added man…

An open source video sharing app with recommendations, transcoding and more

Updated Project: - wrote an admin dashboard

  • added live streaming, live chat
  • improved UI/UX
  • added an appeal/report system
  • added suggested videos
  • added many QOL features
  • wrote tests
  • fixed lots of bugs
  • started work an iOS app
  • ran a full deployment

and more!

Demo Repository

Loading README...

ultraviolet.asdf

NSFW Filter for thumbnails

Thumbnails are now automatically blocked if anything NSFW is detected.

I also moved the clean-up step of jobs to a defer, so errored jobs will still be cleaned up.

I know the logs don’t look the nicest and aren’t consistent, I’ll be rewriting logs to use structured logs soon.

53f859535b

Attachment
Attachment
0
ultraviolet.asdf

UI Update: Infinite Scroll + Loading indicator for suggested videos

I added infinite scroll for suggested videos. This was already implemented in the API, but I guess I forgot to implement it on the website. I also added a loading indicator.

Note: The loading is intentionally slowed down in the demo video, just so you can actually see the indicator

8924fb5425

0
ultraviolet.asdf

UI Update: New layout options

I added layout options to suggested videos - a ‘comfy’ and ‘compact’ layout.
Your selected layout is consistent between page refreshes and different videos.

0c6e2273a8

0
ultraviolet.asdf

UI Update

I updated to video list to readd the separator, use flex wrapping and lower the font size.

Unfortunately, when the flex element wraps, the separator still shows. I spent around an hour trying to fix this using container queries. Container queries are awful to use with heights. Never again.

2b7319b412

Attachment
Attachment
0
ultraviolet.asdf

Testing + Code quality

I wrote a test case for users.GetUserById. Since a lot of validation was the same from the test for users.Get, I moved that logic into a separate function to be used for both test cases.

a25a0f14fe

The users.Create RPC returned a uint64 ID instead of an int64, so I updated the code to use int64 instead, and removed any no longer necessary conversions to int64.

a683c56d0d

Attachment
0
ultraviolet.asdf

Testing + Code quality

I added a test case for the Users.Get RPC, which verifies that all returned fields are as expected.

When I was writing this, I realised that type for User contained an email, even though it was not sent on most emails, which could lead to confusion using any RPC that returns a User.

To fix this, I added a separate type that contained the users email (UserWithEmail) that is used by RPCs that need the users email, and I removed the email field from the User type.

bc8cc01aa0

Attachment
0
ultraviolet.asdf

Testing

I wrote a test case for the GetFollowing RPC.

This test case has a similar setup to the GetFollowers RPC (create two users and follow one), so I split the logic into a separate function to reduce duplicated code.

The test case ensures:

  • Only one following user is returned
  • The returned following user has the correct username and ID
  • The CreatedAt timestamp is valid

5d7ed806a2

Attachment
0
ultraviolet.asdf

Testing

I added a test case for the GetFollowers RPC - this is passing :)

Honestly, writing tests is boring as hell. I’ve only written 37% of the tests for the Users service… and there’s around three other services to test for.

d0cd784d54

Attachment
0
ultraviolet.asdf

Bug Fixes

In my previous devlog I discovered users were able to follow themselves.

In this devlog I:

  • Rewrote the follow/unfollow RPCs so more logic is done with go than SQL
  • Blocked users from following themselves (test case passes)
  • Added a nicer error message when users try to follow a nonexistent users

761211acb0

Attachment
Attachment
0
ultraviolet.asdf

Testing

I added test cases for:

  • Following yourself (should not be allowed)
  • Following someone (allowed)
  • Unfollowing someone (allowed)
  • Following a nonexistent person (should not be allowed)

As a result of this, I have found that:

  • You can follow yourself through the API
  • Error handling should be improved for follow RPCs
  • I should improve my testing solution, right now I have to manually log if it passes, and fails are just logged as an error. I should make this automatic

These should be fixed in the next update :)

c7b3ea8e3b

Attachment
0
ultraviolet.asdf

Code quality + Testing

I improved code quality by:

  • Check grammar/spelling. I fixed a lot of grammar and spelling errors using harper-ls and harper-cli
  • Stricter formatting. I ran gofumpt, which has a lot stricter formatting
  • Linting. This took by far the longest time - I ran golanglint-ci, and one by one, fixed every single lint issue. This included many unhandled errors (that wouldn’t of caused a problem, just weren’t being logged)

a93729e78c

I also updated testing by:

  • Cleaning up code
  • Writing test cases for different password lengths

a5f74ef1f5

Attachment
Attachment
0
ultraviolet.asdf

Feature: Umami tracking

I added Umami, a privacy focused analytics service to Watchtower. As of now it only tracks page views, but in the future I plant to improve integration by adding events for video likes/dislikes, follows, video creation etc.

I also considered Rybbit, because it has more features like error tracking and web vitals, but I could not get a self hosted set up working. Maybe in the future.

Attachment
0
ultraviolet.asdf

Feature: Comment deletion

I added the ability to delete comments. This can only be done by the user who created the comment, or any admin account

0
ultraviolet.asdf

UX Improvement

After updating the video upload script, an issue was created where errors weren’t displayed properly. I have updated the website to clearly show errors

Attachment
0
ultraviolet.asdf

Bug fixes

On the second test case I wrote I found a bug. Not sure if this is a good thing, but at least it means testing is worth it.

  • 686d0d4b70 Add test cases to make sure duplicate emails and usernames are blocked
  • e6562c8aae Fix bug discovered above where errors were not
    handled correctly
  • 2266cef6fc Allow videos down to 240 pixels to be uploaded, and handle videos that are smaller than 240px properly, by rejecting them and showing an error message instead of transcoding them badly
Attachment
0
ultraviolet.asdf

Testing!

Yes - I’ve finally gotten around to writing tests!

Before I was relying on manual testing, by visiting the website - I’ve probably missed quite a few bugs. But now I’ve started writing automated tests for the API.

I have set up the tests so an ephemeral API is set up, along with temporary PostgreSQL, S3, RabbitMQ, and Valkey (redis). These automatically start when you run the tests, and stop when the tests have finished. I did this using the Golang SDK for docker-compose, and waiting for the API to start. After, migrations are automatically applied to the testing database.

As of now, there is only one test case, but I will setup more and more. I plan to implement E2E testing for the website in the future.

Attachment
1

Comments

Neon
Neon about 1 month ago

rabitmq,, very noice

cskartikey

Tagged your project as well cooked!

🔥 cskartikey marked your project as well cooked! As a prize for your nicely cooked project, look out for a bonus prize in the mail :)

ultraviolet.asdf

Full deployment

The full version of Watchtower is deployed on my new server (my old pc) which means you can now upload videos! These will be transcoded to multiple different resolutions, and analyzed to block NSFW content. The NSFW detection may be a bit inaccurate so make sure to use the report and appeal features

Changes

  • 472ff840c5 Fix transcoder dockerfile
  • 1141fb2aec Add analytics dockerfile
  • 15c7b46db9 Video duration’s are hidden if they are zero (happens in demo environment)
  • Multiple Various dockerfile fixes
  • Multiple Switch to old golang version of video analyser
  • d58a78215d Sidebar links are given a semitransparent background
Attachment
0
ultraviolet.asdf

Shipped this project!

Hours: 64.27
Cookies: 🍪 675
Multiplier: 22.64 cookies/hr

I updated my video sharing app!

In this update I polished code, started work on an iOS app and added suggested videos in the view video page!

Please check my previous ship message and devlogs for more details :)

ultraviolet.asdf

Polishing

I refactored some ridiculous code that was inline, and made it a separate function.
This will reduce allot of bandwidth because now the JavaScript is in one place instead of in every single comment

View Commit

Attachment
0
ultraviolet.asdf

Polishing

Spent some time polishing things, and working on docker images, for a move to another server

  • 612a3cecad I removed the dependency on go-sqlite3 which was leftover from when I used sqlite as the database. The error messages for duplicate usernames are now prettier
  • fc2f524b21 I added docker images
  • e6394fea10 I gave the icons when changing/uploading thumbnails a background, so the contrast is still OK when the uploaded image is light or dark
  • 1427c28e33 I allowed the recommended videos to be clicked anywhere for better UX, and added a background on hover

If you spot anything that needs polishing - please let me know!

Attachment
Attachment
Attachment
0
ultraviolet.asdf

iOS App: Update

  1. I added sort options, you can now select between latest, popular, recommended and trending. This took a fair amount of work, I had to guess the type of the gRPC client so I could pass it between Views. This is really hard when you don’t have a functional LSP. I also had to rewrite the way the gRPC client was created so the connection doesn’t close.
  2. Moved thumbnails to a separate component for clarity
0
ultraviolet.asdf

Web Update

I added suggested videos right in the view video page.

I also simplified a lot of repeated code, moved an API endpoint from the Users service to the Videos service, and made it so if there is only one comment on a video it says 1 Comment instead of 1 Comments - Crazy right?!

View 3 commits

Attachment
1

Comments

ultraviolet.asdf
ultraviolet.asdf about 2 months ago

Not pushed to demo yet, intending to change servers and enable custom video uploads soon

ultraviolet.asdf

iOS App!

I started working on an iOS app using swift!

This is my first time ever using swift, so it took quite a while.

Initially I tried using NativeScript Svelte, then React Native. Its pretty hard to get gRPC working with them, needing a proxy or something for the gRPC server, so I switched to swift instead.

Xcode (Apples IDE) doesn’t run on linux, so I had to install xtool and vscode/zed. I have to rebuild everytime I make a change, and the builds are pretty slow, but Swift is a really nice language and I may use it in the future

Attachment
0
ultraviolet.asdf

Shipped this project!

Hours: 44.5
Cookies: 🍪 1070
Multiplier: 24.06 cookies/hr

I built Watchtower, a Youtube clone. It is entirely self hostable.

I worked on it for over 92 hours before I found out about Flavortown.
As of now, I have coded over 44 hours worth of updates!

Note: Live streams and uploading videos are disabled in the demo. I do not have a GPU server, so videos would take days to process and analyse. Live streams would probably blow up the laptop I’m hosting everything on. You can still live chat though

Features

  • Transcoding: uploaded videos get transcoded to multiple resolutions (360p, 480p, 720p, 1080,1440p and 2160p). You can upload vertical videos and it will keep the aspect ratio
  • Thumbnail generation: Creates a storyboard image that allows you to seek through the video with a preview
  • Video Analyser: Videos are split into frames, frames are deduplicated and each frame is analysed for NSFW content
  • Live streams: You can stream from OBS to Watchtower. This uses nginx-rtmp. Authentication is managed by a golang server, approving or rejecting streams
  • Live chat: There is a live stream chat built using websockets
  • CDN: I built a CDN that manages permissions for videos, and proxies to the RTMP server so stream keys remain hidden
  • Admin dashboard: I wrote an admin dashboard with user/video analytics, it allows you to manage users, reports and appeals
  • Reports: Users can report videos and an admin can block reported videos
  • Appeals: Blocked videos can have an appeal requested by the owner, approved or rejected by an admin

Tech stack

  • The API is built using go with gRPC/Protobuf
  • The frontend is written with go, templ and htmx
  • The CDN is built with go
  • S3 (SeaweedFS) is used for storage
  • Postgres is used as the database
  • Valkey(redis) is used for the live chat
  • Gorse is used as a recommendation system
  • RabbitMQ is used as the message broker
  • The transcoder was built with Go + FFMPEG
  • The thumbnail generator was also built with Go + FFMPEG
  • The video_analyser was first built with go, then rewritten with Typescript (Bun). The package nsfwjs was used.
  • The analytics is written with go, and is run as a cronjob once a day, counting the number of users and videos

Development

I built a tool called devman specifically for this. It allows you to run multiple services from a configuration file. Each services can wait for other services to be online, so programs do not crash if something not up yet.
You can check it out on Codeberg

ultraviolet.asdf

Video Reporting

I finished video reporting, now admins can flag a reported video in the admin dashboard

Attachment
Attachment
0
ultraviolet.asdf

Moderation Appeal

You can now request an appeal if your video gets blocked.

There is a new page in the admin dashboard to view appeals

Admins can either reject or remove appeals

Rejecting will not allow the user to create another appeal

View The Code

Attachment
Attachment
Attachment
Attachment
Attachment
0
ultraviolet.asdf

Rewrite

I rewrote the video analyzer (NSFW detection) from Go to TypeScript, because the go package used for NSFW broke.
It might be slower, but at least it works

Can’t really have many screenshots for this update…

I also reduced the amount of things you need to install by using go tools,
where instead of installing the binary I run go get -tool and call it using go tool
More work for me, less work for someone trying to set this up themselves.

I also reinstalled my OS 2 times, so thats why this update took a while

You can view the changes on Codeberg

Attachment
0
ultraviolet.asdf

Live chat

I added chats to live streams! (you can chat even when the stream is offline
This was implemented using websockets (this is like the 8th microservice)

I made it so scrolling pauses the chat, so you can read messages.
The colour of the usernames are based on the hash of your username, I should probably let you pick a colour though.

0
ultraviolet.asdf

Replaced dropzone.js with a custom upload script, so uploading matches the theme better
(4h of work because everything decided to break)
(ps don’t use RustFS, it is buggy and vibecoded)

View the code

Attachment
Attachment
0
ultraviolet.asdf

I added trending, and did a few other things, like improving security, working on a demo environment, improving code, fixing a bug and cleaning up processed thumbnails

View 5 changes

Attachment
0
ultraviolet.asdf

Admin Dashboard Improvements

I added a user management page that lists all users (with scrolling to load more) and allows flag management (Admin/Verified flags)

View the change

I should add search next

0
ultraviolet.asdf

Improved UX by redirecting back to profile page after deleting a video (with a refresh) so deleted videos don’t show after deletion
Also changed some endpoints to use proper http semantics

Attachment
Attachment
0
ultraviolet.asdf

I hid private/unprocessed videos in the recommended feed
Also had problems with atlas migrations which took hours to fix :)

Attachment
0
ultraviolet.asdf

I added a most popular sort option to the homepage of WatchTower.
I am planning to add a trending option as well.
I should probably remove some of the duplicated logic though and replace /videos/latest and /videos/popular with /videos/{sort_type}

Attachment
Attachment
0
ultraviolet.asdf

I added a “latest” sort option to my video sharing platform
I also fixed a bug where usernames wouldn’t show on recommended videos
Next up is sorting by popularity

Attachment
0