<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: jiahui dong</title>
    <description>The latest articles on DEV Community by jiahui dong (@jiahui_dong_e52833db9f4c1).</description>
    <link>https://dev.to/jiahui_dong_e52833db9f4c1</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4129358%2F4f009b0e-a6ab-4f0b-ba9d-a1d5de542620.png</url>
      <title>DEV Community: jiahui dong</title>
      <link>https://dev.to/jiahui_dong_e52833db9f4c1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jiahui_dong_e52833db9f4c1"/>
    <language>en</language>
    <item>
      <title>Reimagining Video Creation with AI &amp; GPU Acceleration</title>
      <dc:creator>jiahui dong</dc:creator>
      <pubDate>Thu, 17 Sep 2026 09:03:35 +0000</pubDate>
      <link>https://dev.to/jiahui_dong_e52833db9f4c1/reimagining-video-creation-with-ai-gpu-acceleration-3205</link>
      <guid>https://dev.to/jiahui_dong_e52833db9f4c1/reimagining-video-creation-with-ai-gpu-acceleration-3205</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article was first published on my site: &lt;a href="https://resume.tensorview.cc/blog/ai-video-creation-engine.html" rel="noopener noreferrer"&gt;https://resume.tensorview.cc/blog/ai-video-creation-engine.html&lt;/a&gt;&lt;br&gt;
FastAPI + GPU / NVENC: business logic decoupled from render&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I built a distributed AI video splitting and synthesis backend. FastAPI handles logic on CPU nodes while encode/decode rides NVIDIA GPUs via FFmpeg CUDA (NVENC) — about 8–10× faster than CPU-only rendering.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fefnivjpuj90knw9cs95u.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fefnivjpuj90knw9cs95u.jpg" alt="Mini-program “AI Original”: split, scenes, voiceover, one-tap synthesize." width="687" height="1536"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Mini-program “AI Original”: split, scenes, voiceover, one-tap synthesize.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Heterogeneous compute:&lt;/strong&gt; logic decoupled from GPU render for concurrent jobs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI workflow:&lt;/strong&gt; DeepSeek rewrite plus Edge-TTS voiceover.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage:&lt;/strong&gt; local cache + Qiniu OSS with LRU cleanup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pro codecs:&lt;/strong&gt; lossless 4K splitting and real-time multi-track mix.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fna22qlfdmpzmv4abxq3w.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fna22qlfdmpzmv4abxq3w.jpg" alt="AI script UI: DeepSeek create/rewrite with Edge-TTS voice pick." width="687" height="1536"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;AI script UI: DeepSeek create/rewrite with Edge-TTS voice pick.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8a9lxwi2bodef0es88lx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8a9lxwi2bodef0es88lx.jpg" alt="History: synthesis status, preview, and download." width="687" height="1536"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;History: synthesis status, preview, and download.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Whether you run a short-video matrix or automated content pipelines, this stack is built for faster synthesis. Always happy to talk optimization or A/V automation.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Original post:&lt;/strong&gt; &lt;a href="https://resume.tensorview.cc/blog/ai-video-creation-engine.html" rel="noopener noreferrer"&gt;https://resume.tensorview.cc/blog/ai-video-creation-engine.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>python</category>
      <category>fastapi</category>
      <category>ai</category>
      <category>ffmpeg</category>
    </item>
    <item>
      <title>Architecting High-Performance Multi-Camera Streaming with RGA Hardware Acceleration</title>
      <dc:creator>jiahui dong</dc:creator>
      <pubDate>Thu, 17 Sep 2026 09:02:48 +0000</pubDate>
      <link>https://dev.to/jiahui_dong_e52833db9f4c1/architecting-high-performance-multi-camera-streaming-with-rga-hardware-acceleration-3aia</link>
      <guid>https://dev.to/jiahui_dong_e52833db9f4c1/architecting-high-performance-multi-camera-streaming-with-rga-hardware-acceleration-3aia</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article was first published on my site: &lt;a href="https://resume.tensorview.cc/blog/rk3588-rga-multi-camera.html" rel="noopener noreferrer"&gt;https://resume.tensorview.cc/blog/rk3588-rga-multi-camera.html&lt;/a&gt;&lt;br&gt;
