2017年9月22日金曜日

packer のビルドフロー中に serverspec でテストする方法

概要

packer の provisioners でインストールしたパッケージなどを serverspec でテストしたということはよくあると思います
イメージなどを作成後に VM として起動して serverspec を実行する手法はよくあると思いますが少し面倒くさいです
今回は packer build 内で serverspec を実行する方法を紹介したいと思います

環境

  • macOS X 10.12.6
  • packer 1.0.4
  • go 1.8
  • serverspec 2.40.0

serverspec 準備

今回は serverspec のテストも 1 から作成してみたいと思います

  • bundle init
  • vim Gemfile
gem "serverspec"
  • bundle install
  • serverspec-init
  1) UN*X
  2) Windows

Select number: 1

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 2

 + spec/
 + spec/localhost/
 + spec/localhost/sample_spec.rb
 + spec/spec_helper.rb
 + Rakefile
 + .rspec

ここで 1 つポイントですが実行するホストは localhost にしましょう
要するにビルド中に作成された VM (localhost) に対して実行するようにします

  • vim spec/localhost/sample_spec.rb
require 'spec_helper'

describe package('httpd'), :if => os[:family] == 'redhat' do
  it { should be_installed }
end
  • vim spec/spec_helper.rb
require 'serverspec'
require 'net/ssh'

options = Net::SSH::Config.for(host, [])
options[:user] = ENV['TARGET_USER']
options[:keys] = ENV['TARGET_KEY']
options[:host_name] = ENV['TARGET_HOST']
options[:port] = ENV['TARGET_PORT']
options[:paranoid] = false unless ENV['SERVERSPEC_HOST_KEY_CHECKING'] =~ (/^(true|t|yes|y|1)$/i)

set :host,         options[:host_name]
set :ssh_options,  options
set :backend,      :ssh
set :display_sudo, true
set :request_pty,  true

sample_spec.rb と spec_helper.rb はとりあえず上記をそのまま利用してください

プラグインのインストール

今回 Github で公開されている packer-provisioner-serverspec を使います

  • go get github.com/unifio/packer-provisioner-serverspec
  • mkdir $HOME/.packer.d/plugins
  • cp $GOPATH/bin/packer-provisioner-serverspec $HOME/.packer.d/plugins

で OK です

テンプレートファイルの作成

  • vim centos7_with_serverspec.json
{
    "builders": [
    {
        "type": "virtualbox-iso",
        "vboxmanage": [
        [ "modifyvm", "{{.Name}}", "--memory", "2048" ],
        [ "modifyvm", "{{.Name}}", "--nic1", "nat" ],
        [ "modifyvm", "{{.Name}}", "--nic2", "hostonly" ],
        [ "modifyvm", "{{.Name}}", "--hostonlyadapter2", "vboxnet0"]
        ],
        "iso_checksum": "27bd866242ee058b7a5754e83d8ee8403e216b93d130d800852a96f41c34d86a",
        "iso_checksum_type": "sha256",
        "iso_url": "http://ftp.riken.jp/Linux/centos/7/isos/x86_64/CentOS-7-x86_64-Minimal-1611.iso",
        "ssh_username": "root",
        "ssh_password": "password",
        "ssh_wait_timeout": "40m",
        "disk_size": "8000",
        "guest_os_type": "RedHat_64",
        "shutdown_command": "/sbin/shutdown -h now",
        "shutdown_timeout": "20s",
        "vm_name": "centos7_from_packer",
        "boot_command": [
        "<tab> text ks=http://192.168.56.101/centos.ks<enter><wait>"
        ]
    }
    ],
    "provisioners": [
    {
        "type": "shell",
        "inline": ["yum install -y vim httpd"]
    },
    {
        "type": "serverspec",
        "rake_file": "Rakefile",
        "rake_task": "spec:all",
        "rake_env_vars": "$BUNDLE_GEMFILE=Gemfile"
    }
    ]
}

基本は過去に CentOS7 をビルドしたテンプレートを使っています
またテンプレートを作成する場所は serverspec-init を実行したパスと同じ場所に配置してください

ポイントは provisioners に "type": "serverspec" が追加されている点です
プラグインをインストールすることでこのタイプが使えるようになります

また、今回は OS のインストールも自動化することを想定しているので kickstart サーバも使っています
kickstart サーバの構築に関してはこちらを御覧ください

