0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

楽天APIでCD情報を取得 アーティスト名と曲名からYoutubeAPIで動画を検索と表示

Last updated at Posted at 2021-11-12

今回の内容

完成形

簡単機能説明

  • UITextFieldに入力されたアーティスト名からCDを検索し、UICollectionViewに表示します。
  • 表示されたUICollectionViewCellを選択すると、CDに入っている曲名を表示するMusicDetailViewmodalで表示します。
  • 表示されたMusicDetailView内のUITableViewに曲名が表示されているので、選択するとVideoListViewmodalで表示します。
  • VideoListViewアーティスト名と曲名でYoutubeから取得してきた動画の一覧を複数表示します。
  • 表示された一覧を選択すると動画を表示する為のVideoPlayViewmodalで表示します。
  • 表示されたVideoPlayViewで動画を再生します。

コード

Model

VideoDetailGroup

VideoDetailDatas
struct VideoDetailDatas{
    
    let thumbnailImageURL:String?
    let title:String?
    let channelTitle:String?
    let videoPlayerContents:VideoPlayerContents?
}

struct VideoPlayerContents{
    
    let videoId:String?
    let description:String?
}
VideoAlamofireProcess
import Alamofire
import SwiftyJSON


class VideoAlamofireProcess{
    
    private var videoDetailResultArray = [VideoDetailDatas]()
}

extension VideoAlamofireProcess{
    
    public func getVideoDetailData(searchKeyword:String?,comletion: @escaping ([VideoDetailDatas]?,Error?) -> Void){
        
        print(searchKeyword as Any)
        
        guard let keyword = searchKeyword else { return }
        
        let apiKey = "https://www.googleapis.com/youtube/v3/search?&key=作成したAPIKey&part=snippet&q=\(keyword.urlEncoded)&maxResults=15"
        
        AF.request(apiKey, method: .get, parameters: nil, encoding: JSONEncoding.default).responseJSON {[self] response in
            
            switch response.result{
                
            case .success:
                
                let detailData = JSON(response.data as Any)
                videoDetailResultArray = []
                
                for dataCount in 0..<detailData["items"].count{
                    
                    if let getThumbnail = detailData["items"][dataCount]["snippet"]["thumbnails"]["medium"]["url"].string,
                       let getTitle = detailData["items"][dataCount]["snippet"]["title"].string,
                       let getChannelName = detailData["items"][dataCount]["snippet"]["channelTitle"].string,
                       let getVideoID = detailData["items"][dataCount]["id"]["videoId"].string,
                       let getDiscription = detailData["items"][dataCount]["snippet"]["description"].string{
                        
                        if detailData["items"][dataCount]["id"]["channelId"].string != getChannelName{
                            
                            videoDetailResultArray.append(VideoDetailDatas(thumbnailImageURL: getThumbnail,
                                                                           title: getTitle,
                                                                           channelTitle: getChannelName,
                                                                           videoPlayerContents: VideoPlayerContents(videoId: getVideoID,
                                                                                                                    description: getDiscription)))
                        }
                    }
                }
                comletion(videoDetailResultArray, nil)
                
            case .failure(let error):
                
                comletion(nil, error)
            }
        }
        
    }
}

MusicDetailDataGroup

MusicDetailData
struct MusicDetailData{
    
    let mediumImageUrl:String?
    let title:String?
    let artistName:String?
    let playList:PlayListDetail?
}

struct PlayListDetail{
    
    let playListContents:String?
}
MusicAlamofireProcess
import Alamofire
import SwiftyJSON

class MusicAlamofireProcess{

    private var privateMusicDetailData = [MusicDetailData]()
}

extension MusicAlamofireProcess{
    