RGA + DMA zero-copy for three high-res camera streams&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In a recent RK3588 Embedded Linux project I owned the vision capture path: three high-resolution streams (coaxial, USB, wireless) previewed at once. CPU soft conversion would choke the UI thread under that throughput.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fut2fef5qpug06ovnl4ny.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fut2fef5qpug06ovnl4ny.jpg" alt="Legacy CPU soft-decode path: heavy memory traffic fights the UI thread." width="800" height="784"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Legacy CPU soft-decode path: heavy memory traffic fights the UI thread.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The architectural decision
&lt;/h2&gt;

&lt;p&gt;I designed around the on-SoC RGA from day one. A DMA path feeds raw YUV from contiguous buffers into RGA for YUV→RGB and scaling, skipping extra CPU cache churn.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdp1twpccvjah44llzygi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdp1twpccvjah44llzygi.jpg" alt="RGA hardware path: CPU only configures; pixels stay on DMA." width="800" height="854"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;RGA hardware path: CPU only configures; pixels stay on DMA.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical challenges
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strict alignment:&lt;/strong&gt; posix_memalign 4K page-aligned pools keep DMA stable and avoid tearing / faults.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Heterogeneous sources:&lt;/strong&gt; a unified buffer layer configures RGA from V4L2 metadata (UYVY vs YUYV, MIPI vs USB).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3sjh30t68wktcrp31ad7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3sjh30t68wktcrp31ad7.jpg" alt="Live preview UI: switch coaxial / FOV / RX with hardware controls in one panel." width="800" height="599"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Live preview UI: switch coaxial / FOV / RX with hardware controls in one panel.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Outcome
&lt;/h2&gt;

&lt;p&gt;All three cameras preview smoothly with negligible CPU on the video path, leaving headroom for business logic and UI. Hardware-aware design still wins in embedded.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Original post:&lt;/strong&gt; &lt;a href="https://resume.tensorview.cc/blog/rk3588-rga-multi-camera.html" rel="noopener noreferrer"&gt;https://resume.tensorview.cc/blog/rk3588-rga-multi-camera.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>embedded</category>
      <category>linux</category>
      <category>rockchip</category>
      <category>rga</category>
    </item>
    <item>
      <title>Embedded Linux I2C Debugging: How a Register Dump Saved Me from an Invisible Timing Trap</title>
      <dc:creator>jiahui dong</dc:creator>
      <pubDate>Thu, 17 Sep 2026 08:56:56 +0000</pubDate>
      <link>https://dev.to/jiahui_dong_e52833db9f4c1/embedded-linux-i2c-debugging-how-a-register-dump-saved-me-from-an-invisible-timing-trap-1cp9</link>
      <guid>https://dev.to/jiahui_dong_e52833db9f4c1/embedded-linux-i2c-debugging-how-a-register-dump-saved-me-from-an-invisible-timing-trap-1cp9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article was first published on my site: &lt;a href="https://resume.tensorview.cc/blog/i2c-trise-ack-trap.html" rel="noopener noreferrer"&gt;https://resume.tensorview.cc/blog/i2c-trise-ack-trap.html&lt;/a&gt;&lt;br&gt;
CCR looked flawless. TRISE was the real trap.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Recently I worked on a layout where the host MCU needed more GPIOs, so we hung an I2C expander. Standard architecture — until intermittent EIO (-5) errors and missing slave ACKs showed up.&lt;/p&gt;

&lt;p&gt;Instead of guessing at the driver, I took a structured path: scope the bus and dump physical I2C registers at the moment of failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Illusion
&lt;/h2&gt;

&lt;p&gt;The Clock Control Register (CCR) was calculated correctly for 100 kHz Standard Mode. On paper the baud rate looked flawless.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Symptom
&lt;/h2&gt;

&lt;p&gt;The oscilloscope showed a slight trailing edge on the SCL rise. The register snapshot at failure captured SR1.AF = 1 (Acknowledge Failure).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Root Cause
&lt;/h2&gt;