動作確認

  • packer build centos7_with_serverspec.json

でいろいろとログが流れます
長いので割愛しますが流れとしては

ISO 取得 -> VM 起動 -> OS インストール -> SSH 接続 -> provisioner 実行 (yum) -> provisioner 実行 (serverspec)

になります
最終的に serverspec が実行されてエラーが出なければ成功です

試しに作成されたイメージを一度起動して VM の中を確認してみましょう
(今回は Virtualbox を使っているので仮想アプライアンスのインポートから作成された ovf を選択すれば OK です)
今回の場合であれば httpd がインストールされていれば OK となります
また、今回の手順の場合作成されたイメージないに serverspec コマンドはインストールされません
packer_with_serverspec1.png

最後に

プラグインを使って packer + serverspec でビルド中にイメージのテストを実施してみました
こっちのほうが手順としても packer build 1 回で済むのでスマートかなと思います

いろいろと調べてみると packer + serverspec を連携する方法は出てきますがビルドするイメージ内に serverspec をインストールする方法が結構多いです
それでも特に問題はないですが ruby など使わないのであれば、あまり入れたいとは思いません
今回の手順はイメージ内に serverspec をインストール必要がないのも嬉しい点かなと思います

少し心配な点があるとすれば今回使ったプラグインがあまりメンテナンスされていない点でしょうか、、、

参考サイト

2017年9月21日木曜日

個人メモ 日本語入りの keynote を slideshare にアップロードする手順

概要

いつも忘れるのでメモ
こちらの手順を使っています

環境

  • macOS X 10.12.6
  • keynote 7.2 (4582)

手順

  1. keynote を使って pdf を書き出し
  2. ターミナルを開く
  3. LANG=C LC_ALL=C sed -i '' s'|/Registry (Adobe) /Ordering (Japan1) /Supplement [0-9]|/Registry(Adobe) /Ordering(Identity) /Supplement 0|g' output.pdf
  4. アップロード
  5. メタデータ (タイトルや説明、タグ、公開フラグ) を設定

2017年9月20日水曜日

Sinatra で Encoding::CompatibilityError - incompatible character encodings: UTF-8 and ASCII-8BIT:

概要

Sinatra で erb を使って HTML を出力するときに発生しました
対応策を紹介します

環境

  • CentOS 7.3.1611
  • Ruby 2.3.1p112

対応方法

res.body.force_encoding("UTF-8")

res.body は文字列です
要するに erb で表示しようとしている文字列のエンコード情報がおかしいので UTF-8 に変更してあげる感じです

erb にマジックコメントを入れることで対応できるという紹介記事もあったのですが自分はそれではできなかったので上記の対応をしました

2017年9月19日火曜日

packer + esxi で CentOS7 の ISO から vmx と vmdk ファイルを作成してみた

概要

前回 Ubuntu で試しました
今回は CentOS で試したので JSON テンプレートの紹介をしたいと思います

環境

  • Ubuntu Ubuntu 16.04.2
  • packer 1.0.4
  • VMware ESXi 6.0.0

事前準備

JSON テンプレート

  • vim centos7.json
{
    "builders": [
    {
        "name": "centos7",
        "type": "vmware-iso",

        "vm_name": "centos7_from_packer",
        "guest_os_type": "rhel7-64",
        "ssh_username": "root",
        "ssh_password": "vm_password",
        "ssh_timeout": "15m",

        "iso_url": "http://ftp.jaist.ac.jp/pub/Linux/CentOS/7/isos/x86_64/CentOS-7-x86_64-Minimal-1708.iso",
        "iso_checksum": "bba314624956961a2ea31dd460cd860a77911c1e0a56e4820a12b9c5dad363f5",
        "iso_checksum_type": "sha256",

        "shutdown_command": "echo 'shutdown -P now' > shutdown.sh; echo 'vm_password'|sudo -S sh 'shutdown.sh'",
        "boot_command": [
        "<tab> text ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/centos7.ks<enter><wait>"
        ],
        "http_directory": "./",
        "tools_upload_flavor": "linux",
        "headless": false,
        "keep_registered": true,

        "remote_type": "esx5",
        "remote_host": "192.168.100.105",
        "remote_datastore": "datastore1",
        "remote_username": "root",
        "remote_password": "esxi_password",
        "disk_type_id": "thin",
        "vmx_data": {
        "ethernet0.networkName": "VM Network"
        },

        "vnc_port_min": "5900",
        "vnc_port_max": "5911",
        "vnc_bind_address": "0.0.0.0",
        "vnc_disable_password": "true"
    }
    ]
}