    public func getMusicDetailData(searchKeyWord:String?,completion: @escaping ([MusicDetailData]?,Error?) -> Void){
     
        guard let keyword = searchKeyWord else { return }
        
        let apiKey = "https://app.rakuten.co.jp/services/api/BooksCD/Search/20170404?format=json&artistName=\(keyword.urlEncoded)&applicationId=作成したアプリID"
        
        AF.request(apiKey, method: .get, parameters: nil, encoding: JSONEncoding.default).responseJSON {[self] response in
            
            switch response.result{
                
            case .success:
                
                let detailData = JSON(response.data as Any)
                
                privateMusicDetailData = []
                
                for dataCount in 0..<detailData["Items"].count{
                    
                    if let getMediumImageUrl = detailData["Items"][dataCount]["Item"]["mediumImageUrl"].string,
                       let getTitle = detailData["Items"][dataCount]["Item"]["title"].string,
                       let getArtistName = detailData["Items"][dataCount]["Item"]["artistName"].string,
                       let getPlayList = detailData["Items"][dataCount]["Item"]["playList"].string{
                        
                        privateMusicDetailData.append(MusicDetailData(mediumImageUrl: getMediumImageUrl,
                                                                      title: getTitle,
                                                                      artistName: getArtistName,
                                                                      playList: PlayListDetail(playListContents: getPlayList)))
                    }
                }
                completion(privateMusicDetailData, nil)
                
                
            case .failure(let error):
            
                completion(nil, error)
            }
        }
    }
}

extension String{
    
    var urlEncoded:String{
        
        let charset = CharacterSet.alphanumerics.union(.init(charactersIn: "/?-._~"))
        let remove = removingPercentEncoding ?? self
        
        return remove.addingPercentEncoding(withAllowedCharacters: charset) ?? remove
    }
}

View

Main.storyboard

0534F408-55C2-48EC-901A-C5FFDE1351EA_4_5005_c.jpeg

CollectionViewCustomCellGroup

EE411338-8B2B-4BAA-94A5-FD72DE707971_4_5005_c.jpeg
CollectionViewCustomCell
import UIKit

class CollectionViewCustomCell: UICollectionViewCell {

    @IBOutlet weak var mediumImageView: UIImageView!
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var artistNameLabel: UILabel!
    
    override func awakeFromNib() {
        super.awakeFromNib()
   
        
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        
        
    }
    
    override func prepareForReuse() {
        super.prepareForReuse()
        
        mediumImageView.image = UIImage(named: "")
        titleLabel.text = ""
        artistNameLabel.text = ""
        
    }
}

MusicDetailViewGroup

CCF57D54-68A4-42F1-92E5-BD72D6040ADE_4_5005_c.jpeg
MusicDetailView!

import UIKit
import SDWebImage

class MusicDetailView: UIViewController {

    @IBOutlet weak var mediumImageView: UIImageView!
    @IBOutlet weak var playListTableVew: UITableView!
    
    public var playListCellContentsArray = [String]()
    public var artistName = String()
    public var mediumImageURL = String()
    
    override func viewDidLoad() {
        super.viewDidLoad()

        playListTableVew.register(UINib(nibName: "PlayListTableViewCell", bundle: nil), forCellReuseIdentifier: "PlayListCell")
        playListTableVew.delegate = self
        playListTableVew.dataSource = self
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        mediumImageView.sd_setImage(with: URL(string: mediumImageURL), completed: nil)
    }

}

extension MusicDetailView:UITableViewDelegate{
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        
        return tableView.frame.height / 8
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
        let videoListView = VideoListView()
        videoListView.getArtistName = artistName + " " + playListCellContentsArray[indexPath.row]
        videoListView.modalPresentationStyle = .automatic
        present(videoListView, animated: true, completion: nil)
    }
}

extension MusicDetailView:UITableViewDataSource{
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        return playListCellContentsArray.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "PlayListCell", for: indexPath) as! PlayListTableViewCell
        
        cell.playListContentsLabel.text = "\(String(indexPath.row + 1))曲目. \(playListCellContentsArray[indexPath.row])"
            
        return cell
    }
 
 
}

PlayListTableViewCell

D567D47B-203E-4BA3-8ABB-071B7A9E2401_4_5005_c.jpeg
import UIKit

class PlayListTableViewCell: UITableViewCell{

    @IBOutlet weak var playListContentsLabel: UILabel!
    
    override func awakeFromNib() {
        super.awakeFromNib()
              
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
               
    }
    
    override func prepareForReuse() {
        super.prepareForReuse()
        
        playListContentsLabel.text = ""
    }

}

VideoListViewGroup

4BA4F8E4-64CF-4E40-9508-CD5184470755_4_5005_c.jpeg
VideoListView
import UIKit

