인물은 프로그램을 옮겨 다녀서 채널 시드로는 못 잡는다. 실측으로 확인했다:
이미 모은 롱폼 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>
61 lines
1.6 KiB
Java
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;
|
|
}
|
|
}
|