詳細に説明

メタデータ

  • name・・・このビルドの名称、packer は並列ビルドが可能でどのビルドかをこの name で識別します
  • type・・・どのビルドタイプを使うか指定します、今回は「vmware-iso」を使いましたが他にも docker や amazon-ebs などたくさんのビルドタイプがあります、今回のタイプはダウンロードした ISO を ESXi でビルドし vSphere 環境で動作する vmx ファイルと vmdk ファイルを作成するためのタイプです

作成する VM の情報

  • vm_name・・・VM の名前
  • guest_os_type・・・vSphere 環境で扱われる OS のタイプを指定します、今回は RedHat 系の OS なので「rhel7-64」を指定しています、他にも ubuntu-64 や other などがあります
  • ssh_username・・・プロビジョニングするために作成した VM に SSH ログインするためのユーザ名です、OS インストール時に作成したユーザ名と同じにする必要があります
  • ssh_password・・・プロビジョニングするために作成した VM に SSH ログインするためのパスワードです、OS インストール時と設定したパスワードと同じにする必要があります
  • ssh_timeout・・・SSH のタイムアウト時間です、この時間内に VM と SSH のコネクションが確立されないとビルドエラーとなります

ISO 情報

  • iso_url・・・ダウンロードする ISO の URLです、ここで指定した ISO をダウンロードして packer はビルドします
  • iso_checksum・・・ダウンロードする ISO のチェックサムです、チェックサムが間違っているとビルドエラーとなります
  • iso_checksum_type・・・チェックサムのタイプを指定します

ビルドオプション

  • shutdown_command・・・すべてのプロビジョニングが完了した後に実行するシャットダウンコマンドを指定します
  • boot_command・・・ISO から OS をインストールする際のオプションを指定します、GRUB 画面を直接制御することができる特殊なコマンドが用意されており、ここで kickstart サーバを指定します
  • http_directory・・・これを指定することで packer 自信が HTTP サーバになることができます、HTTP サーバとして立ち上がった際のバインドされる IP とポートはそれぞれ {{ .HTTPIP }}{{ .HTTPPort }} に格納されます、このオプションでは HTTP サーバの DocumentRoot となるパスを指定します
  • tools_upload_flavor・・・VMware tools のタイプを指定します、他に windows や darwin が指定できます
  • headless・・・ビルド時にコンソールを使うか使わないか指定できます、今回の場合は kickstart を使って OS をインストールするため true でも問題ないです、false にすることでコンソールを確認すると OS のインストール画面が確認できるのでデバッグに役立ちます
  • keep_registered・・・ビルド完了後にサーバを削除するかしないか指定できます、true にするとサーバはシャットダウン後削除されません

ESXi 情報

  • remote_type・・・「esx5」を絶対指定しましょう、それ以外の指定ではエラーとなります
  • remote_host・・・ビルドする ESXi の IP を指定します
  • remote_datastore・・・ダウンロードする ISO を格納したり、成果物となる vmx や vmdk を格納する ESXi のデータストアを指定します
  • remote_password・・・ESXi に SSH ログインするための root パスワードを指定します
  • disk_type_id・・・VM を作成する際のディスクタイプを指定します、thin の場合はシンプロビジョニングが適用されます、他には zeroedthick, eagerzeroedthick などが指定できます、0 - 5 の数字でも指定できます
  • vmx_data・・・VM の構成を細かく設定することができます、CPU やメモリネットワークや接続する外部記憶デバイスを指定することができます、指定するフォーマットは VMware に準拠するため詳細は VMware のドキュメントを確認する必要があります、ethernet0.networkName は 1 枚目の NIC を指定することができます

VNC 関係

  • vnc_port_min・・・ビルド時に作成される VM が LISTEN する VNC サーバに使用される最小ポート番号を指定します
  • vnc_port_max・・・ビルド時に作成される VM が LISTEN する VNC サーバに使用される最大ポート番号を指定します、今回の場合 VM は 5900 から 5911 番の間で VNC サーバを LISTEN します
  • vnc_bind_address・・・ビルド時に作成される VM が LISTEN する VNC サーバのバインド IP を指定します
  • vnc_disable_password・・・VNC サーバのパスワード認証を使うか使わないか指定します、true の場合認証を使いません