class VideoListView: UIViewController {

    @IBOutlet weak var videoListTableView: UITableView!
  
    private let videoAlamofireProcess = VideoAlamofireProcess()
    
    private var videoListContantArray = [VideoDetailDatas]()
    
    public var getArtistName = String()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        videoListTableView.register(UINib(nibName: "VideoListCell", bundle: nil), forCellReuseIdentifier: "VideoListCell")
        videoListTableView.delegate = self
        videoListTableView.dataSource = self
        
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        
        videoAlamofireProcess.getVideoDetailData(searchKeyword: getArtistName) {[self] result, error in
            
            if error != nil{
                
                return
            }
            
            showAnimation(showContent: "5", animationTime: 4, targetView: view) {[self] judgeBool in
                
                if judgeBool == false{
                    
                    return
                }
                videoListContantArray = []
                videoListContantArray = result!
                videoListTableView.reloadData()
            }
        }   
    }
    
}

extension VideoListView:UITableViewDelegate{
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        
        return tableView.frame.height / 2.2
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        let videoPlayView = VideoPlayView()
        videoPlayView.playVideoID = videoListContantArray[indexPath.row].videoPlayerContents?.videoId
        videoPlayView.playVideoTitle = videoListContantArray[indexPath.row].title
        videoPlayView.playVideoDescription = videoListContantArray[indexPath.row].videoPlayerContents?.description
        videoPlayView.modalPresentationStyle = .automatic
        videoPlayView.presentationController?.delegate = self
        present(videoPlayView, animated: true, completion: nil)

    }
}


extension VideoListView:UITableViewDataSource{
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        return videoListContantArray.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "VideoListCell", for: indexPath) as! VideoListCell
        
        cell.thumbnailImageView.sd_setImage(with: URL(string: videoListContantArray[indexPath.row].thumbnailImageURL!), completed: nil)
        cell.titleLabel.text = videoListContantArray[indexPath.row].title
        cell.channelNameLabel.text = videoListContantArray[indexPath.row].channelTitle
        
        return cell
    }
}

extension VideoListView:UIAdaptivePresentationControllerDelegate{
    func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
        
       let viewPlayView = VideoPlayView()
        viewPlayView.videoView.removeFromSuperview()
    }
    
}

VideoListCell

EAD1C018-A6B7-464A-B4E6-A7FC811EE3CE_1_201_a.jpeg
VideoListCell
import UIKit

class VideoListCell: UITableViewCell {

    @IBOutlet weak var thumbnailImageView: UIImageView!
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var channelNameLabel: UILabel!
    
    override func awakeFromNib() {
        super.awakeFromNib()

    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        
        
    }
    
    override func prepareForReuse() {
        super.prepareForReuse()
        
        thumbnailImageView.image = UIImage(named: "")
        titleLabel.text = ""
        channelNameLabel.text = ""
    }
    
}

VideoPlayViewGroup

1A9904E3-263E-4329-AE39-4BC04021DC6A_4_5005_c.jpeg
import UIKit
import youtube_ios_player_helper

class VideoPlayView: UIViewController{

    @IBOutlet weak var videoOnView: UIView!
    @IBOutlet weak var videoTitleLabel: UILabel!
    @IBOutlet weak var videoDescriptionView: UITextView!
    
    public var videoView = YTPlayerView()
    
    public var playVideoID:String?
    public var playVideoTitle:String?
    public var playVideoDescription:String?
    
    override func viewDidLoad() {
        super.viewDidLoad()

        videoTitleLabel.text = playVideoTitle
        videoDescriptionView.text = playVideoDescription
        videoView.frame = videoOnView.frame
        videoView.load(withVideoId: playVideoID!, playerVars: ["playersinline":1])
        videoOnView.addSubview(videoView)
        
        videoView.delegate = self
    }

}

extension VideoPlayView:YTPlayerViewDelegate{
    
    func playerViewDidBecomeReady(_ playerView: YTPlayerView) {
            
        videoView.playVideo()
    }
}


extension VideoPlayView{
    
    override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
        super.dismiss(animated: flag, completion: completion)
        
        guard let presentationController = presentationController else { return }
        