&lt;p&gt;TRISE (maximum rise time) was set too low. The I2C state machine jumped the gun — sampling before SCL had reached a valid high (Vih).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix
&lt;/h2&gt;

&lt;p&gt;I recomputed TRISE from PCLK and the I2C spec, updated Device Tree / driver init, and the bus stayed clean across a 10,000-cycle stress test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaway
&lt;/h2&gt;

&lt;p&gt;High-level abstractions often mask physical reality. When a driver throws a generic error, dump the hardware registers. Registers do not lie.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Original post:&lt;/strong&gt; &lt;a href="https://resume.tensorview.cc/blog/i2c-trise-ack-trap.html" rel="noopener noreferrer"&gt;https://resume.tensorview.cc/blog/i2c-trise-ack-trap.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>embedded</category>
      <category>linux</category>
      <category>i2c</category>
      <category>driver</category>
    </item>
    <item>
      <title>Six Months in Taobao Outsource Groups: The Flow and the Traps</title>
      <dc:creator>jiahui dong</dc:creator>
      <pubDate>Thu, 17 Sep 2026 08:56:10 +0000</pubDate>
      <link>https://dev.to/jiahui_dong_e52833db9f4c1/six-months-in-taobao-outsource-groups-the-flow-and-the-traps-29ln</link>
      <guid>https://dev.to/jiahui_dong_e52833db9f4c1/six-months-in-taobao-outsource-groups-the-flow-and-the-traps-29ln</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article was first published on my site: &lt;a href="https://resume.tensorview.cc/blog/taobao-outsource-group-half-year.html" rel="noopener noreferrer"&gt;https://resume.tensorview.cc/blog/taobao-outsource-group-half-year.html&lt;/a&gt;&lt;br&gt;
Only what I lived through. Personal sharing — not a rant against anyone or the industry, just a sigh after walking through it. I don’t live off this path. Project details later.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Up front: this is a personal note, not a get-rich guide, and not a hit piece on any firm or the whole outsource scene. I’ll unpack the Taobao shop → WeChat order-group path I walked, then the awkward bits I hit as a developer and what I saw on the client side, plus a short contrast with other channels.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. What the flow looks like
&lt;/h2&gt;

&lt;p&gt;On my side, the pipeline was roughly five steps.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Join the group:&lt;/strong&gt; Message Taobao outsource shops and ask if they need developers. Usually yes — they pull you into a WeChat group where orders are posted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Orders get posted:&lt;/strong&gt; Posters I met were mostly company customer-service staff (e.g. from a Suzhou firm). They mentioned ¥500 commission per finished order; I never saw the exact split.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requirements:&lt;/strong&gt; CS usually already sits with the client in a group (sometimes with other clients too). They talk requirements, forward the brief to the developer group, and interested developers DM CS with a quote.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build:&lt;/strong&gt; Developer, client, and CS move into a smaller group. After that CS mostly steps back; the developer talks with the client and builds in parallel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Acceptance &amp;amp; pay:&lt;/strong&gt; After acceptance, developers often wait until month-end for a batch payout. Clients frequently paid the middleman in full at order time — a big structural advantage for the agency.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Where it gets awkward for developers
&lt;/h2&gt;

&lt;p&gt;From the developer seat, these stuck with me the most.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Usually no pay until the work is truly done
&lt;/h2&gt;

&lt;p&gt;Before real completion and acceptance, you often see no money at all. If the client later adds scope or changes their mind, you frequently have to keep going — or the payout stays held.&lt;/p&gt;

&lt;p&gt;When conflict shows up, CS talks to both sides: tells the client “this may not have been clear — add budget or pause”; tells the developer “please tweak a bit more,” sometimes mentioning a little extra pay. That “extra” rarely matches what the middleman already took.&lt;/p&gt;

&lt;p&gt;With no cash in hand, you have little leverage. The middleman carries little risk: if you can’t finish, they can rehire with the same money. You ate the sunk time.&lt;/p&gt;