VNC に関して少し補足として vSphere 環境に作成される VM にはオプションで VNC サーバを立ち上げることができます
もし VM に VNC を立ち上げるとコンソールの情報を VNC でも確認することができます
packer はデフォルトで VNC 接続を使うので VNC 関連のオプションを指定する必要があります

ビルド

  • packer build centos7.json

成果物の保存場所

  • ls /vmfs/volumes/datastore12/output-centos7

ちなみに再度ビルドする場合 output-centos7 フォルダがあるとビルドエラーになるので事前に削除しておきましょう

最後に

packer + esxi で CentOS をビルドしてみました
基本的な操作を確認したかったので、変数の展開やプロビジョニングはしませんでした
興味があればドキュメントを確認しながら実施すれば簡単にできると思います

参考サイト

2017年9月18日月曜日

Swift3 で UITableView を使う方法

概要

基本中の基本なのですが、delete とか dataSource の使い方を忘れてしまうのでメモとして残しておきます

環境

  • macOS X 10.12.6
  • Xcode 8.3.3 (8E3004b)

プロジェクト作成

Single View Application で作成しましょう

Main.storyboard で UI を作成する

すでに作成されている ViewController に追加していきます

UITableView を追加

まず View があるので、その配下に UITableView を追加します
上下左右の Constraints を 0 で設定しましょう
swift3_basic_uitableview1.png

UITableViewCell を追加

その配下に UITableViewCell を追加しましょう
Identifier にセル識別する ID を設定しておきます
今回は「cell」としました
swift3_basic_uitableview2.png

またセルの高さもここで設定できるので設定しておきます
今回は「100」としました

ContentView 配下にコンテンツを追加

セルに表示するコンテンツを追加していきます
今回はよくある定番の画像とラベルを追加します
UILabel と UIImageView を右ペインの一覧から引っ張ってきて配置しましょう
でここでポイントですが、ContentView 内に配置したコンテンツには Constrainsts をしっかり設定しましょう
今回は左に画像を右にラベルを配置するように Constraints を設定しました

ここの Constraints を設定するのが結構たいへんでセルが狭いと Constraints を設定したときに UIImageView と UILabel が重なっているせいで制約が親の View になってしまったりします
なので初めに手動で各種コンテンツを配置したい場所にだいたい配置して、そのあとで Constraints を設定することで Xcode が良い感じに設定してくれるようになります (言葉で説明するのが難しい、、、)

あと設定しなければいかないのは Tag です
各コンテンツに数字のタグを付与することができます
swift ファイル側で各コンテンツを取得する際に必要になるので必ず設定しましょう
またタグはデフォルトが 0 になっていて 0 は使えないので必ず 1 から設定するようにしてください
今回は UIImageView を 1、UILabel を 2 にしました

最終的には以下のようになれば OK です
swift3_basic_uitableview3.png

セルの高さを 100 にしたので UIImageView の高さも Constraints で 100 にしました

delegate と dataSource を紐付ける

ちょっとややこしいので UITableView を選択した状態で Ctrl を押しながら黄色の丸にドロップアンドドロップしてください
すると delegate と dataSource という項目が出るので 2 つとも UITableView に紐付けましょう
swift3_basic_uitableview5.png

紐付けできると右ペインの Outlets の項目に delegate と dataSource が追加されているのがわかると思います

ViewController.swift でテーブル情報を操作する

これもデフォルトである ViewController を使っていきます
Single View Application でプロジェクトを作成した場合 Main.storyboard にある UIViewController のクラスがデフォルトで ViewController.swift になっています

tableView を swift に追加する

画面を分割して storyboard 側から Ctrl を押したまま UITableView の情報をドラックアンドドロップすれば OK です
以下の 1 行が swift 側に追加されれば OK です

@IBOutlet weak var tableView: UITableView!

swift3_basic_uitableview4.png

UITableViewDelegate を継承して必要な関数を実装する

あとは実際にテーブル情報にコンテンツを表示するロジックを swift 側に記載していきます

まず全体のコードはこちら

  • ViewController.swift
import UIKit

