Fix iqiyi

This commit is contained in:
cxfksword
2023-03-10 14:20:01 +08:00
parent bae4b93487
commit 00225ab894
10 changed files with 170 additions and 96 deletions

View File

@@ -51,9 +51,10 @@ namespace Jellyfin.Plugin.Danmu.Test
{
try
{
var vid = "3493131456125200"; // 电视剧
// var vid = "429872"; // 电
var result = await api.GetVideoAsync(vid, CancellationToken.None);
// var id = "j5998jke4w"; // 综艺
// var id = "xkt6z3z798"; // 电视剧
var id = "1e54n0pt5ro"; // 电影
var result = await api.GetVideoAsync(id, CancellationToken.None);
Console.WriteLine(result);
}
catch (Exception ex)

View File

@@ -42,7 +42,8 @@ namespace Jellyfin.Plugin.Danmu.Test
var item = new Movie
{
Name = "少年派的奇幻漂流"
Name = "无间道3终极无间",
ProductionYear = 2003
};
var list = new List<LibraryEvent>();
@@ -134,6 +135,7 @@ namespace Jellyfin.Plugin.Danmu.Test
}
[TestMethod]
public void TestGetMedia()
{
@@ -143,7 +145,7 @@ namespace Jellyfin.Plugin.Danmu.Test
try
{
var api = new Iqiyi(loggerFactory);
var media = await api.GetMedia(new Season(), "19rrhaen1h");
var media = await api.GetMedia(new Season(), "19rrmacgqs");
Console.WriteLine(media);
}
catch (Exception ex)
@@ -163,7 +165,7 @@ namespace Jellyfin.Plugin.Danmu.Test
try
{
var api = new Iqiyi(loggerFactory);
var media = await api.GetMedia(new Season(), "25apmxdhmmh");
var media = await api.GetMedia(new Season(), "1m5gylxxqu0");
Console.WriteLine(media);
}
catch (Exception ex)

View File

@@ -0,0 +1,24 @@
using System.Text.RegularExpressions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace Jellyfin.Plugin.Danmu.Scrapers.Iqiyi.Entity
{
public class IqiyiHtmlAlbumInfo
{
[JsonPropertyName("qipuId")]
public Int64 AlbumId { get; set; }
[JsonPropertyName("albumName")]
public string AlbumName { get; set; }
[JsonPropertyName("albumPageUrl")]
public string AlbumUrl { get; set; }
[JsonPropertyName("videoCount")]
public int VideoCount { get; set; }
}
}

View File

@@ -0,0 +1,53 @@
using System.Text.RegularExpressions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace Jellyfin.Plugin.Danmu.Scrapers.Iqiyi.Entity
{
public class IqiyiHtmlVideoInfo
{
private static readonly Regex regLinkId = new Regex(@"v_(\w+?)\.html", RegexOptions.Compiled);
[JsonPropertyName("albumQipuId")]
public Int64 AlbumId { get; set; }
[JsonPropertyName("tvid")]
public Int64 TvId { get; set; }
[JsonPropertyName("videoName")]
public String VideoName { get; set; }
[JsonPropertyName("videoUrl")]
public String VideoUrl { get; set; }
[JsonPropertyName("channelName")]
public string ChannelName { get; set; }
[JsonPropertyName("duration")]
public int Duration { get; set; }
[JsonIgnore]
public List<IqiyiEpisode> Epsodelist { get; set; }
[JsonIgnore]
public IqiyiHtmlAlbumInfo? AlbumInfo { get; set; }
[JsonIgnore]
public string LinkId
{
get
{
var match = regLinkId.Match(VideoUrl);
if (match.Success && match.Groups.Count > 1)
{
return match.Groups[1].Value.Trim();
}
else
{
return null;
}
}
}
}
}

View File

@@ -11,7 +11,7 @@ namespace Jellyfin.Plugin.Danmu.Scrapers.Iqiyi.Entity
{
public class IqiyiSearchAlbumInfo
{
private static readonly Regex regLinkId = new Regex(@"(v_|a_)(\w+?)\.html", RegexOptions.Compiled);
private static readonly Regex regLinkId = new Regex(@"v_(\w+?)\.html", RegexOptions.Compiled);
[JsonPropertyName("albumId")]
public Int64 AlbumId { get; set; }
@@ -66,14 +66,24 @@ namespace Jellyfin.Plugin.Danmu.Scrapers.Iqiyi.Entity
[JsonPropertyName("videoinfos")]
public List<IqiyiSearchVideoInfo> VideoInfos { get; set; }
public string LinkId
/// <summary>
/// 编码后的视频ID
/// </summary>
[JsonIgnore]
public string? LinkId
{
get
{
var match = regLinkId.Match(Link);
if (match.Success && match.Groups.Count > 2)
var link = Link;
if (VideoInfos != null && VideoInfos.Count > 0)
{
return match.Groups[2].Value.Trim();
link = VideoInfos.First().ItemLink;
}
var match = regLinkId.Match(link);
if (match.Success && match.Groups.Count > 1)
{
return match.Groups[1].Value.Trim();
}
else
{

View File

@@ -13,5 +13,8 @@ namespace Jellyfin.Plugin.Danmu.Scrapers.Iqiyi.Entity
{
[JsonPropertyName("tvId")]
public Int64 VideoId { get; set; }
[JsonPropertyName("itemLink")]
public string ItemLink { get; set; }
}
}

View File

@@ -10,8 +10,6 @@ namespace Jellyfin.Plugin.Danmu.Scrapers.Iqiyi.Entity
{
public class IqiyiVideo
{
private static readonly Regex regLinkId = new Regex(@"(v_|a_)(\w+?)\.html", RegexOptions.Compiled);
[JsonPropertyName("albumId")]
public Int64 AlbumId { get; set; }
[JsonPropertyName("tvId")]
@@ -34,23 +32,5 @@ namespace Jellyfin.Plugin.Danmu.Scrapers.Iqiyi.Entity
[JsonPropertyName("epsodelist")]
public List<IqiyiEpisode> Epsodelist { get; set; }
public string LinkId
{
get
{
if (string.IsNullOrEmpty(AlbumUrl)) return null;
var match = regLinkId.Match(AlbumUrl);
if (match.Success && match.Groups.Count > 2)
{
return match.Groups[2].Value.Trim();
}
else
{
return null;
}
}
}
}
}

View File

@@ -24,7 +24,7 @@ namespace Jellyfin.Plugin.Danmu.Scrapers.Iqiyi.ExternalId
public ExternalIdMediaType? Type => null;
/// <inheritdoc />
public string UrlFormatString => "https://www.iqiyi.com/a_{0}.html";
public string UrlFormatString => "https://www.iqiyi.com/v_{0}.html";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is Season;

View File

@@ -47,10 +47,6 @@ public class Iqiyi : AbstractScraper
var videos = await this._api.SearchAsync(searchName, CancellationToken.None).ConfigureAwait(false);
foreach (var video in videos)
{
var videoId = video.VideoId;
var title = video.Name;
var pubYear = video.Year;
if (isMovieItemType && video.ChannelName != "电影")
{
continue;
@@ -64,9 +60,9 @@ public class Iqiyi : AbstractScraper
list.Add(new ScraperSearchInfo()
{
Id = $"{video.LinkId}",
Name = title,
Name = video.Name,
Category = video.ChannelName,
Year = pubYear,
Year = video.Year,
EpisodeSize = video.ItemTotalNumber,
});
}
@@ -82,7 +78,6 @@ public class Iqiyi : AbstractScraper
var videos = await this._api.SearchAsync(searchName, CancellationToken.None).ConfigureAwait(false);
foreach (var video in videos)
{
var videoId = video.VideoId;
var title = video.Name;
var pubYear = video.Year;
@@ -126,15 +121,9 @@ public class Iqiyi : AbstractScraper
return null;
}
// id是编码后的需要还原为真实id
// id是编码后的
var isMovieItemType = item is MediaBrowser.Controller.Entities.Movies.Movie;
var tvId = await _api.GetTvId(id, !isMovieItemType, CancellationToken.None);
if (string.IsNullOrEmpty(tvId))
{
return null;
}
var video = await _api.GetVideoAsync(tvId, CancellationToken.None).ConfigureAwait(false);
var video = await _api.GetVideoAsync(id, CancellationToken.None).ConfigureAwait(false);
if (video == null)
{
log.LogInformation("[{0}]获取不到视频信息id={1}", this.Name, id);
@@ -143,7 +132,7 @@ public class Iqiyi : AbstractScraper
var media = new ScraperMedia();
media.Id = video.LinkId; // 使用url编码后的idmovie使用vid电视剧使用aid
media.Id = id; // 使用url编码后的id
if (isMovieItemType && video.Epsodelist != null && video.Epsodelist.Count > 0)
{
media.CommentId = $"{video.Epsodelist[0].TvId}";
@@ -167,14 +156,14 @@ public class Iqiyi : AbstractScraper
return null;
}
// id是编码后的需要还原为真实id
var tvId = await _api.GetTvId(id, false, CancellationToken.None);
if (string.IsNullOrEmpty(tvId))
// id是编码后的
var video = await _api.GetVideoBaseAsync(id, CancellationToken.None).ConfigureAwait(false);
if (video == null)
{
return null;
}
return new ScraperEpisode() { Id = id, CommentId = tvId };
return new ScraperEpisode() { Id = id, CommentId = $"{video.TvId}" };
}
public override async Task<ScraperDanmaku?> GetDanmuContent(BaseItem item, string commentId)

View File

@@ -25,7 +25,9 @@ namespace Jellyfin.Plugin.Danmu.Scrapers.Iqiyi;
public class IqiyiApi : AbstractApi
{
private static readonly Regex regTvId = new Regex(@"""tvid"":(\d+?),", RegexOptions.Compiled);
private new const string HTTP_USER_AGENT = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/93.0.4577.63";
private static readonly Regex regVideoInfo = new Regex(@"""videoInfo"":(\{.+?\}),", RegexOptions.Compiled);
private static readonly Regex regAlbumInfo = new Regex(@"""albumInfo"":(\{.+?\}),", RegexOptions.Compiled);
private TimeLimiter _timeConstraint = TimeLimiter.GetFromMaxCountByInterval(1, TimeSpan.FromMilliseconds(1000));
@@ -42,6 +44,7 @@ public class IqiyiApi : AbstractApi
public IqiyiApi(ILoggerFactory loggerFactory)
: base(loggerFactory.CreateLogger<IqiyiApi>())
{
httpClient.DefaultRequestHeaders.Add("user-agent", HTTP_USER_AGENT);
}
@@ -70,7 +73,6 @@ public class IqiyiApi : AbstractApi
var searchResult = await response.Content.ReadFromJsonAsync<IqiyiSearchResult>(_jsonOptions, cancellationToken).ConfigureAwait(false);
if (searchResult != null && searchResult.Data != null)
{
// 综艺没找到接口获取准确的正片列表,忽略不处理
result = searchResult.Data.DocInfos
.Where(x => x.Score > 0.7)
.Select(x => x.AlbumDocInfo)
@@ -82,7 +84,7 @@ public class IqiyiApi : AbstractApi
return result;
}
public async Task<IqiyiVideo?> GetVideoAsync(string id, CancellationToken cancellationToken)
public async Task<IqiyiHtmlVideoInfo?> GetVideoAsync(string id, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(id))
{
@@ -91,42 +93,73 @@ public class IqiyiApi : AbstractApi
var cacheKey = $"video_{id}";
var expiredOption = new MemoryCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30) };
if (_memoryCache.TryGetValue<IqiyiVideo?>(cacheKey, out var video))
if (_memoryCache.TryGetValue<IqiyiHtmlVideoInfo?>(cacheKey, out var video))
{
return video;
}
// 获取影片信息(vid)https://pcw-api.iqiyi.com/video/video/baseinfo/429872
// 获取电视剧信息(aid)https://pcw-api.iqiyi.com/album/album/baseinfo/5328486914190101
// 获取电视剧剧集信息(综艺不适用)(aid)https://pcw-api.iqiyi.com/albums/album/avlistinfo?aid=5328486914190101&page=1&size=100
// 获取电视剧剧集信息(综艺不适用)(aid)https://pcw-api.iqiyi.com/albums/album/avlistinfo?aid=5328486914190101&page=1&size=10
// 获取电视剧剧集信息(综艺不适用)(aid)https://pub.m.iqiyi.com/h5/main/videoList/album/?albumId=5328486914190101&size=39&page=1&needPrevue=true&needVipPrevue=false
var url = $"https://pcw-api.iqiyi.com/video/video/baseinfo/{id}";
var response = await httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<IqiyiVideoResult>(_jsonOptions, cancellationToken).ConfigureAwait(false);
if (result != null && result.Data != null)
var videoInfo = await GetVideoBaseAsync(id, cancellationToken).ConfigureAwait(false);
if (videoInfo != null)
{
if (result.Data.ChannelId == 6)
if (videoInfo.ChannelName == "综艺" && videoInfo.AlbumInfo != null)
{ // 综艺需要特殊处理
result.Data.Epsodelist = await this.GetZongyiEpisodesAsync($"{result.Data.AlbumId}", cancellationToken).ConfigureAwait(false);
videoInfo.Epsodelist = await this.GetZongyiEpisodesAsync($"{videoInfo.AlbumInfo.AlbumId}", cancellationToken).ConfigureAwait(false);
}
else if (result.Data.ChannelId != 1)
else if (videoInfo.ChannelName != "电影" && videoInfo.AlbumInfo != null)
{ // 电视剧需要再获取剧集信息
result.Data.Epsodelist = await this.GetEpisodesAsync($"{result.Data.AlbumId}", result.Data.VideoCount, cancellationToken).ConfigureAwait(false);
videoInfo.Epsodelist = await this.GetEpisodesAsync($"{videoInfo.AlbumId}", videoInfo.AlbumInfo.VideoCount, cancellationToken).ConfigureAwait(false);
}
else
else if (videoInfo.ChannelName == "电影")
{ // 电影
result.Data.Epsodelist = new List<IqiyiEpisode>() {
new IqiyiEpisode() {TvId = result.Data.TvId, Order = 1, Name = result.Data.Name, Duration = result.Data.Duration, PlayUrl = result.Data.PlayUrl}
var duration = new TimeSpan(0, 0, videoInfo.Duration);
videoInfo.Epsodelist = new List<IqiyiEpisode>() {
new IqiyiEpisode() {TvId = videoInfo.TvId, Order = 1, Name = videoInfo.VideoName, Duration = duration.ToString(@"hh\:mm\:ss"), PlayUrl = videoInfo.VideoUrl}
};
}
_memoryCache.Set<IqiyiVideo?>(cacheKey, result.Data, expiredOption);
return result.Data;
_memoryCache.Set<IqiyiHtmlVideoInfo?>(cacheKey, videoInfo, expiredOption);
return videoInfo;
}
_memoryCache.Set<IqiyiVideo?>(cacheKey, null, expiredOption);
_memoryCache.Set<IqiyiHtmlVideoInfo?>(cacheKey, null, expiredOption);
return null;
}
public async Task<IqiyiHtmlVideoInfo?> GetVideoBaseAsync(string id, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(id))
{
return null;
}
var cacheKey = $"video_base_{id}";
var expiredOption = new MemoryCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30) };
if (_memoryCache.TryGetValue<IqiyiHtmlVideoInfo?>(cacheKey, out var video))
{
return video;
}
await this.LimitRequestFrequently();
var url = $"https://m.iqiyi.com/v_{id}.html";
var response = await httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
var body = await response.Content.ReadAsStringAsync();
var videoJson = regVideoInfo.FirstMatchGroup(body);
var videoInfo = videoJson.FromJson<IqiyiHtmlVideoInfo>();
if (videoInfo != null)
{
var albumJson = regAlbumInfo.FirstMatchGroup(body);
videoInfo.AlbumInfo = albumJson.FromJson<IqiyiHtmlAlbumInfo>();
_memoryCache.Set<IqiyiHtmlVideoInfo?>(cacheKey, videoInfo, expiredOption);
return videoInfo;
}
_memoryCache.Set<IqiyiHtmlVideoInfo?>(cacheKey, null, expiredOption);
return null;
}
@@ -163,8 +196,6 @@ public class IqiyiApi : AbstractApi
return new List<IqiyiEpisode>();
}
// 获取电视剧剧集信息(综艺不适用)
var url = $"https://pcw-api.iqiyi.com/album/album/baseinfo/{albumId}";
var response = await httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
@@ -193,7 +224,7 @@ public class IqiyiApi : AbstractApi
var videoListResult = await response.Content.ReadFromJsonAsync<IqiyiVideoListResult>(_jsonOptions, cancellationToken).ConfigureAwait(false);
if (videoListResult != null && videoListResult.Data != null && videoListResult.Data.Videos != null && videoListResult.Data.Videos.Count > 0)
{
list.AddRange(videoListResult.Data.Videos);
list.AddRange(videoListResult.Data.Videos.Where(x => !x.ShortTitle.Contains("精编版") && !x.ShortTitle.Contains("会员版")));
}
else
{
@@ -215,25 +246,6 @@ public class IqiyiApi : AbstractApi
return new List<IqiyiEpisode>();
}
public async Task<string> GetTvId(string id, bool isAlbum, CancellationToken cancellationToken)
{
var cacheKey = $"tvid_{id}";
var expiredOption = new MemoryCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30) };
if (_memoryCache.TryGetValue<string>(cacheKey, out var tvid))
{
return tvid;
}
var url = isAlbum ? $"https://m.iqiyi.com/a_{id}.html" : $"https://m.iqiyi.com/v_{id}.html";
var response = await httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
var body = await response.Content.ReadAsStringAsync();
tvid = regTvId.FirstMatchGroup(body);
_memoryCache.Set<string>(cacheKey, tvid, expiredOption);
return tvid;
}
public async Task<List<IqiyiComment>> GetDanmuContentAsync(string tvId, CancellationToken cancellationToken)
{