&lt;p&gt;Why isn’t scope locked in one shot? What I saw: clients don’t know the domain and invent asks later; nobody foresaw every edge case, and developers can’t know every industry; or communication turns sour and every change drains you.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Developer take-home is low; the middle cut is thick
&lt;/h2&gt;

&lt;p&gt;Multiple agencies often post in the same groups, so developers compete hard and take-home rates stay low. Two firms I ran into marked up a lot: one roughly 2× the developer quote to the client; another as high as 7–8×.&lt;/p&gt;

&lt;p&gt;They don’t always care how polished the work is. If the client can accept it, rough edges may be fine — they know the developer take-home is already small.&lt;/p&gt;

&lt;p&gt;One of mine: a “transfer station” job. I quoted ~¥300; the client paid 5000+ for the code work. Full web stack plus server deploy. ¥300 can look okay until endless UI and feature churn — even on an open-source base — wears you down.&lt;/p&gt;

&lt;p&gt;I ran 2–3 jobs in parallel with AI help, so as a side income it looked okay: a couple of deliveries every 1–2 weeks. Cost: high energy, limited skill growth, almost no lasting passive income, and few repeats.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Even after friending clients, repeats were rare
&lt;/h2&gt;

&lt;p&gt;I added almost every client and did plenty of extra fixes, yet almost none returned via this Taobao-group path. Boss Zhipin and friend referrals brought more repeats. The CS-mediated experience may be part of it — if it feels mediocre, people don’t come back the same way.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Clients can lose out too
&lt;/h2&gt;

&lt;p&gt;Same transfer-station case: competition kept my quote around ¥300, while the agency charged the client about ¥5000 — most of it middleman margin.&lt;/p&gt;

&lt;p&gt;What surprised me more: the client rented that company’s server, didn’t keep the account, and fully handed the project off. ~¥3000/year on a crowded shared box that felt sluggish — enough money to self-host — yet they paid without digging in.&lt;/p&gt;

&lt;p&gt;So the picture I saw is twisted: developer prices get crushed while most money stays in the middle. Many “developers” listed on Taobao are ad-hoc outsourcers — not much of a hard guarantee.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Stopping here for now
&lt;/h2&gt;

&lt;p&gt;By comparison, other channels felt better: forums like V2EX or EleDuck where you take jobs yourself, or Boss Zhipin — basically anything except specialized middleman agencies. And always take a deposit and agree on staged delivery up front: projects run steadier, and you keep negotiating room while building. Lock the payment milestones before you start.&lt;/p&gt;

&lt;p&gt;Personal experience and a sigh — not a verdict on the industry. What projects I shipped and how I scheduled them — later. If you pass through similar groups, the three questions I wish I’d clarified earlier: when money arrives, how big the cut is, and how scope gets locked to cut rework.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Original post:&lt;/strong&gt; &lt;a href="https://resume.tensorview.cc/blog/taobao-outsource-group-half-year.html" rel="noopener noreferrer"&gt;https://resume.tensorview.cc/blog/taobao-outsource-group-half-year.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>sidehustle</category>
      <category>outsourcing</category>
      <category>freelance</category>
      <category>growth</category>
    </item>
    <item>
      <title>Outsourcing Part 2: AI Side Jobs on the Web — Where Money and Pain Come From</title>
      <dc:creator>jiahui dong</dc:creator>
      <pubDate>Thu, 17 Sep 2026 08:48:44 +0000</pubDate>
      <link>https://dev.to/jiahui_dong_e52833db9f4c1/outsourcing-part-2-ai-side-jobs-on-the-web-where-money-and-pain-come-from-3161</link>
      <guid>https://dev.to/jiahui_dong_e52833db9f4c1/outsourcing-part-2-ai-side-jobs-on-the-web-where-money-and-pain-come-from-3161</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article was first published on my site: &lt;a href="https://resume.tensorview.cc/blog/outsource-ai-side-hustle-part-2.html" rel="noopener noreferrer"&gt;https://resume.tensorview.cc/blog/outsource-ai-side-hustle-part-2.html&lt;/a&gt;&lt;br&gt;