class ViewController: UIViewController, UITableViewDelegate {

    @IBOutlet weak var tableView: UITableView!
    let pokemons = [
        [
            "name": "モクロー",
            "image": "http://www.pokemon.co.jp/ex/sun_moon/common/images/pokemon/partner_01/portrait.png"
        ],
        [
            "name": "ニャビー",
            "image": "http://www.pokemon.co.jp/ex/sun_moon/common/images/pokemon/partner_02/portrait.png"
        ],
        [
            "name": "アシマリ",
            "image": "http://www.pokemon.co.jp/ex/sun_moon/common/images/pokemon/partner_03/portrait.png"
        ]
    ]

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    // Cell の数を返却します
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
        return pokemons.count
    }

    // Cell の情報を生成します
    func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell {
        let pokemon = pokemons[indexPath.row]
        // Identifier を元にセルを取得
        let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell")! as UITableViewCell
        // タグ番号 1 で UIImageView インスタンスの生成
        let url = URL(string: pokemon["image"]!)
        let data = try? Data(contentsOf: url!)
        let image: UIImage = UIImage(data: data!)!
        let imageView = cell.viewWithTag(1) as! UIImageView
        imageView.image = image
        // タグ番号 2 で UILabel インスタンスの生成
        let name = cell.viewWithTag(2) as! UILabel
        name.text = pokemon["name"]
        return cell
    }

    // Cell の高さを返却します, StoryBoard の TableView の高さと同じにする必要があります
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 100
    }
}

まず UITableViewDelegate を継承します
そして最低限必要なメソッドを 3 つ定義します「numberOfRowsInSection」「cellForRowAtIndexPath」「heightForRowAt」

numberOfRowsInSection はテーブル内に含まれるセルの数を返却する関数です
基本はテーブルに表示する配列などのサイズを返します

cellForRowAtIndexPath はテーブル内のセルにコンテンツを表示する関数です
index が引数で受け取れるのでこの引数を元に UITableViewCell の情報を取得します
そして取得したセル情報のタグ情報をもとに viewWithTag メソッドを使ってコンテンツ表示用の View を取得します
あとはコンテンツ情報をそれぞれの View にセットし最後に cell を返却することでセルにコンテンツが表示されます

heightForRowAt はセルの高さを返す関数です
Main.storyboard 側でも定義していると思いますが関数も定義しましょう

他にも使える関数はたくさんあるので詳しくは参考サイトにある Apple 公式のドキュメントを御覧ください

ATS について

今回はとりあえず ATS を off にしました
Targets を開いて Info に「App Transport Security Settings」->「Allow Arbitrary Loads」を「YES」に設定しています

本当ならちゃんと http するドメインを登録する必要があります

動作確認

あとは実行するだけです
今回は 3 つしかコンテンツがありませんが画面外の場合もちゃんとスクロールすれば表示されます
swift3_basic_uitableview6.png

最後に

Swift3 での UITableView の使い方を紹介しました
iOS アプリの開発手法としてはかなり初歩的なやり方になるかなと思います
結構複雑そうには見えますが、慣れれば簡単に使えるようになると思います

ちなみに今回のようにセルで画像を表示する方法はダメな方法です
これだとセルが表示されるたびに通信をするので大量にスクロールするとアプリがダウンすると思います
本来であれば画像を一度ダウンロードしたらメモリなどにキャッシュしてキャッシュがある場合はそれを表示するようなロジックが必要になります

過去にそれを解決する手法も紹介しているので興味があれば御覧ください

参考サイト

2017年9月17日日曜日

Swift3 で YoutubePlayer を使って動画を再生する方法

概要

Swift3 で YouTube の動画を再生する方法を紹介します
今回は YouTubePlayer というライブラリを使って再生させてみます

環境

  • macOS X 10.12.6
  • Xcode 8.3.3 (8E3004b)
  • YouTubePlayer 0.4.0

ライブラリインストール

cocoapods でインストールすることができます

  • vim Podfile
pod 'YouTubePlayer'
  • pod install

View を設置して YouTubePlayerView をカスタムクラスに設定する

まず Main.storyboard を編集します
ViewController に 1 つ View を配置し Class に「YouTubePlayerView」を設定し Module に「YouTubePlayer」を設定しましょう
Constraints は適当に設定してください
swift3_with_youtubeplayer1.png

