11package run .ikaros .plugin .bgmtv ;
22
3- import static run .ikaros .api .infra .utils .ReactiveBeanUtils .copyProperties ;
4-
53import java .time .LocalDateTime ;
64import java .time .format .DateTimeFormatter ;
75import java .time .format .DateTimeFormatterBuilder ;
119import java .util .List ;
1210import java .util .Map ;
1311import java .util .Objects ;
14- import java .util .Optional ;
15- import java .util .Set ;
16- import java .util .stream .Collectors ;
1712import lombok .extern .slf4j .Slf4j ;
1813import org .apache .commons .lang3 .StringUtils ;
1914import org .pf4j .Extension ;
20- import org .springframework .core .io .buffer .DataBufferFactory ;
21- import org .springframework .core .io .buffer .DefaultDataBufferFactory ;
22- import org .springframework .util .Assert ;
23- import reactor .core .publisher .Flux ;
24- import reactor .core .publisher .Mono ;
25- import run .ikaros .api .core .attachment .Attachment ;
26- import run .ikaros .api .core .attachment .AttachmentConst ;
2715import run .ikaros .api .core .attachment .AttachmentOperate ;
28- import run .ikaros .api .core .attachment .AttachmentUploadCondition ;
16+ import run .ikaros .api .core .character .Character ;
17+ import run .ikaros .api .core .person .Person ;
2918import run .ikaros .api .core .subject .Episode ;
30- import run .ikaros .api .core .subject .EpisodeOperate ;
3119import run .ikaros .api .core .subject .Subject ;
32- import run .ikaros .api .core .subject .SubjectSync ;
33- import run .ikaros .api .core .subject .SubjectSyncPlatformOperate ;
3420import run .ikaros .api .core .subject .SubjectSynchronizer ;
3521import run .ikaros .api .core .tag .Tag ;
36- import run .ikaros .api .core .tag .TagOperate ;
37- import run .ikaros .api .infra .utils .FileUtils ;
38- import run .ikaros .api .infra .utils .ReactiveBeanUtils ;
3922import run .ikaros .api .store .enums .EpisodeGroup ;
4023import run .ikaros .api .store .enums .SubjectSyncPlatform ;
4124import run .ikaros .api .store .enums .SubjectType ;
42- import run .ikaros .api .store .enums .TagType ;
4325import run .ikaros .plugin .bgmtv .model .BgmTvEpisode ;
4426import run .ikaros .plugin .bgmtv .model .BgmTvEpisodeType ;
4527import run .ikaros .plugin .bgmtv .model .BgmTvSubject ;
@@ -53,202 +35,74 @@ public class BgmTvSubjectSynchronizer implements SubjectSynchronizer {
5335
5436 private final BgmTvRepository bgmTvRepository ;
5537 private final AttachmentOperate attachmentOperate ;
56- private final TagOperate tagOperate ;
57- private final EpisodeOperate episodeOperate ;
58- private final SubjectSyncPlatformOperate syncOperate ;
38+
39+ private ThreadLocal <BgmTvSubject > subjectThreadLocal = new ThreadLocal <>();
5940
6041 public BgmTvSubjectSynchronizer (BgmTvRepository bgmTvRepository ,
61- AttachmentOperate attachmentOperate , TagOperate tagOperate ,
62- EpisodeOperate episodeOperate ,
63- SubjectSyncPlatformOperate syncOperate ) {
42+ AttachmentOperate attachmentOperate ) {
6443 this .bgmTvRepository = bgmTvRepository ;
6544 this .attachmentOperate = attachmentOperate ;
66- this .tagOperate = tagOperate ;
67- this .episodeOperate = episodeOperate ;
68- this .syncOperate = syncOperate ;
6945 }
7046
47+
7148 @ Override
7249 public SubjectSyncPlatform getSyncPlatform () {
7350 return SubjectSyncPlatform .BGM_TV ;
7451 }
7552
76-
7753 @ Override
78- public synchronized Mono <Subject > pull (String id ) {
79- Assert .hasText (id , "bgmtv id must has text." );
80-
81- if (id .startsWith ("https://" )) {
82- id = id .replace ("https://bgm.tv/subject/" , "" );
83- }
84- final Long subjectId = Long .valueOf (id );
85-
86- BgmTvSubject bgmTvSubject = bgmTvRepository .getSubject (Long .valueOf (id ));
87- if (bgmTvSubject == null || bgmTvSubject .getId () == null ) {
88- log .warn ("Pull bgmtv subject is null, skip operate." );
89- return Mono .empty ();
90- }
91-
92- Subject subject =
93- convert (Objects .requireNonNull (bgmTvSubject ));
94- if (Objects .isNull (subject )) {
95- log .warn ("Pull subject is null, skip operate." );
96- return Mono .empty ();
97- }
98-
99- log .info ("Pull subject:[{}] by platform:[{}] and id:[{}]" ,
100- subject .getName (), getSyncPlatform ().name (), id );
101-
102- List <Episode > episodes =
103- bgmTvRepository .findEpisodesBySubjectId (Long .valueOf (id ), null ,
104- null , null )
105- .stream ()
106- .map (bgmTvEpisode -> SubjectType .MUSIC .equals (subject .getType ()) ?
107- convertMusicEpisode (bgmTvEpisode ) : convertEpisode (bgmTvEpisode ))
108- .map (episode -> episode .setSubjectId (subject .getId ()))
109- .toList ();
110- log .info ("Pull episode count:[{}] by platform:[{}] and id:[{}]" ,
111- episodes .size (), getSyncPlatform ().name (), id );
112- Mono <List <Episode >> episodesMono =
113- episodeOperate .updateEpisodesWithSubjectId (subjectId , episodes ).collectList ();;
114-
115- // save bgmtv tags
116- Set <String > bgmTvSubTagNames = bgmTvSubject .getTags ().stream ()
117- .map (BgmTvTag ::getName ).collect (Collectors .toSet ());
118- Mono <List <Tag >> tagsMono =
119- episodesMono .thenMany (Flux .fromStream (bgmTvSubTagNames .parallelStream ()))
120- .map (tagName -> Tag .builder ()
121- .createTime (LocalDateTime .now ())
122- .type (TagType .SUBJECT )
123- .masterId (subject .getId ())
124- .name (tagName )
125- .userId (-1L )
126- .build ())
127- .flatMap (tagOperate ::create )
128- .collectList ();
129-
130- // save subject sync entity
131- SubjectSync subjectSync = SubjectSync .builder ()
132- .platform (getSyncPlatform ())
133- .platformId (id )
134- .subjectId (subject .getId ())
135- .syncTime (LocalDateTime .now ())
136- .build ();
137- Mono <SubjectSync > syncMono = tagsMono .then (syncOperate .save (subjectSync ));
138-
139- // download cover image and update url
140- if (StringUtils .isNotBlank (subject .getCover ())
141- && subject .getCover ().startsWith ("http" )) {
142- String coverUrl = subject .getCover ();
143- String coverFileName = StringUtils .isNotBlank (subject .getNameCn ())
144- ? subject .getNameCn () : subject .getName ();
145- coverFileName =
146- System .currentTimeMillis () + "-" + coverFileName
147- + "." + FileUtils .parseFilePostfix (FileUtils .parseFileName (coverUrl ));
148- byte [] bytes = bgmTvRepository .downloadCover (coverUrl );
149- DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory ();
150- return syncMono .then (attachmentOperate .upload (AttachmentUploadCondition .builder ()
151- .parentId (AttachmentConst .COVER_DIRECTORY_ID )
152- .name (coverFileName ).dataBufferFlux (Mono .just (dataBufferFactory .wrap (bytes )).flux ())
153- .build ()))
154- .map (Attachment ::getUrl )
155- .map (subject ::setCover );
54+ public Subject fetchSubjectWithPlatformId (String platformId ) {
55+ BgmTvSubject bgmTvSubject = bgmTvRepository .getSubject (Long .valueOf (platformId ));
56+ subjectThreadLocal .set (bgmTvSubject );
57+ if (Objects .isNull (bgmTvSubject )) {
58+ return null ;
15659 }
157-
158- return syncMono .then (Mono .just (subject ));
60+ return new Subject ()
61+ .setId (Long .valueOf (String .valueOf (bgmTvSubject .getId ())))
62+ .setType (convertType (bgmTvSubject .getType (), bgmTvSubject .getPlatform ()))
63+ .setName (bgmTvSubject .getName ())
64+ .setNameCn (StringUtils .isNotBlank (bgmTvSubject .getNameCn ())
65+ ? bgmTvSubject .getNameCn () : bgmTvSubject .getName ())
66+ .setInfobox (bgmTvSubject .getInfobox ())
67+ .setSummary (bgmTvSubject .getSummary ())
68+ .setNsfw (bgmTvSubject .getNsfw ())
69+ .setAirTime (convertAirTime (
70+ Objects .nonNull (bgmTvSubject .getDate ()) ? bgmTvSubject .getDate () : "1999-09-09" ))
71+ .setCover (bgmTvSubject .getImages ().getLarge ());
15972 }
16073
16174 @ Override
162- public Mono <Subject > merge (Subject subject , String platformId ) {
163- Assert .notNull (subject , "subject must not null." );
164- Assert .hasText (platformId , "bgmtv id must has text." );
165- final Long subjectId = subject .getId ();
166-
167- // search bgmtv subject info
168- BgmTvSubject bgmTvSubject = bgmTvRepository .getSubject (Long .valueOf (platformId ));
169- if (bgmTvSubject == null || bgmTvSubject .getId () == null ) {
170- log .warn ("Pull subject is null, skip operate." );
171- return Mono .empty ();
172- }
173-
174- // merge bgmtv subject
175- subject = mergeBgmTvSubject (subject , bgmTvSubject );
176- log .info ("Merge subject:[{}] by platform:[{}] and id:[{}]" ,
177- Objects .requireNonNull (subject ).getName (), getSyncPlatform ().name (), platformId );
178-
179-
180- // merge bgmtv subject episodes
181- List <Episode > episodes =
182- bgmTvRepository .findEpisodesBySubjectId (Long .valueOf (platformId ), null ,
183- null , null )
184- .stream ()
185- .map (this ::convertEpisode )
186- .toList ();
187-
188- Mono <List <Episode >> episodesMono =
189- episodeOperate .updateEpisodesWithSubjectId (subjectId , episodes ).collectList ();
190-
191-
192- // save subject sync entity
193- SubjectSync subjectSync = SubjectSync .builder ()
194- .platform (getSyncPlatform ())
195- .platformId (platformId )
196- .subjectId (subjectId )
197- .syncTime (LocalDateTime .now ())
198- .build ();
199- Mono <SubjectSync > syncMono =
200- episodesMono .then (syncOperate .findBySubjectIdAndPlatformAndPlatformId (
201- subjectId , getSyncPlatform (), platformId ))
202- .switchIfEmpty (syncOperate .save (subjectSync ));
75+ public List <Episode > fetchEpisodesWithPlatformId (String platformId ) {
76+ BgmTvSubject bgmTvSubject = subjectThreadLocal .get ();
77+ return bgmTvRepository .findEpisodesBySubjectId (Long .valueOf (platformId ), null ,
78+ null , null )
79+ .stream ()
80+ .map (bgmTvEpisode -> bgmTvSubject .getType () == 3 ?
81+ convertMusicEpisode (bgmTvEpisode ) : convertEpisode (bgmTvEpisode ))
82+ .toList ();
83+ }
20384
204- DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory ();
205- // merge tags
206- return syncMono .thenMany (tagOperate .findAll (TagType .SUBJECT , subject .getId (), null ))
207- .map (Tag ::getName )
208- .filter (StringUtils ::isNotBlank )
209- .collect (Collectors .toSet ())
210- .flatMapMany (existsTags ->
211- Flux .fromStream (bgmTvSubject .getTags ().parallelStream ())
212- .map (BgmTvTag ::getName )
213- .filter (bgmTvTagName -> !existsTags .contains (bgmTvTagName ))
214- )
85+ @ Override
86+ public List <Tag > fetchTagsWithPlatformId (String platformId ) {
87+ BgmTvSubject bgmTvSubject = subjectThreadLocal .get ();
88+ return bgmTvSubject .getTags ().stream ()
89+ .map (BgmTvTag ::getName )
21590 .map (name -> Tag .builder ()
216- .type (TagType .SUBJECT )
217- .masterId (Long .valueOf (bgmTvSubject .getId ()))
21891 .name (name )
219- .createTime (LocalDateTime .now ())
220- .userId (-1L )
22192 .build ())
222- .flatMap (tagOperate ::create )
223- .collectList ()
224- // merge cover
225- .then (Mono .just (subject ))
226- .filter (sub -> StringUtils .isBlank (sub .getCover ()))
227- .filter (cover -> StringUtils .isNotBlank (bgmTvSubject .getImages ().getLarge ()))
228- .map (cover -> bgmTvSubject .getImages ().getLarge ())
229- .map (bgmTvRepository ::downloadCover )
230- .map (bytes -> AttachmentUploadCondition .builder ()
231- .parentId (AttachmentConst .COVER_DIRECTORY_ID )
232- .name (
233- System .currentTimeMillis ()
234- + "-" + (StringUtils .isNotBlank (bgmTvSubject .getNameCn ())
235- ? bgmTvSubject .getNameCn () : bgmTvSubject .getName ())
236- + "." + FileUtils .parseFilePostfix (
237- FileUtils .parseFileName (bgmTvSubject .getImages ().getLarge ()))
238- )
239- .dataBufferFlux (Mono .just (dataBufferFactory .wrap (bytes )).flux ())
240- .build ())
241- .flatMap (attachmentOperate ::upload )
242- .map (Attachment ::getUrl )
243- .map (subject ::setCover )
93+ .toList ();
94+ }
24495
245- .then (Mono .just (subject ));
96+ @ Override
97+ public List <Person > fetchPersonsWithPlatformId (String platformId ) {
98+ // TODO 条目人物相关
99+ return List .of ();
246100 }
247101
248102 @ Override
249- public Mono < Subject > pullSelfAndRelations (String s ) {
250- // TODO pull self and all relations
251- return Mono . empty ();
103+ public List < Character > fetchCharactersWithPlatformId (String platformId ) {
104+ // TODO 条目角色相关
105+ return List . of ();
252106 }
253107
254108 private Subject mergeBgmTvSubject (Subject subject , BgmTvSubject bgmTvSubject ) {
@@ -336,25 +190,6 @@ private Episode convertMusicEpisode(BgmTvEpisode bgmTvEpisode) {
336190 }
337191
338192
339- private Subject convert (BgmTvSubject bgmTvSubject ) {
340- if (Objects .isNull (bgmTvSubject )) {
341- return null ;
342- }
343- return new Subject ()
344- .setId (Long .valueOf (String .valueOf (bgmTvSubject .getId ())))
345- .setType (convertType (bgmTvSubject .getType (), bgmTvSubject .getPlatform ()))
346- .setName (bgmTvSubject .getName ())
347- .setNameCn (StringUtils .isNotBlank (bgmTvSubject .getNameCn ())
348- ? bgmTvSubject .getNameCn () : bgmTvSubject .getName ())
349- .setInfobox (bgmTvSubject .getInfobox ())
350- .setSummary (bgmTvSubject .getSummary ())
351- .setNsfw (bgmTvSubject .getNsfw ())
352- .setAirTime (convertAirTime (
353- Objects .nonNull (bgmTvSubject .getDate ()) ? bgmTvSubject .getDate () : "1999-09-09" ))
354- .setCover (bgmTvSubject .getImages ().getLarge ());
355- }
356-
357-
358193 private LocalDateTime convertAirTime (String date ) {
359194 if (StringUtils .isBlank (date )) {
360195 return null ;
0 commit comments