Sequel to part 1. What web/mini-program gigs I actually shipped, how I used AI to move fast, and two jobs that drained me. Personal notes — not a playbook, not a rant.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Part 1 mapped Taobao middleman groups and cuts. This one is the doing: what the jobs looked like, rough income, which clients were fine, and which ones left me with one rule — don’t take that kind of job again.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. What I actually built
&lt;/h2&gt;

&lt;p&gt;Most were compact full-stack web jobs: software download sites, API relay/proxy stations, Reddit-like forums, WordPress trade sites, tutoring-school landing pages. Mini programs too — e.g. Coze-backed Q&amp;amp;A, and a construction-vehicle monitoring app.&lt;/p&gt;

&lt;p&gt;AI wrote most of the code. My setup was plain: a monthly AI Pro plan bought on Xianyu, plus Cursor — enough quota to push simple gigs through a month. My job was scope, edges, and getting to acceptance — not typing every line from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Rough income
&lt;/h2&gt;

&lt;p&gt;In a normal month of these simpler jobs, an extra ¥3–4k side income felt common. With luck — first-hand work, thin middle cut — ¥8k in a month is possible. High variance; don’t budget on the ceiling.&lt;/p&gt;

&lt;p&gt;Think of it as side-hustle leverage: talk, ship, collect — stack other gigs and you might approach full-time income. That needs decent jobs. Taobao-agency handoffs crush the rate; I covered that in part 1.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Two jobs that really hurt
&lt;/h2&gt;

&lt;p&gt;Not every job was painful. Some shipped, got accepted, and felt clean. Two stuck with me hard enough to write down.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. A two-day HTML site that stretched nearly two months
&lt;/h2&gt;

&lt;p&gt;One client wanted an app download site. Objectively a one-to-two-day static page. It ran nearly two months — not because the page was hard, but because their app wasn’t ready, and each build meant another Qiniu upload from me. Easy work, calendar owned by them; effective rate tanked and it felt awful.&lt;/p&gt;

&lt;p&gt;Lesson: if delivery depends on the client’s own product cadence, lock how many update rounds, how time is billed, and what happens when they slip — or you’re their unpaid upload clerk.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Rush job + a crawling server: never take “must ship in a week”
&lt;/h2&gt;

&lt;p&gt;Another was a relay/transfer station with a one-week deadline. That taught me: skip rush jobs when you can. They can ping by the hour; WeChat opens to a wall of messages, and their anxiety owns your calendar.&lt;/p&gt;

&lt;p&gt;Worse: we were on the agency’s server, painfully slow. Build on GitHub, upload, deploy — each step crawled; a tiny tweak could eat half a day before you saw it live. The client kept inventing tiny changes, rushed me daily, barely pushed the middleman — who still took the fat cut. In that setup the developer loses twice, and it barely makes sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Smooth jobs still cost attention
&lt;/h2&gt;

&lt;p&gt;Plenty of jobs were fine: clear enough scope, ship, accept. Even then, many clients know neither tech nor product — so you still eat empty talk and shifting asks. Light work, heavy mental overhead.&lt;/p&gt;