        presentationController.delegate?.presentationControllerDidDismiss?(presentationController)
    }
}

Lottie

import UIKit
import Lottie

extension UIViewController{
    
    func showAnimation(showContent:String?,animationTime:Double?,targetView:UIView,completion: @escaping (Bool) -> Void){
        
        guard let content = showContent else { completion(false); return }
        guard let time = animationTime else { completion(false); return }
        
        let underView = UIView(frame: CGRect(x: targetView.frame.maxX / 4, y: targetView.frame.maxY * 0.25, width: targetView.frame.width / 2, height: targetView.frame.width / 2))
        underView.layer.cornerRadius = 15.0
        underView.backgroundColor = .darkGray
        underView.alpha = 0.7
        targetView.addSubview(underView)
        
        let animationView = AnimationView()
        animationView.frame = CGRect(x: targetView.frame.maxX / 4, y: targetView.frame.maxY * 0.25, width: targetView.frame.width / 2, height: targetView.frame.width / 2)
        animationView.layer.cornerRadius = 15.0
        animationView.backgroundColor = .clear
        animationView.animation = Animation.named(content)
        animationView.contentMode = .scaleAspectFit
        animationView.loopMode = .loop
        animationView.play(completion: nil)
        targetView.addSubview(animationView)
    
        
        DispatchQueue.main.asyncAfter(deadline: .now() + time) {
            
            completion(true)
            animationView.stop()
            animationView.removeFromSuperview()
            underView.removeFromSuperview()
        }
    }
    
}

Controller

import UIKit
import SDWebImage

class ViewController: UIViewController {

    @IBOutlet weak var searchTextField: UITextField!
    @IBOutlet weak var searchButton: UIButton!
    @IBOutlet weak var searchResultCollectionView: UICollectionView!
    
    private let musicAlamofireProcess = MusicAlamofireProcess()
    
    private var cellLayout = UICollectionViewFlowLayout()
    
    private var cellContentsArray = [MusicDetailData]()
    
    override func viewDidLoad() {
        super.viewDidLoad()

        searchResultCollectionView.register(UINib(nibName: "CollectionViewCustomCell", bundle: nil), forCellWithReuseIdentifier: "MusicDetailCell")
        searchResultCollectionView.delegate = self
        searchResultCollectionView.dataSource = self
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        cellLayout.sectionInset = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
        searchResultCollectionView.collectionViewLayout = cellLayout
    }

    @IBAction func search(_ sender: UIButton) {
        
        musicAlamofireProcess.getMusicDetailData(searchKeyWord: searchTextField.text) {[self] result, error in
            
            if error != nil{
                
                return
            }
            
            showAnimation(showContent: "5", animationTime: 3.0, targetView: view) { resultBool in
                
                if resultBool == false{
                    
                    return
                }
                
                cellContentsArray = result!
                searchResultCollectionView.reloadData()
            }
        }
    }
    
}

extension ViewController:UICollectionViewDelegate{
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        
        let musicDetailView = MusicDetailView()
        musicDetailView.artistName = cellContentsArray[indexPath.row].artistName!
        musicDetailView.playListCellContentsArray = (cellContentsArray[indexPath.row].playList?.playListContents?.components(separatedBy: "###"))!
        musicDetailView.mediumImageURL = cellContentsArray[indexPath.row].mediumImageUrl!
        musicDetailView.modalPresentationStyle = .automatic
        present(musicDetailView, animated: true, completion: nil)
    }
}

extension ViewController:UICollectionViewDataSource{
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        
        return cellContentsArray.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MusicDetailCell", for: indexPath) as! CollectionViewCustomCell
        
        cell.mediumImageView.sd_setImage(with: URL(string: cellContentsArray[indexPath.row].mediumImageUrl!), completed: nil)
        cell.titleLabel.text = cellContentsArray[indexPath.row].title
        cell.artistNameLabel.text = cellContentsArray[indexPath.row].artistName
        
        return cell
    }
    
}

extension ViewController:UICollectionViewDelegateFlowLayout{
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        
        return CGSize(width: collectionView.frame.width / 2 - 10, height: collectionView.frame.height / 2 - 10)
    }
}

終わり

ご指摘、ご質問などありましたら、コメントまでお願い致します。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?