IBOutlet で .swift ファイルに接続する

設置した YouTubePlayerView を Control + ドラッグで .swift ファイル上に接続しましょう
.swift ファイルには import が必要なので以下を追記します

import YouTubePlayer

うまく接続できると以下の変数が自動で追加になります

@IBOutlet weak var player: YouTubePlayerView!

swift3_with_youtubeplayer2.png

こんな感じになれば OK です

YouTube の動画を再生するロジックを追記すうる

.swift ファイル側で追加した View を制御して動画を再生してみましょう
といっても以下の 1 行を追加するだけです

player.loadVideoID("1rlB8HSuWks")

今回は viewDidLoad 内でコールしています
ここで指定している ID は動画にアクセスしたときに URL に含まれる watch?v=1rlB8HSuWks の部分です

動作確認

あとは実行するだけです
シミュレータでも動作します
swift3_with_youtubeplayer3.gif

再生、停止はもちろん早送りや早戻し、全画面表示も対応しています

最後に

Swift3 で YouTubePlayer を使って YouTube の動画を再生してみました
今回試したシミュレータの iOS のバージョンが 10.3.1 だったので iOS11 でも動作するかは不明です
Github で公開されているコードは一応メンテされているっぽいですが今後も続くかは不明です
おそらく内部的には AVFoundation の AVPlayer などを使っていると思うので自分でやろうと思えば同じことはできますが結構簡単に使えるので、今後もエンハンスしてほしいなと思います

すでに Swft3 の環境でビルドすると YouTubePlayer のライブラリの警告が 10 個ほど出るので心配な気もしますが、、、

参考サイト

2017年9月16日土曜日

SpriteKit で AVPlayer + SKVideoNode を使って動画を再生してみた

概要

SpriteKit で動画を再生した場合はあると思います
SKVideoNode という SpriteKit で動画を再生するためのノードがありこれと AVFoundation の AVPlayer というモジュールを使うと動画を再生することができます

環境

  • macOS X 10.12.6
  • Xcode 8.3.3 (8E3004b)

再生する動画のダウンロード

今回はこれを利用します
ダウンロードしたら Targets -> Build Phases -> Copy Bundle Resources にダウンロードしたファイルを追加します
spritekit_with_avplayer1.png

Swift ファイルの編集

.sks ファイルの編集は省略します
動画をスタートする用のボタンと停止する用のボタンを配置しているだけです

import SpriteKit
import AVFoundation

class VideoScene: SKScene {

    var play = SKSpriteNode()
    var pause = SKSpriteNode()
    var video = SKVideoNode()

    override func didMove(to view: SKView) {
        play = self.childNode(withName: "play") as! SKSpriteNode
        pause = self.childNode(withName: "pause") as! SKSpriteNode
        video = {
            let urlString = Bundle.main.path(forResource: "mp4_h264_aac", ofType: "mp4")
            let url = URL(fileURLWithPath: urlString!)
            let item = AVPlayerItem(url: url)
            let player = AVPlayer(playerItem: item)
            return SKVideoNode(avPlayer: player)
        }()
        self.addChild(video)
    }

    func touchDown(atPoint pos : CGPoint) {
        if let node = atPoint(pos) as? SKSpriteNode {
            if(node == play){
                if let _ = self.view {
                    print("play")
                    video.play()
                }
            } else if(node == pause){
                if let _ = self.view {
                    print("pause")
                    video.pause()
                }
            }

        }
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for t in touches { self.touchDown(atPoint: t.location(in: self)) }
    }
}

コード中で SKVideoNode を作成しています
あとはノードをタップしたときに playpause を実行しています

動作確認

必ず実機で行うようにしてください
シミューレータだとなぜか動画のノードが表示されませんでした (音はするんですが)
各種ボタンをタップして動画の再生と停止ができれば OK です

動かしてみるとわかりますがループ再生できません
一度最後まで行ったら再度アプリを起動しないと動画が始めに戻りません

これが SKVideoNode を使った場合の最大の弱点です
一応ループしたりシークバーを出したりする方法はあるみたいなので興味があれば調べてみてください

また今回は Web にある音源をダウンロードしてから再生しました
直接 URL を指定して再生できるかやってみたのですがうまくできませんでした
今回の組み合わせだとローカルにダウンロードしてからでないと再生できないのかもしれません

参考サイト