&lt;p&gt;As a true side hustle — not another full-time stress layer — it can feel okay: fingers, voice, ship. Stack other gigs and you might near full-time income. Source still matters: prefer first-hand, thin-cut work. Taobao middleman boards stay a bad deal long-term.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Rules I’m keeping
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Skip pure rush jobs:&lt;/strong&gt; a one-week hard deadline plus hourly pings usually means scope creep and no evenings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don’t bind to an unfinished client product forever:&lt;/strong&gt; cap or bill upload/release rounds up front.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid agency server + agency cut together:&lt;/strong&gt; bad env, thin pay, half-day deploys per tweak.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI speeds coding; you still set boundaries:&lt;/strong&gt; Cursor writes code — it won’t stop a client’s change loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefer first-hand channels:&lt;/strong&gt; side work can be pleasant — not inside the thickest cut forever.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Still personal. Web gigs plus AI can add real cashflow; whether it feels good hangs on where the job comes from, how loose the deadline is, and whether the env lets you iterate fast. For the pipeline and cuts, see part 1. If more traps are worth writing down, there’ll be a part 3.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Original post:&lt;/strong&gt; &lt;a href="https://resume.tensorview.cc/blog/outsource-ai-side-hustle-part-2.html" rel="noopener noreferrer"&gt;https://resume.tensorview.cc/blog/outsource-ai-side-hustle-part-2.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>sidehustle</category>
      <category>outsourcing</category>
      <category>ai</category>
      <category>freelance</category>
    </item>
    <item>
      <title>How I Ship a Controlled Download Site: HTML + Private Object Storage + VPS</title>
      <dc:creator>jiahui dong</dc:creator>
      <pubDate>Thu, 17 Sep 2026 08:48:43 +0000</pubDate>
      <link>https://dev.to/jiahui_dong_e52833db9f4c1/how-i-ship-a-controlled-download-site-html-private-object-storage-vps-24f7</link>
      <guid>https://dev.to/jiahui_dong_e52833db9f4c1/how-i-ship-a-controlled-download-site-html-private-object-storage-vps-24f7</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article was first published on my site: &lt;a href="https://resume.tensorview.cc/blog/secure-download-site-flask-qiniu.html" rel="noopener noreferrer"&gt;https://resume.tensorview.cc/blog/secure-download-site-flask-qiniu.html&lt;/a&gt;&lt;br&gt;
This is the real flow I used — front-end page, private object-storage URLs, then a VPS. Screenshots are from an anonymized demo brand.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your outline is right: page → object storage → wire uploads → rent a VPS → git and run. What usually fails in freelance delivery is not “can you write HTML,” but the edges: where secrets live, how links expire, chat-app cache, and the reverse proxy. Below is build, then deploy, plus the traps I hit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F18owxichwqnex6t8aced.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F18owxichwqnex6t8aced.jpg" alt="Demo download page (desktop): brand nav, Windows button, feature cards." width="800" height="695"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo download page (desktop): brand nav, Windows button, feature cards.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Lock acceptance before you code
&lt;/h2&gt;

&lt;p&gt;Agree what “done” means before coding. Otherwise you become their unpaid upload clerk every time the app binary changes. At minimum, put these acceptance checks in the quote.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Can download:&lt;/strong&gt; PC and Android each get a package; the button gives clear feedback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can expire:&lt;/strong&gt; signed URLs time out (often 24h) and stop working.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No secrets in the browser:&lt;/strong&gt; keys only in server env/config.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chat cache does not stick:&lt;/strong&gt; opening in WeChat/Line-like in-apps shows the latest page, not last week’s copy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-upload rounds:&lt;/strong&gt; how many free binary updates; extras billed per push.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Build: the front-end pages
&lt;/h2&gt;

&lt;p&gt;First deliverable is a download-center lookalike: download, FAQ, support. The download page switches desktop/mobile; FAQ locks install steps with screenshots; support lists regional contacts. Technically: HTML/CSS plus a little JS. The backend only mints download URLs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Download button:&lt;/strong&gt; POST for a URL, then navigate; 5s cooldown stops double-taps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asset busting:&lt;/strong&gt; version query on CSS/JS/images so clients see CSS updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anonymize for portfolio:&lt;/strong&gt; swap company name, ICP, real support contacts before blogging screenshots.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fljv6x7y5tq2va8rbzvj9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fljv6x7y5tq2va8rbzvj9.jpg" alt="Same page on the mobile tab: QR + Android button." width="800" height="695"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Same page on the mobile tab: QR + Android button.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmwma5jsartchhu2qhkuj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmwma5jsartchhu2qhkuj.jpg" alt="Narrow viewport: brand + nav stack for phone acceptance checks." width="420" height="2146"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Narrow viewport: brand + nav stack for phone acceptance checks.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Build: object storage (Qiniu in this case)
&lt;/h2&gt;

