h-lab/src/main/java/com/hlab/yanalyst/domain/channel/TrackedPerson.java
hehihoho3@gmail.com 7a954907d7 feat: 인물 추적 — 피드에 '인물' 탭 추가
인물은 프로그램을 옮겨 다녀서 채널 시드로는 못 잡는다. 실측으로 확인했다:
이미 모은 롱폼 200건에서 제목에 인물명이 잡히는 건 4%뿐이다. 예능 클립 제목은
인물명 대신 상황을 쓰기 때문("혹시 젤리 가격 아세요"). 그래서 인물명으로
유튜브를 검색해 등장분을 찾는다.

- TrackedPerson 엔티티. 1명당 검색 101 units 라 하루 1회만 돌고 활성 인물
  상한(기본 10명)을 둔다. 10명이면 1,010 units/일.
- 인물 탭은 source 가 아니라 matchedPerson 으로 조회한다. 소스 채널에서 이미
  수집한 영상이 인물 검색에도 걸리면 소스 탭에서 사라지면 안 되기 때문이다.
- 새로 발견한 영상은 source=PERSON — 수집함/발굴 화이트리스트 밖이라 자동 격리.
- 시간당 조회수 하한(기본 20)으로 노이즈를 거른다. 인물명 검색에는 팬채널·
  커버곡·개인 브이로그가 대량으로 딸려 오는데(실측 30회 미만), 생 조회수로
  자르면 방금 올라온 영상까지 걸리므로 속도로 잰다. 적용 후 노이즈 6건이
  전부 빠지고 29건 모두 실질 소재만 남았다.

실측: 윤경호 → KBS 청룡시리즈어워즈 남우조연상(31.6만), KBS 한국방송(18.2만),
SBS 스브스 Drama. 전부 채널 시드에 없는 곳이라 인물 추적으로만 잡힌다.

테스트 3건 추가(속도 필터 경계·신선한 업로드 통과·값 없을 때 미배제) — 총 92건 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 22:38:48 +09:00

61 lines
1.6 KiB
Java

package com.hlab.yanalyst.domain.channel;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import java.time.LocalDateTime;
/**
* 추적 대상 인물. 인물은 프로그램을 옮겨 다니므로 채널만 쫓으면 놓치는 소재를 잡아낸다.
*
* <p>1명당 검색 1회(100 units)가 들어 하루 1회만 수집한다. 활성 인원은
* {@code hlab.feed.person.max-people} 로 제한한다.
*/
@Entity
@Table(name = "tracked_persons", indexes = @Index(name = "idx_tp_name", columnList = "name", unique = true))
@Getter
@NoArgsConstructor
public class TrackedPerson {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, length = 100)
private String name;
/** 꺼두면 수집 대상에서 빠진다(쿼터를 아끼면서 목록은 유지). */
@Column(nullable = false)
private Boolean enabled = true;
/** 마지막 수집 시각. */
private LocalDateTime lastCollectedAt;
/** 마지막 수집에서 새로 담은 롱폼 수. */
private Integer lastFound;
@CreationTimestamp
@Column(updatable = false)
private LocalDateTime createdAt;
public TrackedPerson(String name) {
this.name = name;
this.enabled = true;
}
public boolean isEnabled() {
return Boolean.TRUE.equals(this.enabled);
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void recordCollection(int found) {
this.lastCollectedAt = LocalDateTime.now();
this.lastFound = found;
}
}