feat: 숏폼 큐 엔티티(ShortformJob/Clip)와 리포지토리
rawOutput은 파싱 성공 여부와 무관하게 원문 보존. videoId 유니크로 중복 등록 방지. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
3fda9077a6
commit
005df3225b
@ -0,0 +1,51 @@
|
|||||||
|
package com.hlab.yanalyst.domain.shortform;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/** 작업 하나에서 나온 구간(ID 1~5) 하나. capcutJson이 실제 편집 프로그램에 붙여넣는 값. */
|
||||||
|
@Entity
|
||||||
|
@Table(name = "shortform_clip")
|
||||||
|
@Getter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class ShortformClip {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||||
|
@JoinColumn(name = "job_id")
|
||||||
|
private ShortformJob job;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private Integer clipNo;
|
||||||
|
|
||||||
|
@Column(length = 100)
|
||||||
|
private String titleTop;
|
||||||
|
|
||||||
|
@Column(length = 200)
|
||||||
|
private String titleMain;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "TEXT")
|
||||||
|
private String capcutJson;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "TEXT")
|
||||||
|
private String titleCandidates;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "TEXT")
|
||||||
|
private String durationTable;
|
||||||
|
|
||||||
|
static ShortformClip of(ShortformJob job, ParsedClip p) {
|
||||||
|
ShortformClip clip = new ShortformClip();
|
||||||
|
clip.job = job;
|
||||||
|
clip.clipNo = p.clipNo();
|
||||||
|
clip.titleTop = p.titleTop();
|
||||||
|
clip.titleMain = p.titleMain();
|
||||||
|
clip.capcutJson = p.capcutJson();
|
||||||
|
clip.titleCandidates = p.titleCandidates();
|
||||||
|
clip.durationTable = p.durationTable();
|
||||||
|
return clip;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
package com.hlab.yanalyst.domain.shortform;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.hibernate.annotations.CreationTimestamp;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 숏폼 큐 작업 하나 = 유튜브 영상 하나. Opal "숏폼 생성기" 실행 결과를 담는다.
|
||||||
|
*
|
||||||
|
* <p>rawOutput은 파싱 성공 여부와 무관하게 항상 원문을 보존한다 — 파서가 놓친 정보는
|
||||||
|
* 여기서 복구할 수 있다.
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "shortform_job",
|
||||||
|
indexes = @Index(name = "idx_sfj_video_id", columnList = "videoId", unique = true))
|
||||||
|
@Getter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class ShortformJob {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(nullable = false, length = 500)
|
||||||
|
private String youtubeUrl;
|
||||||
|
|
||||||
|
@Column(nullable = false, length = 20)
|
||||||
|
private String videoId;
|
||||||
|
|
||||||
|
/** 결과의 첫 클립 title_main. 목록 표시에 쓴다. */
|
||||||
|
@Column(length = 300)
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(nullable = false, length = 20)
|
||||||
|
private ShortformJobStatus status = ShortformJobStatus.PENDING;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "TEXT")
|
||||||
|
private String rawOutput;
|
||||||
|
|
||||||
|
@CreationTimestamp
|
||||||
|
@Column(updatable = false)
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
private LocalDateTime completedAt;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "job", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||||
|
@OrderBy("clipNo ASC")
|
||||||
|
private List<ShortformClip> clips = new ArrayList<>();
|
||||||
|
|
||||||
|
public static ShortformJob create(String youtubeUrl, String videoId) {
|
||||||
|
ShortformJob job = new ShortformJob();
|
||||||
|
job.youtubeUrl = youtubeUrl;
|
||||||
|
job.videoId = videoId;
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyResult(String raw, List<ParsedClip> parsed) {
|
||||||
|
this.rawOutput = raw;
|
||||||
|
this.clips.clear();
|
||||||
|
for (ParsedClip p : parsed) {
|
||||||
|
this.clips.add(ShortformClip.of(this, p));
|
||||||
|
}
|
||||||
|
this.title = parsed.isEmpty() ? this.title : parsed.get(0).titleMain();
|
||||||
|
this.status = ShortformJobStatus.DONE;
|
||||||
|
this.completedAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markFailed(String raw) {
|
||||||
|
this.rawOutput = raw;
|
||||||
|
this.status = ShortformJobStatus.FAILED;
|
||||||
|
this.completedAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package com.hlab.yanalyst.domain.shortform;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface ShortformJobRepository extends JpaRepository<ShortformJob, Long> {
|
||||||
|
Optional<ShortformJob> findByVideoId(String videoId);
|
||||||
|
List<ShortformJob> findAllByOrderByCreatedAtDesc();
|
||||||
|
List<ShortformJob> findByStatusOrderByCreatedAtAsc(ShortformJobStatus status);
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
package com.hlab.yanalyst.domain.shortform;
|
||||||
|
|
||||||
|
public enum ShortformJobStatus { PENDING, DONE, FAILED }
|
||||||
Loading…
Reference in New Issue
Block a user