Here's the formulation and Gurobi code for the ad placement optimization problem:

**Decision Variables:**

* `x`: Number of ads placed in DIY videos
* `y`: Number of ads placed in Shopping videos
* `z`: Number of ads placed in Unboxing videos

**Objective Function:**

Maximize total viewership: `10000x + 4000y + 9000z`

**Constraints:**

* **Budget Constraint:** `5000x + 3200y + 4000z <= 120000`
* **DIY Video Limit:** `x <= 5`
* **Unboxing Video Proportion:** `z <= 0.5 * (x + y + z)`
* **Shopping Video Proportion:** `y >= 0.2 * (x + y + z)`
* **Non-negativity:** `x, y, z >= 0`
* **Integer Constraint:** `x, y, z` must be integers (since you can't place fractions of ads)


```python
import gurobipy as gp

# Create a new model
model = gp.Model("AdPlacement")

# Create variables
x = model.addVar(vtype=gp.GRB.INTEGER, name="DIY_ads")
y = model.addVar(vtype=gp.GRB.INTEGER, name="Shopping_ads")
z = model.addVar(vtype=gp.GRB.INTEGER, name="Unboxing_ads")

# Set objective function
model.setObjective(10000*x + 4000*y + 9000*z, gp.GRB.MAXIMIZE)

# Add constraints
model.addConstr(5000*x + 3200*y + 4000*z <= 120000, "Budget")
model.addConstr(x <= 5, "DIY_limit")
model.addConstr(z <= 0.5 * (x + y + z), "Unboxing_proportion")
model.addConstr(y >= 0.2 * (x + y + z), "Shopping_proportion")

# Optimize model
model.optimize()

# Print results
if model.status == gp.GRB.OPTIMAL:
    print(f"Optimal Viewership: {model.objVal}")
    print(f"DIY Ads: {x.x}")
    print(f"Shopping Ads: {y.x}")
    print(f"Unboxing Ads: {z.x}")
else:
    print("No solution found.")

```
