import Foundation ///IP信息结构 struct JsonIPInfo: Codable { let data:Datas? let message:String? let status:String? enum CodingKeys: String, CodingKey { case data = "data" case message = "message" case status = "status" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) data = try values.decodeIfPresent(Datas.self, forKey: .data) message = try values.decodeIfPresent(String.self, forKey: .message) status = try values.decodeIfPresent(String.self, forKey: .status) } struct Datas: Codable { let isoCode:String? let ip:String? enum CodingKeys: String, CodingKey { case isoCode = "isoCode" case ip = "ip" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) isoCode = try values.decodeIfPresent(String.self, forKey: .isoCode) ip = try values.decodeIfPresent(String.self, forKey: .ip) } } } ///预览结构体 struct JsonBrowses: Codable { ///获取访问数据 let responseContext:ResponseContext? ///抓取的数据_预览首次(无额外参数)获取会调用这个 let contents:Contents? ///抓取的数据_预览再(有额外参数)获取会调用这个 let continuationContents:ContinuationContents? enum CodingKeys: String, CodingKey { case responseContext = "responseContext" case contents = "contents" case continuationContents = "continuationContents" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) responseContext = try values.decodeIfPresent(ResponseContext.self, forKey: .responseContext) contents = try values.decodeIfPresent(Contents.self, forKey: .contents) continuationContents = try values.decodeIfPresent(ContinuationContents.self, forKey: .continuationContents) } //MARK: - 内容结构 ///访问数据 struct ResponseContext: Codable { let visitorData:String? enum CodingKeys: String, CodingKey { case visitorData = "visitorData" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) visitorData = try values.decodeIfPresent(String.self, forKey: .visitorData) } } ///内容结构一 struct Contents: Codable { let singleColumnBrowseResultsRenderer : SingleColumnBrowseResultsRenderer? enum CodingKeys: String, CodingKey { case singleColumnBrowseResultsRenderer = "singleColumnBrowseResultsRenderer" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) singleColumnBrowseResultsRenderer = try values.decodeIfPresent(SingleColumnBrowseResultsRenderer.self, forKey: .singleColumnBrowseResultsRenderer) } struct SingleColumnBrowseResultsRenderer: Codable { ///通常只有1位数据 let tabs : [Tab]? enum CodingKeys: String, CodingKey { case tabs = "tabs" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) tabs = try values.decodeIfPresent([Tab].self, forKey: .tabs) } struct Tab : Codable { let tabRenderer : TabRenderer? enum CodingKeys: String, CodingKey { case tabRenderer = "tabRenderer" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) tabRenderer = try values.decodeIfPresent(TabRenderer.self, forKey: .tabRenderer) } struct TabRenderer : Codable { ///内容总汇(包含content以及第一阶段ctoke吗) let content : Content? enum CodingKeys: String, CodingKey { case content = "content" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) content = try values.decodeIfPresent(Content.self, forKey: .content) } struct Content : Codable { let sectionListRenderer : SectionListRenderer? enum CodingKeys: String, CodingKey { case sectionListRenderer = "sectionListRenderer" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) sectionListRenderer = try values.decodeIfPresent(SectionListRenderer.self, forKey: .sectionListRenderer) } //MARK: - 核心内容,从此往上代码无意义 struct SectionListRenderer : Codable { ///指向图像数据内容 let contents : [Content]? ///指向续页编码 let continuations : [Continuation]? let trackingParams : String? enum CodingKeys: String, CodingKey { case contents = "contents" case continuations = "continuations" case trackingParams = "trackingParams" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) contents = try values.decodeIfPresent([Content].self, forKey: .contents) continuations = try values.decodeIfPresent([Continuation].self, forKey: .continuations) trackingParams = try values.decodeIfPresent(String.self, forKey: .trackingParams) } ///图像数据内容 struct Content: Codable { let musicCarouselShelfRenderer:MusicCarouselShelfRenderer? enum CodingKeys: String, CodingKey { case musicCarouselShelfRenderer = "musicCarouselShelfRenderer" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) musicCarouselShelfRenderer = try values.decodeIfPresent(MusicCarouselShelfRenderer.self, forKey: .musicCarouselShelfRenderer) } struct MusicCarouselShelfRenderer: Codable { ///指向模块标题 let header:Header? ///指向模块内容 let contents:[Content]? enum CodingKeys: String, CodingKey { case header = "header" case contents = "contents" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) header = try values.decodeIfPresent(Header.self, forKey: .header) contents = try values.decodeIfPresent([Content].self, forKey: .contents) } ///模块标题 struct Header: Codable { let musicCarouselShelfBasicHeaderRenderer:MusicCarouselShelfBasicHeaderRenderer? enum CodingKeys: String, CodingKey { case musicCarouselShelfBasicHeaderRenderer = "musicCarouselShelfBasicHeaderRenderer" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) musicCarouselShelfBasicHeaderRenderer = try values.decodeIfPresent(MusicCarouselShelfBasicHeaderRenderer.self, forKey: .musicCarouselShelfBasicHeaderRenderer) } struct MusicCarouselShelfBasicHeaderRenderer: Codable { let title:Title? enum CodingKeys: String, CodingKey { case title = "title" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) title = try values.decodeIfPresent(Title.self, forKey: .title) } struct Title: Codable { ///一般是第0位 let runs:[Run]? enum CodingKeys: String, CodingKey { case runs = "runs" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) runs = try values.decodeIfPresent([Run].self, forKey: .runs) } //MARK: - 文本内容,标题/作者/专辑 struct Run: Codable { ///关键文本内容(代表各级标题) let text:String? ///指向预览ID let navigationEndpoint: NavigationEndpoint? enum CodingKeys: String, CodingKey { case text = "text" case navigationEndpoint = "navigationEndpoint" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) text = try values.decodeIfPresent(String.self, forKey: .text) navigationEndpoint = try values.decodeIfPresent(NavigationEndpoint.self, forKey: .navigationEndpoint) } struct NavigationEndpoint: Codable { let browseEndpoint:BrowseEndpoint? enum CodingKeys: String, CodingKey { case browseEndpoint = "browseEndpoint" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) browseEndpoint = try values.decodeIfPresent(BrowseEndpoint.self, forKey: .browseEndpoint) } //MARK: - 展示内容块的预览内容 struct BrowseEndpoint: Codable { ///预览ID(作者/歌单/专辑都有,关键内容) let browseId:String? ///预览参数(作者/歌单/专辑具备,关键内容) let params:String? ///指向预览类型 let browseEndpointContextSupportedConfigs:BrowseEndpointContextSupportedConfigs? enum CodingKeys: String, CodingKey { case browseId = "browseId" case params = "params" case browseEndpointContextSupportedConfigs = "browseEndpointContextSupportedConfigs" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) browseId = try values.decodeIfPresent(String.self, forKey: .browseId) params = try values.decodeIfPresent(String.self, forKey: .params) browseEndpointContextSupportedConfigs = try values.decodeIfPresent(BrowseEndpointContextSupportedConfigs.self, forKey: .browseEndpointContextSupportedConfigs) } struct BrowseEndpointContextSupportedConfigs: Codable { let browseEndpointContextMusicConfig:BrowseEndpointContextMusicConfig? enum CodingKeys: String, CodingKey { case browseEndpointContextMusicConfig = "browseEndpointContextMusicConfig" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) browseEndpointContextMusicConfig = try values.decodeIfPresent(BrowseEndpointContextMusicConfig.self, forKey: .browseEndpointContextMusicConfig) } //MARK: - Browse类型 作者/歌单/专辑 struct BrowseEndpointContextMusicConfig: Codable { let pageType:String? enum CodingKeys: String, CodingKey { case pageType = "pageType" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) pageType = try values.decodeIfPresent(String.self, forKey: .pageType) } } } } } } } } } ///音乐内容 struct Content: Codable { ///常规列表(通常是四组件构成,封面,一级标题,二级标题,三级标题,额外包含ID内容,通常是单曲) let musicResponsiveListItemRenderer:RootMusicResponsiveListItemRenderer? ///两行式列表(通常是三组件构成,封面,一级标题,二级标题,额外包含ID内容,通常是歌单) let musicTwoRowItemRenderer:RootMusicTwoRowItemRenderer? enum CodingKeys: String, CodingKey { case musicResponsiveListItemRenderer = "musicResponsiveListItemRenderer" case musicTwoRowItemRenderer = "musicTwoRowItemRenderer" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) musicResponsiveListItemRenderer = try values.decodeIfPresent(RootMusicResponsiveListItemRenderer.self, forKey: .musicResponsiveListItemRenderer) musicTwoRowItemRenderer = try values.decodeIfPresent(RootMusicTwoRowItemRenderer.self, forKey: .musicTwoRowItemRenderer) } } } } ///续页编码 struct Continuation: Codable { let nextContinuationData:NextContinuationData? enum CodingKeys: String, CodingKey { case nextContinuationData = "nextContinuationData" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) nextContinuationData = try values.decodeIfPresent(NextContinuationData.self, forKey: .nextContinuationData) } //MARK: - 续页编码 struct NextContinuationData: Codable { let continuation:String? let clickTrackingParams:String? enum CodingKeys: String, CodingKey { case continuation = "continuation" case clickTrackingParams = "clickTrackingParams" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) continuation = try values.decodeIfPresent(String.self, forKey: .continuation) clickTrackingParams = try values.decodeIfPresent(String.self, forKey: .clickTrackingParams) } } } } } } } } } ///内容结构二 struct ContinuationContents: Codable { let sectionListContinuation:SectionListContinuation? enum CodingKeys: String, CodingKey { case sectionListContinuation = "sectionListContinuation" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) sectionListContinuation = try values.decodeIfPresent(SectionListContinuation.self, forKey: .sectionListContinuation) } //MARK: - 核心内容,从此往上代码无意义 struct SectionListContinuation : Codable { ///指向图像数据内容 let contents : [Content]? ///指向续页编码 let continuations : [Continuation]? let trackingParams : String? enum CodingKeys: String, CodingKey { case contents = "contents" case continuations = "continuations" case trackingParams = "trackingParams" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) contents = try values.decodeIfPresent([Content].self, forKey: .contents) continuations = try values.decodeIfPresent([Continuation].self, forKey: .continuations) trackingParams = try values.decodeIfPresent(String.self, forKey: .trackingParams) } ///图像数据内容 struct Content: Codable { let musicCarouselShelfRenderer:MusicCarouselShelfRenderer? enum CodingKeys: String, CodingKey { case musicCarouselShelfRenderer = "musicCarouselShelfRenderer" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) musicCarouselShelfRenderer = try values.decodeIfPresent(MusicCarouselShelfRenderer.self, forKey: .musicCarouselShelfRenderer) } struct MusicCarouselShelfRenderer: Codable { ///指向模块标题 let header:Header? ///指向音乐内容 let contents:[Content]? enum CodingKeys: String, CodingKey { case header = "header" case contents = "contents" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) header = try values.decodeIfPresent(Header.self, forKey: .header) contents = try values.decodeIfPresent([Content].self, forKey: .contents) } ///模块标题 struct Header: Codable { let musicCarouselShelfBasicHeaderRenderer:MusicCarouselShelfBasicHeaderRenderer? enum CodingKeys: String, CodingKey { case musicCarouselShelfBasicHeaderRenderer = "musicCarouselShelfBasicHeaderRenderer" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) musicCarouselShelfBasicHeaderRenderer = try values.decodeIfPresent(MusicCarouselShelfBasicHeaderRenderer.self, forKey: .musicCarouselShelfBasicHeaderRenderer) } struct MusicCarouselShelfBasicHeaderRenderer: Codable { let title:Title? enum CodingKeys: String, CodingKey { case title = "title" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) title = try values.decodeIfPresent(Title.self, forKey: .title) } struct Title: Codable { ///一般是第0位 let runs:[Run]? enum CodingKeys: String, CodingKey { case runs = "runs" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) runs = try values.decodeIfPresent([Run].self, forKey: .runs) } //MARK: - 文本内容,标题/作者/专辑 struct Run: Codable { ///关键文本内容(代表各级标题) let text:String? ///指向预览ID let navigationEndpoint: NavigationEndpoint? enum CodingKeys: String, CodingKey { case text = "text" case navigationEndpoint = "navigationEndpoint" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) text = try values.decodeIfPresent(String.self, forKey: .text) navigationEndpoint = try values.decodeIfPresent(NavigationEndpoint.self, forKey: .navigationEndpoint) } struct NavigationEndpoint: Codable { let browseEndpoint:BrowseEndpoint? enum CodingKeys: String, CodingKey { case browseEndpoint = "browseEndpoint" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) browseEndpoint = try values.decodeIfPresent(BrowseEndpoint.self, forKey: .browseEndpoint) } //MARK: - 展示内容块的预览内容 struct BrowseEndpoint: Codable { ///预览ID(作者/歌单/专辑都有,关键内容) let browseId:String? ///预览参数(作者/歌单/专辑具备,关键内容) let params:String? ///指向预览类型 let browseEndpointContextSupportedConfigs:BrowseEndpointContextSupportedConfigs? enum CodingKeys: String, CodingKey { case browseId = "browseId" case params = "params" case browseEndpointContextSupportedConfigs = "browseEndpointContextSupportedConfigs" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) browseId = try values.decodeIfPresent(String.self, forKey: .browseId) params = try values.decodeIfPresent(String.self, forKey: .params) browseEndpointContextSupportedConfigs = try values.decodeIfPresent(BrowseEndpointContextSupportedConfigs.self, forKey: .browseEndpointContextSupportedConfigs) } struct BrowseEndpointContextSupportedConfigs: Codable { let browseEndpointContextMusicConfig:BrowseEndpointContextMusicConfig? enum CodingKeys: String, CodingKey { case browseEndpointContextMusicConfig = "browseEndpointContextMusicConfig" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) browseEndpointContextMusicConfig = try values.decodeIfPresent(BrowseEndpointContextMusicConfig.self, forKey: .browseEndpointContextMusicConfig) } //MARK: - Browse类型 作者/歌单/专辑 struct BrowseEndpointContextMusicConfig: Codable { let pageType:String? enum CodingKeys: String, CodingKey { case pageType = "pageType" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) pageType = try values.decodeIfPresent(String.self, forKey: .pageType) } } } } } } } } } ///音乐内容 struct Content: Codable { ///常规列表(通常是四组件构成,封面,一级标题,二级标题,三级标题,额外包含ID内容,通常是单曲) let musicResponsiveListItemRenderer:RootMusicResponsiveListItemRenderer? ///两行式列表(通常是三组件构成,封面,一级标题,二级标题,额外包含ID内容,通常是歌单) let musicTwoRowItemRenderer:RootMusicTwoRowItemRenderer? enum CodingKeys: String, CodingKey { case musicResponsiveListItemRenderer = "musicResponsiveListItemRenderer" case musicTwoRowItemRenderer = "musicTwoRowItemRenderer" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) musicResponsiveListItemRenderer = try values.decodeIfPresent(RootMusicResponsiveListItemRenderer.self, forKey: .musicResponsiveListItemRenderer) musicTwoRowItemRenderer = try values.decodeIfPresent(RootMusicTwoRowItemRenderer.self, forKey: .musicTwoRowItemRenderer) } } } } ///续页编码 struct Continuation: Codable { let nextContinuationData:NextContinuationData? enum CodingKeys: String, CodingKey { case nextContinuationData = "nextContinuationData" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) nextContinuationData = try values.decodeIfPresent(NextContinuationData.self, forKey: .nextContinuationData) } //MARK: - 续页编码 struct NextContinuationData: Codable { let continuation:String? let clickTrackingParams:String? enum CodingKeys: String, CodingKey { case continuation = "continuation" case clickTrackingParams = "clickTrackingParams" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) continuation = try values.decodeIfPresent(String.self, forKey: .continuation) clickTrackingParams = try values.decodeIfPresent(String.self, forKey: .clickTrackingParams) } } } } } } //MARK: - 图像信息数据结构 ///单曲/视频结构(通常是四组件构成,封面,一级标题,二级标题,额外包含ID内容) struct RootMusicResponsiveListItemRenderer: Codable { ///指向封面图像 let thumbnail:Thumbnail? ///指向标题歌手专辑(通常有多位,0位是标题,后续都为副标题) let flexColumns:[FlexColumn]? ///菜单操作(某些情况下flexColumns无法拿到playListId) let menu:Menu? ///单曲/视频ID(playlistItemData存在说明这条数据单曲/视频) let playlistItemData:PlaylistItemData? ///专辑/歌单ID(navigationEndpoint存在说明这条数据是专辑/歌单) let navigationEndpoint:NavigationEndpoint? enum CodingKeys: String, CodingKey { case thumbnail = "thumbnail" case flexColumns = "flexColumns" case menu = "menu" case playlistItemData = "playlistItemData" case navigationEndpoint = "navigationEndpoint" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) thumbnail = try values.decodeIfPresent(Thumbnail.self, forKey: .thumbnail) flexColumns = try values.decodeIfPresent([FlexColumn].self, forKey: .flexColumns) menu = try values.decodeIfPresent(Menu.self, forKey: .menu) playlistItemData = try values.decodeIfPresent(PlaylistItemData.self, forKey: .playlistItemData) navigationEndpoint = try values.decodeIfPresent(NavigationEndpoint.self, forKey: .navigationEndpoint) } //含有封面图像 struct Thumbnail: Codable { let musicThumbnailRenderer:MusicThumbnailRenderer? enum CodingKeys: String, CodingKey { case musicThumbnailRenderer = "musicThumbnailRenderer" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) musicThumbnailRenderer = try values.decodeIfPresent(MusicThumbnailRenderer.self, forKey: .musicThumbnailRenderer) } struct MusicThumbnailRenderer: Codable { let thumbnail:Thumbnail? enum CodingKeys: String, CodingKey { case thumbnail = "thumbnail" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.thumbnail = try values.decodeIfPresent(Thumbnail.self, forKey: .thumbnail) } struct Thumbnail: Codable { ///封面图片组(默认取最后一位,图像内容最大最高清) let thumbnails:[Thumbnails]? enum CodingKeys: String, CodingKey { case thumbnails = "thumbnails" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.thumbnails = try values.decodeIfPresent([Thumbnails].self, forKey: .thumbnails) } //MARK: - 封面图片 ///封面图片 struct Thumbnails: Codable { ///封面图片路径 let url:String? enum CodingKeys: String, CodingKey { case url = "url" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) url = try values.decodeIfPresent(String.self, forKey: .url) } } } } } //含有标题作者专辑 struct FlexColumn: Codable { let musicResponsiveListItemFlexColumnRenderer:MusicResponsiveListItemFlexColumnRenderer? enum CodingKeys: String, CodingKey { case musicResponsiveListItemFlexColumnRenderer = "musicResponsiveListItemFlexColumnRenderer" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) musicResponsiveListItemFlexColumnRenderer = try values.decodeIfPresent(MusicResponsiveListItemFlexColumnRenderer.self, forKey: .musicResponsiveListItemFlexColumnRenderer) } struct MusicResponsiveListItemFlexColumnRenderer: Codable { let text:Text? enum CodingKeys: String, CodingKey { case text = "text" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) text = try values.decodeIfPresent(Text.self, forKey: .text) } struct Text: Codable { let runs:[Run]? enum CodingKeys: String, CodingKey { case runs = "runs" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) runs = try values.decodeIfPresent([Run].self, forKey: .runs) } struct Run: Codable { ///标题文本 let text:String? let navigationEndpoint:NavigationEndpoint? enum CodingKeys: String, CodingKey { case text = "text" case navigationEndpoint = "navigationEndpoint" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) text = try values.decodeIfPresent(String.self, forKey: .text) navigationEndpoint = try values.decodeIfPresent(NavigationEndpoint.self, forKey: .navigationEndpoint) } struct NavigationEndpoint: Codable { let watchEndpoint:WatchEndpoint? let browseEndpoint:BrowseEndpoint? enum CodingKeys: String, CodingKey { case watchEndpoint = "watchEndpoint" case browseEndpoint = "browseEndpoint" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) watchEndpoint = try values.decodeIfPresent(WatchEndpoint.self, forKey: .watchEndpoint) browseEndpoint = try values.decodeIfPresent(BrowseEndpoint.self, forKey: .browseEndpoint) } struct WatchEndpoint: Codable { let playlistId:String? enum CodingKeys: String, CodingKey { case playlistId = "playlistId" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) playlistId = try values.decodeIfPresent(String.self, forKey: .playlistId) } } struct BrowseEndpoint: Codable { let browseId:String? let params:String? let browseEndpointContextSupportedConfigs:BrowseEndpointContextSupportedConfigs? enum CodingKeys: String, CodingKey { case browseId = "browseId" case params = "params" case browseEndpointContextSupportedConfigs = "browseEndpointContextSupportedConfigs" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) browseId = try values.decodeIfPresent(String.self, forKey: .browseId) params = try values.decodeIfPresent(String.self, forKey: .params) browseEndpointContextSupportedConfigs = try values.decodeIfPresent(BrowseEndpointContextSupportedConfigs.self, forKey: .browseEndpointContextSupportedConfigs) } struct BrowseEndpointContextSupportedConfigs: Codable { let browseEndpointContextMusicConfig:BrowseEndpointContextMusicConfig? enum CodingKeys: String, CodingKey { case browseEndpointContextMusicConfig = "browseEndpointContextMusicConfig" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) browseEndpointContextMusicConfig = try values.decodeIfPresent(BrowseEndpointContextMusicConfig.self, forKey: .browseEndpointContextMusicConfig) } struct BrowseEndpointContextMusicConfig: Codable { let pageType:String? enum CodingKeys: String, CodingKey { case pageType = "pageType" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) pageType = try values.decodeIfPresent(String.self, forKey: .pageType) } } } } } } } } } //菜单操作 struct Menu: Codable { let menuRenderer:MenuRenderer? enum CodingKeys: String, CodingKey { case menuRenderer = "menuRenderer" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) menuRenderer = try values.decodeIfPresent(MenuRenderer.self, forKey: .menuRenderer) } struct MenuRenderer: Codable { let items:[Item]? enum CodingKeys: String, CodingKey { case items = "items" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) items = try values.decodeIfPresent([Item].self, forKey: .items) } struct Item: Codable { let menuNavigationItemRenderer:MenuNavigationItemRenderer? enum CodingKeys: String, CodingKey { case menuNavigationItemRenderer = "menuNavigationItemRenderer" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) menuNavigationItemRenderer = try values.decodeIfPresent(MenuNavigationItemRenderer.self, forKey: .menuNavigationItemRenderer) } struct MenuNavigationItemRenderer: Codable { let navigationEndpoint:NavigationEndpoint? enum CodingKeys: String, CodingKey { case navigationEndpoint = "navigationEndpoint" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) navigationEndpoint = try values.decodeIfPresent(NavigationEndpoint.self, forKey: .navigationEndpoint) } struct NavigationEndpoint: Codable { let watchEndpoint:WatchEndpoint? enum CodingKeys: String, CodingKey { case watchEndpoint = "watchEndpoint" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) watchEndpoint = try values.decodeIfPresent(WatchEndpoint.self, forKey: .watchEndpoint) } struct WatchEndpoint: Codable { let playlistId:String? enum CodingKeys: String, CodingKey { case playlistId = "playlistId" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) playlistId = try values.decodeIfPresent(String.self, forKey: .playlistId) } } } } } } } struct PlaylistItemData: Codable { let videoId:String? enum CodingKeys: String, CodingKey { case videoId = "videoId" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) videoId = try values.decodeIfPresent(String.self, forKey: .videoId) } } struct NavigationEndpoint: Codable { let browseEndpoint:BrowseEndpoint? enum CodingKeys: String, CodingKey { case browseEndpoint = "browseEndpoint" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) browseEndpoint = try values.decodeIfPresent(BrowseEndpoint.self, forKey: .browseEndpoint) } struct BrowseEndpoint: Codable { let browseId:String? let params:String? let browseEndpointContextSupportedConfigs:BrowseEndpointContextSupportedConfigs? enum CodingKeys: String, CodingKey { case browseId = "browseId" case params = "params" case browseEndpointContextSupportedConfigs = "browseEndpointContextSupportedConfigs" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) browseId = try values.decodeIfPresent(String.self, forKey: .browseId) params = try values.decodeIfPresent(String.self, forKey: .params) browseEndpointContextSupportedConfigs = try values.decodeIfPresent(BrowseEndpointContextSupportedConfigs.self, forKey: .browseEndpointContextSupportedConfigs) } struct BrowseEndpointContextSupportedConfigs: Codable { let browseEndpointContextMusicConfig:BrowseEndpointContextMusicConfig? enum CodingKeys: String, CodingKey { case browseEndpointContextMusicConfig = "browseEndpointContextMusicConfig" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) browseEndpointContextMusicConfig = try values.decodeIfPresent(BrowseEndpointContextMusicConfig.self, forKey: .browseEndpointContextMusicConfig) } struct BrowseEndpointContextMusicConfig: Codable { let pageType:String? enum CodingKeys: String, CodingKey { case pageType = "pageType" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) pageType = try values.decodeIfPresent(String.self, forKey: .pageType) } } } } } } ///列表/专辑结构(通常是三组件构成,封面,一级标题,二级标题,额外包含ID内容) struct RootMusicTwoRowItemRenderer: Codable { ///内容预览图片 let thumbnailRenderer:ThumbnailRenderer? ///内容标题 let title:Title? ///内容副标题 let subtitle:Subtitle? ///内容iD和编码 let navigationEndpoint:NavigationEndpoint? enum CodingKeys: String, CodingKey { case thumbnailRenderer = "thumbnailRenderer" case title = "title" case subtitle = "subtitle" case navigationEndpoint = "navigationEndpoint" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) thumbnailRenderer = try values.decodeIfPresent(ThumbnailRenderer.self, forKey: .thumbnailRenderer) title = try values.decodeIfPresent(Title.self, forKey: .title) subtitle = try values.decodeIfPresent(Subtitle.self, forKey: .subtitle) navigationEndpoint = try values.decodeIfPresent(NavigationEndpoint.self, forKey: .navigationEndpoint) } struct ThumbnailRenderer: Codable { let musicThumbnailRenderer:MusicThumbnailRenderer? enum CodingKeys: String, CodingKey { case musicThumbnailRenderer = "musicThumbnailRenderer" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) musicThumbnailRenderer = try values.decodeIfPresent(MusicThumbnailRenderer.self, forKey: .musicThumbnailRenderer) } struct MusicThumbnailRenderer: Codable { let thumbnail:Thumbnail? enum CodingKeys: String, CodingKey { case thumbnail = "thumbnail" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) thumbnail = try values.decodeIfPresent(Thumbnail.self, forKey: .thumbnail) } struct Thumbnail: Codable { let thumbnails:[Thumbnails]? enum CodingKeys: String, CodingKey { case thumbnails = "thumbnails" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) thumbnails = try values.decodeIfPresent([Thumbnails].self, forKey: .thumbnails) } struct Thumbnails: Codable { let url:String? enum CodingKeys: String, CodingKey { case url = "url" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) url = try values.decodeIfPresent(String.self, forKey: .url) } } } } } struct Title: Codable { let runs:[Run]? enum CodingKeys: String, CodingKey { case runs = "runs" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) runs = try values.decodeIfPresent([Run].self, forKey: .runs) } struct Run: Codable { let text:String? enum CodingKeys: String, CodingKey { case text = "text" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) text = try values.decodeIfPresent(String.self, forKey: .text) } } } struct Subtitle: Codable { let runs:[Run]? enum CodingKeys: String, CodingKey { case runs = "runs" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) runs = try values.decodeIfPresent([Run].self, forKey: .runs) } struct Run: Codable { let text:String? enum CodingKeys: String, CodingKey { case text = "text" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) text = try values.decodeIfPresent(String.self, forKey: .text) } } } struct NavigationEndpoint: Codable { ///列表/专辑类型 let browseEndpoint:BrowseEndpoint? ///音视频类型 let watchEndpoint:WatchEndpoint? enum CodingKeys: String, CodingKey { case browseEndpoint = "browseEndpoint" case watchEndpoint = "watchEndpoint" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) browseEndpoint = try values.decodeIfPresent(BrowseEndpoint.self, forKey: .browseEndpoint) watchEndpoint = try values.decodeIfPresent(WatchEndpoint.self, forKey: .watchEndpoint) } struct BrowseEndpoint: Codable { let browseId:String? let params:String? let browseEndpointContextSupportedConfigs:BrowseEndpointContextSupportedConfigs? enum CodingKeys: String, CodingKey { case browseId = "browseId" case params = "params" case browseEndpointContextSupportedConfigs = "browseEndpointContextSupportedConfigs" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) browseId = try values.decodeIfPresent(String.self, forKey: .browseId) params = try values.decodeIfPresent(String.self, forKey: .params) browseEndpointContextSupportedConfigs = try values.decodeIfPresent(BrowseEndpointContextSupportedConfigs.self, forKey: .browseEndpointContextSupportedConfigs) } struct BrowseEndpointContextSupportedConfigs: Codable { let browseEndpointContextMusicConfig:BrowseEndpointContextMusicConfig? enum CodingKeys: String, CodingKey { case browseEndpointContextMusicConfig = "browseEndpointContextMusicConfig" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) browseEndpointContextMusicConfig = try values.decodeIfPresent(BrowseEndpointContextMusicConfig.self, forKey: .browseEndpointContextMusicConfig) } struct BrowseEndpointContextMusicConfig: Codable { let pageType:String? enum CodingKeys: String, CodingKey { case pageType = "pageType" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) pageType = try values.decodeIfPresent(String.self, forKey: .pageType) } } } } struct WatchEndpoint: Codable { ///音视频ID let videoId:String? ///音视频所在列表ID let playlistId:String? ///音视频类型 let watchEndpointMusicSupportedConfigs:WatchEndpointMusicSupportedConfigs? enum CodingKeys: String, CodingKey { case videoId = "videoId" case playlistId = "playlistId" case watchEndpointMusicSupportedConfigs = "watchEndpointMusicSupportedConfigs" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) videoId = try values.decodeIfPresent(String.self, forKey: .videoId) playlistId = try values.decodeIfPresent(String.self, forKey: .playlistId) watchEndpointMusicSupportedConfigs = try values.decodeIfPresent(WatchEndpointMusicSupportedConfigs.self, forKey: .watchEndpointMusicSupportedConfigs) } struct WatchEndpointMusicSupportedConfigs: Codable { let watchEndpointMusicConfig:WatchEndpointMusicConfig? enum CodingKeys: String, CodingKey { case watchEndpointMusicConfig = "watchEndpointMusicConfig" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) watchEndpointMusicConfig = try values.decodeIfPresent(WatchEndpointMusicConfig.self, forKey: .watchEndpointMusicConfig) } struct WatchEndpointMusicConfig: Codable { let musicVideoType:String? enum CodingKeys: String, CodingKey { case musicVideoType = "musicVideoType" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) musicVideoType = try values.decodeIfPresent(String.self, forKey: .musicVideoType) } } } } } }