&lt;p&gt;Do not host installers as naked files on the VPS — bandwidth and leak control both suffer. Put binaries in a private object bucket; the backend SDK mints time-limited signed URLs. This case used Qiniu; overseas, the same pattern maps to S3 or R2.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bucket:&lt;/strong&gt; create a private space; bind a download CDN domain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keys:&lt;/strong&gt; create Access/Secret keys for the server only — never commit, never ship to JS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Object keys:&lt;/strong&gt; agree paths like exe/app-windows.exe and exe/app-android.apk; same names front and back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optional CDN locks:&lt;/strong&gt; timestamp hotlink protection / per-IP rate limits on top of signed URLs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Build: upload binaries and wire the API
&lt;/h2&gt;

&lt;p&gt;After uploading to the right keys, the backend (Flask here) exposes POST /api/download with type=pc|android. It reads client IP, applies a short rate window, calls the private URL API, and returns { success, url }. The front-end navigates to that URL. GET /api/health helps smoke-test deploys.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Smoke test:&lt;/strong&gt; curl the API — expect a long URL with e= and token=; opening it should start the download.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limit:&lt;/strong&gt; e.g. 2 requests / 5s per IP; over limit → 429 + wait_time for the UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Config:&lt;/strong&gt; prefer env vars; commit examples only, never real secrets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Deploy: rent a VPS
&lt;/h2&gt;

&lt;p&gt;A small VPS is enough (1C2G often fine for this guide site). Ubuntu LTS, open 80/443, SSH keys only. Point DNS A records at the public IP. If the client owns the domain, they add the record; otherwise accept on IP / temp domain first.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Deploy: git, run, reverse-proxy
&lt;/h2&gt;

&lt;p&gt;One chain: git clone/pull → venv → pip install → env vars → python app.py for dev, gunicorn bound to 127.0.0.1:5000 in prod. Nginx in front: static files + /api reverse-proxy. Finish with certbot HTTPS.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;systemd:&lt;/strong&gt; enable on boot and restart on failure — fewer midnight WeChat pings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs:&lt;/strong&gt; keep gunicorn/nginx error paths; first ask if it is 502 or 429.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permissions:&lt;/strong&gt; service user needs read on the app tree; tighten secret file modes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fresume.tensorview.cc%2Fimg%2Fblog%2Fdownload-site-faq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fresume.tensorview.cc%2Fimg%2Fblog%2Fdownload-site-faq.jpg" alt="FAQ page: screenshot-backed install steps cut repeat support." width="800" height="4958"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;FAQ page: screenshot-backed install steps cut repeat support.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4ps7fv2wnv4fs35n5lq5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4ps7fv2wnv4fs35n5lq5.jpg" alt="Support page (demo data): regional mail placeholders — swap for client contacts in production." width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Support page (demo data): regional mail placeholders — swap for client contacts in production.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Details that bite in production
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In-app cache:&lt;/strong&gt; Cache-Control: no-store on HTML; append _t= timestamps. Small detail, big for WeChat-like clients.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrong object key:&lt;/strong&gt; bucket has exe/foo.exe but code says foo.exe → Document not found. Match the console path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain vs CDN:&lt;/strong&gt; site domain and file CDN domain can differ; renew certs before expiry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Written checklist:&lt;/strong&gt; health URL, download URL, expired-link failure, in-app screenshot — send it to the client.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  8. End-to-end flow (quote appendix)
&lt;/h2&gt;

&lt;p&gt;Build: three pages → private bucket + CDN → upload binaries → Flask mint API wired to buttons. Deploy: VPS → git → venv → env → gunicorn + Nginx + HTTPS → accept in-app and expired-link cases. Clear edges and screenshotable checks make fixed-price gigs viable — if re-upload rounds are priced.&lt;/p&gt;

&lt;p&gt;Screenshots are anonymized demos, not a real client brand. Treat this as a checklist if you take similar gigs — same road as my Taobao-group and AI side-job notes, different slice.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Original post:&lt;/strong&gt; &lt;a href="https://resume.tensorview.cc/blog/secure-download-site-flask-qiniu.html" rel="noopener noreferrer"&gt;https://resume.tensorview.cc/blog/secure-download-site-flask-qiniu.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>flask</category>
      <category>qiniu</category>
      <category>nginx</category>
      <category>freelance</category>
    </item>
  </channel>
</rss>
