- 投稿日:2021-06-20T23:39:42+09:00
【GitHub Actions】DenoのカバレッジをCodecovで出す
自分はちょっとした書き捨てのスクリプトのために、よく使う関数をGitHubにまとめて置いています。とりあえずGitHubに置いておけば、rawファイル用のURLを用いてどこからでもimportできるので便利なんですよね。 GitHubに置いたついでに、GitHub ActionsとCodecovを使ってDenoのカバレッジツールを試してみました。 カバレッジとは Denoには組み込みのテストランナーがあります(deno testコマンド)。コード全体のうち、テストが実行された割合(カバレッジ)を出力してくれる機能もあります(deno coverageコマンド)。 今回は、GitHubと連携してカバレッジを表示してくれるCodecovというサービスを使って、GitHubへのpush時にカバレッジを自動で表示させたいと思います。 GitHub Actionsの設定 /.github/workflows/test.ymlファイル内に設定を書いていきます。 Git Checkout リポジトリをチェックアウトします。 - name: Git Checkout Module uses: actions/checkout@v2 Denoのインストール GitHub Actions上にDenoをインストールするためのアクションが存在します。 少し前までdenolib/setup-denoというコミュニティ作成のアクションが主流だったのですが、今は新しく公開された公式のdenoland/setup-denoを使うことが推奨されています。 - name: Use Deno uses: denoland/setup-deno@v1 with: deno-version: v1.x テスト&カバレッジ収集 deno testコマンドでカバレッジを出すことができます。 - name: Test run: deno test -A --coverage=coverage 上記のコマンドを実行すると、/coverage/ディレクトリにカバレッジのデータが出力されます。 lcovファイルの出力 deno coverageコマンドでlcovファイルが出力できます。 - name: Create coverage report run: deno coverage ./coverage --lcov > coverage.lcov 上記のコマンドを実行すると、/coverage/ディレクトリのデータからlcovファイルを作成し、coverage.lcovに吐き出してくれます。 Codecov ファイルをCodecovにアップロードします。 Codecov公式のアクションを利用します。 - name: Codecov uses: codecov/codecov-action@v1.5.2 with: file: ./coverage.lcov fail_ci_if_error: true その他 ついでに、リンター、フォーマッター、型チェックのコマンドも実行したいと思います。 - name: Format # フォーマッター run: deno fmt --check - name: Lint # リンター run: deno lint - name: Type Check # 型チェック run: deno test --no-run $(find . -regex '.*\.\(js\|ts\|jsx\|tsx\|mjs\|mjsx\)' -printf '%p ') GitHub Actionsではファイル名のglob展開が使えないようだったので(調べれば使える方法があるのかもしれませんが)、型チェックのファイル指定にはfindコマンドを使用しています。 全てのファイルを型チェックしていますが、(Worker用のファイルがあるなど)この挙動が望ましくない場合は適宜変更してください。 最終的に完成したのが次のファイルです。 /.github/workflows/test.yml name: Format & Lint & Type Check & Test & Coverage # push時とpr時に実行 on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - name: Git Checkout Module uses: actions/checkout@v2 - name: Use Deno uses: denoland/setup-deno@v1 with: deno-version: v1.x # 最新バージョンを使用 # フォーマッタ - name: Format run: deno fmt --check # リンター - name: Lint run: deno lint # 型チェック - name: Type Check run: deno test --no-run $(find . -regex '.*\.\(js\|ts\|jsx\|tsx\|mjs\|mjsx\)' -printf '%p ') # カバレッジ計測 - name: Test run: deno test -A --coverage=coverage # lcovファイル出力 - name: Create coverage report run: deno coverage ./coverage --lcov > coverage.lcov # Codecovにアップロード - name: Codecov uses: codecov/codecov-action@v1.5.2 with: file: ./coverage.lcov fail_ci_if_error: true Codecovとの連携 Codecovに登録 公式ページからサインアップします。GitHubアカウントと連携させることができます。 プルリクにカバレッジを表示 公式Github Appsをインストールすると、プルリクのコメントでカバレッジを表示してくれます。 バッジを表示 Readme.mdにバッジを表示させることができます。 リポジトリ > Settings > Badgeからマークダウン用のコードをコピペすると、画像のようなバッジが表示されます。 以上の手順で、GitHub ActionsとCodecovを連携させ、Deno用コードのカバレッジを出すことができました! 感想 GitHub Actionsでリンター、フォーマッタ、型チェック、テスト、カバレッジをチェックするようにしました。 ところで、ここまでDeno以外の追加のツールを一切使っていないのにお気づきでしょうか。 Node.jsの場合は、ESLint、Prettier、tsc、Jestをインストールした上で、それぞれに対して設定ファイルを書く必要がありました。しかし、Denoの場合は組み込みのツールが利用できるため、追加のツールが一切必要ありません。とても便利ですね。 (ところで、コアチームはESLintとPrettierとJestとCloudflare WorkersとESDocとnpmを同時に保守管理しているようなもので、かなり忙しそうに見えますね…)
- 投稿日:2021-06-20T23:03:52+09:00
Vue.jsでカスタムページネーションを作る
Vue.jsにはVue Pagination 2というpagination用のライブラリがありますが、仕組みを理解したいと思い自作しました。今回はそのカスタムページネーションについて記事にします。ソースコードはGitHubにて公開しています。 手っ取り早く実装したい方は上記ライブラリを使うことをお勧めします。時間があり、仕組みから理解したい人は読んでくれたら嬉しいです。 完成図 手の込んだことはしておらず、ページボタンを押すと表示するアイテム一覧が変わるだけの簡単な実装にしています。 対象読者 普段pagination実装はライブラリで済ませているけど、今こそ中身を理解したいと言う人 既存ライブラリのpaginationに満足しておらず、自作paginationを作りたいと思っている人 内容 方針 やっていることは決して難しいことではなく、表示したい部分のインデックスを取得して、それに対応する配列の一部分を取ってくるだけです。ページの変更は、表示したいインデックスを変更するだけで大丈夫です。 それでは実装に移りましょう。 実装 紙面の都合上、重要な部分のみ取り上げます。詳細に関してはGitHubより確認して頂ければと思います。 Vue.jsの基本はすでに理解している前提で話を進めますが、難しいことはしていないので基本を押さえている方であれば大丈夫だと思います。 ページネーションコンポーネントには次の2つを渡します。 1. アイテムを格納した配列 (items) 2. 1ページに表示したいアイテム数 (itemNumPerPage) App.vue <template> <div class="home"> <pagination :items="items" :itemNumPerPage="10" /> </div> </template> 今回はデモなので渡すアイテム配列は要素に文字列を持つ配列にしています。 続いて、ページネーションコンポーネントについて見ていきます。 Paginaiton.vue <template> <div class="pagination"> <div class="item-list"> <item v-for="(item, idx) in displayItems" :key="idx" :title="item" /> </div> <div class="page-btns"> <page-button @changePage="changePage" v-for="n in pageNum" :key="n" :pageNumber="n" :curPage="curPage" /> </div> </div> </template> <script> import Item from '@/components/Item.vue'; import PageButton from '@/components/PageButton.vue'; export default { components: { Item, PageButton }, props: { items: Array, itemNumPerPage: Number, }, data() { return { curPage: 1, // curPage starts from 1 pageNum: 0, // number os pages } }, created() { this.calcPageNum(); }, computed: { displayItems() { const startIdx = (this.curPage - 1) * this.itemNumPerPage; const endIdx = startIdx + this.itemNumPerPage; return this.items.slice(startIdx, endIdx); }, }, methods: { changePage(value) { this.curPage = value; }, calcPageNum() { this.pageNum = Math.ceil(this.items.length / this.itemNumPerPage); } } } </script> itemコンポーネントは単に渡した数字を表示しているだけなので省きます。 実際に画面に表示したいアイテム部分列を選んでいるのは算出プロパティdisplayItemsです。 displayItems() { const startIdx = (this.curPage - 1) * this.itemNumPerPage; const endIdx = startIdx + this.itemNumPerPage; return this.items.slice(startIdx, endIdx); } 表示したい配列部分の開始、終了インデックスを計算して、スライス関数を使って部分配列を作成して返しています(現在のページ番号を保存する変数curPageは画面に表示されているページ番号と揃えるために1スタートにしている点に注意)。 ページ数の計算はcalcPageNum関数で行っています。この関数はcreatedで呼び出しています。 calcPageNum() { this.pageNum = Math.ceil(this.items.length / this.itemNumPerPage); } Math.ceil()にしているのは、最終ページの表示アイテム数がitemNumPerPageより小さい場合でも表示できるようにするためです。 続いてPageButtonコンポーネントについて見ます。 PageButton.vue <template> <div class="page-btn"> <button @click="btnPressed" :class="{active: isCurrentPage}" >{{ pageNumber }} </button> </div> </template> <script> export default { props: { pageNumber: Number, curPage: Number }, computed: { isCurrentPage() { return this.pageNumber === this.curPage; } }, methods: { btnPressed() { this.$emit('changePage', this.pageNumber); } } } </script> propsでcurPageを受け取り、同じページ番号のボタンの背景色は変えています。 ボタンが押されたときに親コンポーネントに対してcurPageを変更するように知らせています。親コンポーネントであるpagination.vueではその知らせを受け取ったら、changePage関数を呼び出して新しいページ番号にcurPageを変更しています。 pagination.vue changePage(value) { this.curPage = value; } 以上で実装に関しては終わりです。 まとめ 本記事ではVue.jsを使ったカスタムページネーションの作成について解説しました。 時間ができたときに、一つ前、先のページに進むボタンなどを付け加えたいと思っています。 「もっとこうした方が良いよ」「こういうやり方もあるよ」というアドバイスがあれば、ぜひコメントを書いてくださると助かります。 参考資料 Pagination in Vanilla Javascript
- 投稿日:2021-06-20T22:36:00+09:00
JavaScriptでカウントダウンタイマーを作る
HTML + CSS + JavaScript でカウントダウンタイマーを作りました。ソースコードに直接記述した日時までカウントダウンします。 URL https://cordelia-sixth.github.io/countdown_timer_js/ ソースコード https://github.com/cordelia-sixth/countdown_timer_js ディレクトリ構成 app ├── index.html ├── script.js ├── style.css └── background.jpeg index.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Countdown Timer</title> <link rel="stylesheet" href="style.css"> <script src="script.js" defer></script> </head> <body> <h1>Time left in 2021</h1> <div class="countdown-container"> <div class="countdown-elm"> <p class="time-text" id="days">0</p> <span>days</span> </div> <div class="countdown-elm"> <p class="time-text" id="hours">0</p> <span>hours</span> </div> <div class="countdown-elm"> <p class="time-text" id="mins">0</p> <span>mins</span> </div> <div class="countdown-elm"> <p class="time-text" id="seconds">0</p> <span>seconds</span> </div> </div> </body> </html> script.js const daysElm = document.getElementById('days'); const hoursElm = document.getElementById('hours'); const minsElm = document.getElementById('mins'); const secondsElm = document.getElementById('seconds'); // ここに好きな日時を記述する // 西暦 月 日 const target = '2022 1 1'; function countDown() { const targetDate = new Date(target); const currentDate = new Date(); const totalSeconds = (targetDate - currentDate) / 1000; const days = Math.floor(totalSeconds / 3600 / 24); const hours = Math.floor(totalSeconds / 3600) % 24; const mins = Math.floor(totalSeconds / 60) % 60; const seconds = Math.floor(totalSeconds % 60); daysElm.innerHTML = days; hoursElm.innerHTML = formatTime(hours); minsElm.innerHTML = formatTime(mins); secondsElm.innerHTML = formatTime(seconds); } function formatTime(time) { return time < 10 ? `0${time}` : time; } setInterval(countDown, 1000); countDown()がタイマー処理です。setInterval()により1秒ごとに呼び出されて時間が更新されます。 formatTime()で表示される時間を整形しています。時間、分、秒が10以下の場合は常にゼロを表示させてレイアウトが崩れるのを防いでいます。 style.css @import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro&display=swap'); * { box-sizing: border-box; } body { background-image: url(./background.jpeg); background-size: cover; background-position: center center; display: flex; flex-direction: column; align-items: center; min-height: 100vh; font-family: 'Source Sans Pro', sans-serif; margin: 0; } h1 { font-weight: normal; font-size: 4rem; margin-top: 5rem; } .countdown-container { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; } .time-text { font-weight: bold; font-size: 6rem; line-height: 1; margin: 1rem 2rem; } .countdown-elm { text-align: center; } .countdown-elm span { font-size: 1.2rem; } 終わりに 時間の計算方法がとても勉強になりました。こういった単位換算が苦手なので...。次は日時を入力できるフォームを追加したり、複数のタイマーを表示できるようにしてみたいですね! 読んでいただきありがとうございました!フィードバックしていただけたら嬉しいです! URL https://cordelia-sixth.github.io/countdown_timer_js/ ソースコード https://github.com/cordelia-sixth/countdown_timer_js 参考 リンク Github https://github.com/cordelia-sixth Products https://raindrop.io/cordelia/my-products-18715918 Qiita https://qiita.com/cordelia Twitter https://twitter.com/cordelia_sixth
- 投稿日:2021-06-20T21:42:25+09:00
リアルタイムチャートをDjango ChannelsとEpoch.jsでつくってみた
はじめに あるとき、モーションセンサデータをリアルタイムで可視化したいなと思うことがありました。そこで、ネットでリアルタイム可視化ツールについて調べてみると、近年のIoTブームもありたくさんのツール、製品が世の中に出ていました。無料のツールだと、「Grafana」などがあります。個人的には「Node-RED」のdashboardも気に入っています。 では、なぜDjangoでリアルタイムチャートを作ろうと思ったのか? 理由は3つです。 WebアプリケーションであるためWebブラウザから手軽に利用できる 可視化をちょっと試してみたい、ちょっとカスタマイズしたいとき、言語がPythonだと嬉しい人が多そう(フロントエンド部分は結局jsに頼るのですが。。) 自分の勉強のため 自分の勉強がてら、誰かのお役に立てれば幸いです。 システム構成 先に結論です。完成版はこちらです。 概要 今回のシステムはDjango Channelsを用いて実装します。Django Channelsでは、Channelsを介してメッセージをやり取りすることで、HTTPと似た方法でWebsocketの実装が行えます。(画像:Finally, Real-Time Django Is Here: Get Started with Django Channelsより) HTTP & Websocket 今回のシステムではブラウザから送られてくるHTTPリクエストの処理はHTTP(VIEW)が、センサーから送られてくるデータをブラウザへ送信する処理はWebsocket(COUSUMER)が実行します。 センサとの通信 センサデータはCONSUMERへ送信します。プロトコルで任意です(MQTTやUDPなど)。今回はHASC Loggerというモーションセンサアプリを用いて、加速度をセンシングし、UDPで送信します。 可視化 可視化部分はリアルタイムチャートの実装に特化したライブラリであるEpoch.jsを使用します。 環境構築 今回はWSL(ubuntu-18.04)上で作業していきます(普段はLinux上で作業することが多いので)。 Python環境はpyenvで構築します。anaconda3-5.3.1をインストールします(pyenvのインストール方法は省略します) pyenv install anaconda3-5.3.1 pyenv global anaconda3-5.3.1 Pythonのバージョンは3.7.0です。 python -V Python 3.7.0 djangoとchannelsはpipでインストールします(django==3.2.4 channels==3.0.3で動作確認しました)。 pip install django==3.2.4 channels==3.0.3 実装 プロジェクトを作成する realtime_chart_projectという名前のプロジェクトを作成します。 django-admin startproject realtime_chart_project 一応、動作確認します cd realtime_chart_project python manage.py runserver ブラウザからhttp://127.0.0.1:8000へアクセスします。 アプリケーションを作成 realtime_chartという名前のアプリを作成します。 python manage.py startapp realtime_chart settings.pyのINSTALLED_APPSに'realtime_chart'を追加します。 realtime_chart_project/realtime_chart_project/settings.py INSTALLED_APPS = [ 'realtime_chart', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] HTTPリクエスト/レスポンスの実装 はじめにURLを設定していきます。DjangoはHTTPリクエストを受け取ると、ルートのURLの設定から参照して views.pyを探し、views.pyを呼び出してリクエストを処理します。 realtime_chart_project/realtime_chart/urls.py from django.urls import path from . import views urlpatterns = [ path('', views.chart, name='chart'), ] realtime_chart_project/realtime_chart_project/urls.py from django.contrib import admin from django.urls import include, path urlpatterns = [ path('realtime_chart/', include('realtime_chart.urls')), path('admin/', admin.site.urls), ] 続いてviews.pyにリクエストを処理するコードを記述します。ここではチャートを表示するchart.htmlを返す関数を定義します。 realtime_chart_project/realtime_chart/views.py from django.shortcuts import render def chart(request): return render(request, 'chart.html', {}) chart.htmlの中身は可視化の部分で説明します。 Websocketの実装(Django Channelsの設定) settings.pyのINSTALLED_APPSに'channels'を追加します。 settings.py INSTALLED_APPS = [ 'channels', 'realtime_chart', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] また、ルートのルーティング設定にChannelsを追加するために、以下をsettings.pyに追記します。 realtime_chart_project/realtime_chart_project/settings.py # Channels ASGI_APPLICATION = 'realtime_chart_project.asgi.application' consumers.pyを実装します。 ここではUDPでセンサからデータを受け取り、Websocketでブラウザにデータを送信する処理が行われています。 realtime_chart_project/realtime_chart/consumers.py from channels.generic.websocket import WebsocketConsumer import json import threading import time import random from socket import socket, AF_INET, SOCK_DGRAM class SensorConsumer(WebsocketConsumer): def connect(self): self.accept() self.start_publish() def disconnect(self, close_code): self.stop_publish() def start_publish(self): self.publishing = True self.t = threading.Thread(target=self.publish) self.t.start() def stop_publish(self): self.publishing = False self.t.join() def publish(self): # UDPの設定 HOST = '' PORT = 4001 s = socket(AF_INET, SOCK_DGRAM) s.bind((HOST, PORT)) while True: # センサーからUDPでデータを受信 msg, address = s.recvfrom(8192) t = int(float(msg.decode('utf-8').split('\t')[0])) x = float(msg.decode('utf-8').split('\t')[-1].split(',')[1]) y = float(msg.decode('utf-8').split('\t')[-1].split(',')[2]) z = float(msg.decode('utf-8').split('\t')[-1].split(',')[3]) # Websocketで送信 if self.publishing == False: break self.send(text_data=json.dumps([ {'time': t,'y': x,}, {'time': t,'y': y,}, {'time': t,'y': z,}, ])) s.close() rounting.pyを設定します。 realtime_chart_project/realtime_chart/rounting.py from django.urls import re_path from . import consumers websocket_urlpatterns = [ re_path('ws/', consumers.SensorConsumer.as_asgi()), ] 最後にルートのルーティング設定にrealtime_chart.routingモジュールを追加します。asgi.pyで、AuthMiddlewareStack、URLRouter、realtime_chart.routingをインポートし、ProtocolTypeRouterを以下の形式で"websocket"キーに挿入します。 realtime_chart_project/asgi.py import os from channels.auth import AuthMiddlewareStack from channels.routing import ProtocolTypeRouter, URLRouter from django.core.asgi import get_asgi_application import realtime_chart.routing os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'realtime_chart_project.settings') application = ProtocolTypeRouter({ "http": get_asgi_application(), "websocket": AuthMiddlewareStack( URLRouter( realtime_chart.routing.websocket_urlpatterns ) ), }) 可視化の実装 Epoch.jsを使用するのに必要なファイルをダウンロードします。 ダウンロードしたファイルの中のcss/epoch.min.cssとjs/epoch.min.jsをrealtime_chart_project/static/下に置きます。 static ├── css │ └── epoch.min.css └── js └── epoch.min.js D3.jsはCDN(Content Delivery Network)上のものを使用します。 <script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script> chart.htmlを作成します。 realtime_chart_project/templates/chart.html <html> <head> <title>graph test</title> </head> <body> <h1>Real time chart</h1> <div id="graph" class="epoch" style="height: 200px;"></div> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> <script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script> {% load static %} <script type="text/javascript" src="{% static 'js/epoch.min.js' %}"></script> <link rel="stylesheet" type="text/css" href="{% static 'css/epoch.min.css' %}"> <script type="text/javascript"> const chatSocket = new WebSocket( 'ws://' + window.location.host + '/ws/' ); var data = [ { label: "X-axis", values: [] }, { label: "Y-axis", values: [] }, { label: "Z-axis", values: [] }, ]; var lineChart = $('#graph').epoch({ type: 'time.line', data: data, axes: ['left', 'right', 'bottom'], }); chatSocket.onmessage = function(e) { const current = JSON.parse(e.data); lineChart.push(current); }; chatSocket.onclose = function(e) { console.error('Chat socket closed unexpectedly'); }; </script> </body> </html> 最後に、templatesとstaticのディレクトリをsettings.pyに追加します。 realtime_chart_project/realtime_chart_project/settings.py TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [BASE_DIR, 'templates'], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] STATICFILES_DIRS = ( [BASE_DIR, 'static'] ) 実行 今回はスマートフォンで観測した3軸加速度データをリアルタイムで可視化するデモを行います。 初めにHASC Loggerをスマートフォンにインストールします。 HASC Loggerから[ホストのIP]:4001へ3軸加速度をUDPで送信します。設定の方法は「角速度から回転行列を求める - 実装編」を参考にしてください。 リアルタイムチャートアプリを起動します。 python manage.py migrate python manage.py runserver ブラウザからhttp://127.0.0.1:8000/realtime_chartへアクセスします。 グラフがリアルタイムで表示されたら成功です。 Django ChannelsとEpoch.jsでセンサデータをリアルタイムに表示するチャート作ってみました。Django Channelsを使用することで、HTTPと同じくらい簡単にWebsocketを実装出来ました。Epoch.jsも学習コストが低く使いやすかったです。 pic.twitter.com/oflpVucoVk— yakiimo121 (@yakiimo121) June 20, 2021 まとめ Django ChannelsとEpoch.jsを用いてリアルタイムチャートを作成しました。Django Channelsを使用することで、HTTPと同じくらい簡単にWebsocketを実装出来ました。Epoch.jsも学習コストが低く使いやすかったです。 ただし、Epoch.jsでも100Hzのモーションセンサデータをリアルタイムで描画するのは困難でした。遅延が発生したり、それにともないキューからデータがこぼれたりします。さすがにWebベースの限界なのでしょうか?それとも他のライブラリを使えば解決できるのでしょうか??もちろん、温度データを可視化する場合などでは、もっとサンプリングレートが低くていいので問題ありません。 今後は機能を拡張していきたいと思います。データの保存機能、MQTTへの対応、デザインをもっとかっこよくするなどですかね。 参考 chart https://qiita.com/shiro-kuma/items/0607e01a19e093fdb631 https://4009.jp/post/archives/20 Websocket https://www.keicode.com/script/html5-websocket-1.php https://pypi.org/project/gevent-websocket/ https://pypi.org/project/django-gevent-websocket/ Django Channels https://docs.djangoproject.com/ja/3.2/intro/tutorial01/ https://channels.readthedocs.io/en/latest/tutorial/index.html https://qiita.com/massa142/items/cbd508efe0c45b618b34#groups https://qiita.com/ekzemplaro/items/a6b81bd1d181fdd0cc24 http://engmng.blog.fc2.com/blog-entry-110.html https://kivantium.hateblo.jp/entry/2020/05/02/151321 https://blog.heroku.com/in_deep_with_django_channels_the_future_of_real_time_apps_in_django Epoch https://qiita.com/okoppe8/items/d8d8bc4e68b1da4a0a36 https://syncer.jp/d3js http://epochjs.github.io/epoch/
- 投稿日:2021-06-20T19:05:41+09:00
死活監視をGASを使ってSlackへ通知させてみた〜①〜
今回はタイトル通り、死活監視をGASで書いてSlackに通知させてみたよというお話です。 綺麗なまとめというより自分自身の思考の流れも書いています。 ※まだまだ知識不足で説明不十分や間違っている部分もあるかと思いますので、 もし気になる点があればコメントいただければと思います。 この記事を読む対象者 ・「死活監視」って何?という方 ・GASってなに?ガス?ギャス?という方 ・JavaScriptは少し触ったことがある方 ・GASで書いたものをSlackに通知させてみたい方 死活監視とは? コンピューターやシステムが正常に稼働しているかどうか、外部から継続的に監視すること。 これはWEBサイトの場合、ユーザーから見た場合にサイトが見れない状態だと通知が来るといった流れになるかと思います。 他にも監視のレベルは様々あるかと思いますが、死活監視は最低限、致命的なものを防ぐ、といったイメージです。 (これはプール監視のお兄さん) GASとは? GASとはGoogle Apps Scriptの略称で、読み方としては「ガス」が多いそうです。(ずっと「ギャス」だと思ってました。ギャス!) 名前の通りGoogleが開発・提供しているプログラミング言語になり、基本的にはJavaScriptをベースに作成されていて書き方はほとんど同じかと思います。 今回はGASで死活監視を行いましたが、他にも方法はたくさんありますのであくまで一例として参考にしていただければと思います。 よし、じゃあ死活監視、早速コードを書くぞ!! 、、といきなり手をつけてもコードはかけないので、まずは今わかっていないことを整理しよう。 ①どのようにサイトが死んでいるかどうかの判断をするのか ②Slackへの通知の方法 大きくですが、この①②がわかれば、手を動かすイメージが付きそう! ということで、1つずつ調べていきました。 ①どのようにサイトが死んでいるかどうかの判断をするのか サイト上の一部の文字が取得できるかどうかとか。。? サイトが見れない時のエラー文が含まれていないとか・・? と、あれやこれや考えながら死活監視を調べてみましたが、 どうやらHTTPレスポンスステータスコードというものがあるそうです。 画面のこういうやつです。404の部分です。 聞いたことはありましたし、こういう時に使えるものなんだなと理解しました。 HTTPレスポンスステータスコードとは 超ざっくりですが、HTTPリクエストが正常に完了したかどうかを示すコードです。 ざっと、コード別に分けるとこんな感じです。 ・情報レスポンス (100–199) ・成功レスポンス (200–299) ・リダイレクト (300–399) ・クライアントエラー (400–499) ・サーバエラー (500–599) 今回はこのステータスコードが200ならOK、200以外ならNGとして通知をさせることにしました。 これで①は何とかなりそう! ②Slackへの通知の方法 なぜSlackかという部分は、普段社内の連絡ツールとしてSlackを使っているためです。 Slackに通知が来れば、例えばメールよりもすぐに気づいて対応できるだろうと。 Slack×GASの書き方は大量に記事があったので助かりました。 先人の皆様、ありがとうございます。。 参考リンクは最後にまとめて掲載させていただきます。 GASにはSlackライブラリというものがあり、ライブラリを設定?し、 トークンを取得すればAPI認証等が不要で簡単にSlackへの投稿ができるとのことでした。 ②もライブラリのおかげでイメージもついた! ということで、やっとこさコードを書きます。 Slackのトークンの取得や通知方法は、ほとんどこちらの記事を見て 設定させていただいたので、省略させていただきます。 実際に書いたコード //SlackAPIで登録したボットのトークンを設定する const token = PropertiesService.getScriptProperties().getProperty('SLACK_API_TOKEN'); //ライブラリから導入したSlackAppを定義し、トークンを設定する const slackApp = SlackApp.create(token); //Slackボットがメッセージを投稿するチャンネルを定義する const channelId = PropertiesService.getScriptProperties().getProperty('SLACK_CHANNELID'); // siteURLを定義 const siteUrl = PropertiesService.getScriptProperties().getProperty('SITE_URL'); function isSiteAlive() { const response = UrlFetchApp.fetch(siteUrl, { muteHttpExceptions:true }); // レスポンスコードを取得 const code = response.getResponseCode(); // レスポンスコードが200かどうかでメッセージを変更 if(code == 200 ) { const message = 'サイトは問題なく作動しています'; slackApp.postMessage(channelId, message); else{ const message = '<!channel> サイトがダウンしています!至急確認してください!!'; slackApp.postMessage(channelId, message); } }; ここで分かりにくかった部分だけ補足で解説です。 const response = UrlFetchApp.fetch(siteUrl, { muteHttpExceptions:true }); ここでの UrlFetchApp はHTTPリクエストを行うためのクラスになります。 そして muteHttpExceptions:true こちらはUrlFetchApp.fetch() メソッドと一緒に使用し、 trueに指定しておくと(デフォルトはfalse)エラーを吐かずにHttpResponse を返してくれるようになります。 つまり、HttpResponse の中身を見ればどんなエラーなのかが分かるということです。 今回は返ってくるレスポンスコードを元にSlackに通知を送りたいのでtrueとしています。 あとは最初に並んでいる変数のような部分 //SlackAPIで登録したボットのトークンを設定する const token = PropertiesService.getScriptProperties().getProperty('SLACK_API_TOKEN'); //ライブラリから導入したSlackAppを定義し、トークンを設定する const slackApp = SlackApp.create(token); //Slackボットがメッセージを投稿するチャンネルを定義する const channelId = PropertiesService.getScriptProperties().getProperty('SLACK_CHANNELID'); // siteURLを定義 const siteUrl = PropertiesService.getScriptProperties().getProperty('SITE_URL'); こちらはプロパティに値を設定することで、コードにベタ書きをせずにすみます。 (プロパティの設定方法が、以前のエディタのフォーマットでしかできなかったので その時だけエディタの以前のバージョンに戻していました。。) もし新しいエディタでの方法があれば教えていただきたいです! エディタ上で実行し、大丈夫そうであればトリガーを設定します。 以下の画面から、今回は1分置きの設定にしました。 結果、、、 無事通知が来るようになりました(パチパチ) 最後に(今回の課題) 今回のコードだと以下の課題があります。 ・1つのサイトのみの監視しかできない ・サイトがダウンしている場合、復旧するまでずっと全員にメンションで通知が続く(うるさい) これを ・複数のサイトの監視 ・サーバーが落ちた時に通知が来て、次に復旧するまでは通知が来ないようにする という部分が次の課題かと思います。 ここの課題についてはまた次の記事で書ければと思います! 参考リンク https://e-words.jp/w/%E6%AD%BB%E6%B4%BB%E7%9B%A3%E8%A6%96.html https://developer.mozilla.org/ja/docs/Web/HTTP/Status https://auto-worker.com/blog/?p=2904 https://tonari-it.com/gas-web-api-http-urlfetch/
- 投稿日:2021-06-20T18:48:01+09:00
[JavaScript] HTMLを取得するメソッド三種
はじめに HTMLを取得する三種のJavaScriptメソッドを紹介します。 HTMLの要素を取得する方法としてはid名を指定して取得する方法と、class名を指定して取得する方法と、セレクタ名を取得する方法が存在します。 それでは一つずつ紹介していきます!よろしくお願いします。 document.getElementById("id名") これは引数に渡したidを持つ要素を取得することができます。 //id名がhogeの要素を指定する場合 document.getElementById("hoge") この場合はhogeというid名のHTMLの要素を取得することができます。 document.getElementsByClassName("class名") 使い方はidを使って取得する方法と同じですが、このメソッドの名前のなかのElementsが複数形になっていることに気をつけましょう。 idとの使い分けとしては、HTMLの中で同じ名前のidは存在できないので取得する要素は一つだけになります。 しかし、classはHTMLで複数同じ名前が存在する可能性があるので、それら全ての要素を取得することができます。Elementsが複数形になっている理由も納得ですね! //class名がfugaの要素を指定する場合 document.getElementsByClassName("fuga") document.querySelectorAll("セレクタ名") セレクタ名はCSSでスタイルを適用するために指定している要素です。CSSでスタイル適応する時はid名や、class名、はたまたタグ名を指定しますよね。つまり、先ほど紹介したメソッドを使わなくても、これで完結するのです。 id名を指定する時は、id名の直前に . をつけます。class名は # をつけます。タグ名はそのままです。 //class名がfugaの要素を指定する場合 document.querySelectorAll(".fuga") //id名がhogeの要素を指定する場合 document.querySelectorAll("#hoge") //h1タグの要素を指定する場合 document.querySelectorAll("h1") おまけ ちなみに、document.querySelectorAll("セレクタ名")は合致する要素を全て取得してきますが、 document.querySelectorだと、合致する要素のうち一番はじめに見つかった要素を一つだけ取得します。 //id名,class名,タグ名の指定方法は、querySelectorAllメソッドと同じ document.querySelector("セレクタ名") 最後に これからオリジナルアプリにこの方法でフロントを実装したいところがありまして、おさらいしてみました。 JavaScriptこれから深めていきたいと思います!!!
- 投稿日:2021-06-20T18:00:58+09:00
ES7 ECMAScript 2016 入門
はじめに 今回ですが個人的にES7以降の新機能や構文で追えていない知識がありましたので、自分用のメモとして記述します。今更感も否めないですがそこはご了承くださいませ?♂️ また、今回はES7のみですが、次回以降でES8以降の機能も順番に書いていきたいと思います。 そもそもECMAScriptって? 最初ですので簡単にECMAScriptに触れておきます。 ECMAScriptは、European Computer Manufacturer Associationの略称で、Ecma Internationalによって標準化されたJavaScriptを実装する上での仕様です。ES6(ECMAScript 2015)以降は、毎年6月に公開され、正式な仕様となっております。 ECMAScriptのバージョン(ES6以降) バージョン 略称(通称) 公開日 ECMAScript 2015 ES2015(別名ES6) 2015年6月 ECMAScript 2016 ES2016(別名ES7) 2016年6月 ECMAScript 2017 ES2017(別名ES8) 2017年6月 ECMAScript 2018 ES2018(別名ES9) 2018年6月 ECMAScript 2019 ES2019(別名ES10) 2019年6月 ECMAScript 2020 ES2020(別名ES11) 2020年6月 ES2016(ES7) の新機能 では、早速本題に入ります。 ES7で追加された新機能は主に下記の2つです。 Array.prototype.includes()メソッド べき乗演算子 ・Array.prototype.includes()メソッド まずはArray.prototype.includes()メソッドについてみていきます。 includes()は、配列内に特定の要素が含まれているかどうかを真偽値で返してくれるメソッドです。要素が含まれていればtrue、含まれていなければfalseとなります。 【例】 const array = ['要素1','要素2','要素3']; console.log(array.includes('要素1')); // => true また、includesメソッドは第2引数に配列内で探し始める位置をインデックスで指定することができます。(下記参照) 【構文】 arr.includes(valueToFind[, fromIndex]) 【例1】 const array = ['要素1','要素2','要素3']; console.log(array.includes('要素1', 3)); // => false 上記の例はインデックスに3を指定していますが、配列内の要素は3つ(インデックス番号が2まで)しかないのでfalseが返ります。 【例2】 const array = ['要素1','要素2','要素3']; console.log(array.includes('要素3', -1)); // => true また、上記の例はインデックスに負の数を指定すると、その絶対値の数だけ配列の末尾から戻った位置が検索開始地点となりますのでtrueが返ります。 ・べき乗演算子 続いてべき乗演算子について記述します。 べき乗演算子は、1番目のオペランドを2番目のオペランドの累乗まで上げた結果を返す演算子です。簡単にいうと累乗計算ですね。こちらは例を見て頂いた方がわかりやすいかと思います。 【構文】 Operator: var1 ** var2 【例1】 console.log(2 ** 2); // => 4 console.log(2 ** 3); // => 8 console.log(1 ** -1); // => 1 console.log(NaN ** 1); // => NaN NaNは、累乗してもNaNになります。 【例2】 console.log(2 ** 3 ** 2); // 512 console.log(2 ** (3 ** 2)); // 512 console.log((2 ** 3) ** 2); // 64 べき乗演算子は右結合的の為、a ** b ** c と a ** (b ** c) は等しいべき乗式となります。優先順位と異なる順に計算したい場合は、丸カッコを用いて計算順序を明示します。 はい、今回はここまでとさせて頂きます✍️ おわりに ES7はES6に比べる新機能のボリュームは少ないですが、特にArray.prototype.includes()メソッドは個人的に実務で使える場面が多くありますので大変ありがたいメソッドです?♂️ では、次回はES8について確認していきたいと思います。 参考文献 MDN Web Docs
- 投稿日:2021-06-20T17:59:52+09:00
JSでワード検索
一次元オブジェクト配列のワード検索で完全一致 const objArray = [{id: 1, name: 'hogeo', address: 'shibuya'},{id: 2, name: 'hogeko', address: 'shibuya'},{id: 3, name: 'hageo', address: 'shinjuku'},{id: 4, name: 'hageko', address: 'shinjuku'}]; const word = 'hogeko' const searchByWord = objArray.filter((object)=>{ const searchValues = Object.values(object);// [1, 'hogeo', 'shibuya'] return searchValues.some((str)=>{return String(str).includes(word)}); //'1'=='hogeko', 'hogeo'=='hogeko', 'shibuya'=='hogeko'=> 全部falseなのでfilterではじかれる }); console.log(searchByWord); //[{id: 2, name: 'hogeko', address: 'shibuya'}]; // word = 'shi' ->[{id: 1, name: 'hogeo', address: 'shibuya'},{id: 2, name: 'hogeko', address: 'shibuya'},{id: 3, name: 'hageo', address: 'shinjuku'},{id: 4, name: 'hageko', address: 'shinjuku'}] // word = 'shin' ->[{id: 3, name: 'hageo', address: 'shinjuku'},{id: 4, name: 'hageko', address: 'shinjuku'}] // word = '4' ->[{id: 4, name: 'hageko', address: 'shinjuku'}] // word = ' hogeko ' ->[] 一次元オブジェクト配列のワード検索で複数のワードすべて含む const objArray = [{id: 1, name: 'hogeo', address: 'shibuya'},{id: 2, name: 'hogeko', address: 'shibuya'},{id: 3, name: 'hageo', address: 'shinjuku'},{id: 4, name: 'hageko', address: 'shinjuku'},{id: 5, name: 'hogeo', address: 'shinjuku'}]; const words = 'hogeo shin'.trim().replace(/\s+/g,' '); //trim()を使うと' abc '->'abc' 最初と最後のスペースが詰められる //' abc def '->'abc def' const searchByWord = objArray.filter((object)=>{ const str = Object.values(object).join(""); // '1hogeoshibuya' const wordArray = words.split(' '); //['hogeo', 'shin'] return wordArray.length==1 ? str.includes(words) : wordArray.every((word)=>{return str.includes(word)}); //今回は['hogeo', 'shin']なのでwordArray.every((word)=>{return str.includes(word)})が発動する //'1hogeoshibuya'.includes('hogeo'), '1hogeoshibuya'.includes('shin')=>hogeoが入っているのでtrue, shinは入ってないのでfalse,everyは全てtrueにならなければいけないのでこのオブジェクトははじかれる }); console.log(searchByWord)// [{id: 5, name: 'hogeo', address: 'shinjuku'}]; // words = 'hogeo shi' ->[{id: 1, name: 'hogeo', address: 'shibuya'},{id: 5, name: 'hogeo', address: 'shinjuku'}]; 一次元オブジェクト配列のワード検索で複数のワードどちらかを含む const objArray = [{id: 1, name: 'hogeo', address: 'shibuya'},{id: 2, name: 'hogeko', address: 'shibuya'},{id: 3, name: 'hageo', address: 'shinjuku'},{id: 4, name: 'hageko', address: 'shinjuku'},{id: 5, name: 'hogeo', address: 'shinjuku'}]; const words = 'hogeo hogeko'.trim() const searchByWord = objArray.filter((object)=>{ const str = Object.values(object).join(""); const wordArray = words.split(' '); return wordArray.length==1?str.includes(words):wordArray.some((word)=>{return str.includes(word)}); //上記のeveryをsomeに変更しただけ。hogeoまたはhogekoどちらがtrueなら取得できる。 }); console.log(searchByWord)// [{id: 1, name: 'hogeo', address: 'shibuya'},{id: 2, name: 'hogeko', address: 'shibuya'},{id: 5, name: 'hogeo', address: 'shinjuku'}];
- 投稿日:2021-06-20T17:19:46+09:00
ウェブアプリを独立表示させる方法
ブラウザの現在のページをシンプルな別ウィンドウで表示するブックマークレット ・任意のブラウザを表示する。 ・下記のブックマークレットを実行 javascript:(()=>{window.open(location.href,null,'width=500,toolbar=yes,menubar=yes,scrollbars=yes')})() Discordのウェブ版をアプリ化しよう 訳あってデスクトップ版のアプリが使用できないためDiscordをこのブクマに登録してみる。 javascript:(()=>{window.open('https://discord.com/channels/@me',null,'width=500,toolbar=yes,menubar=yes,scrollbars=yes')})() SUCCESS!
- 投稿日:2021-06-20T17:05:23+09:00
【Javascript】axiosエラーハンドリング共通化を考えてみた
Vue.jsの開発で非同期通信にaxiosを使用している。 非同期通信時のエラーハンドリングのロジック自体は同じで、各axios実装箇所で行っていた。 開発の効率や、ソースコードの可読性、後々メンテナンス、拡張などを考えた時のために共通化を考えてみた。 axios実装のビフォーアフター Before 【既存のaxios実装】 axiosの各呼び出し箇所で下記のようなエラーハンドリングしており、同じロジックコードが散在している。 axios.post(url, params) .then(response => { // 成功時の処理 }).catch(error => { // 失敗時の処理 switch (error.response?.status) { case 401: // HTTPステータスに応じて処理 case 403: default: // 例外処理 } }); After 【エラーハンドリングの共通化後のaxios実装】 エラーハンドリングを共通化したことでaxiosの各呼び出し箇所では成功時の処理のみを実装する。 ソースコードがスッキリした!! const response = axios.post(url, params); if(response) { // 成功時の処理(エラー時は共通コードでハンドリングされるため、responseが返らない) } axiosのエラーハンドリング共通化 axiosAPIのInterceptorsを活用する。 Interceptorsについてはこちら参照:Interceptors | axios公式 Interceptorsを使って、axiosのエラーハンドリングを共通化 axiosErrorHandler.js axios.interceptors.response.use(function (response) { // 成功時の処理 return response; }, function (error) { // 失敗時の処理 switch (error.response?.status) { case 401: // HTTPステータスに応じて処理 case 403: default: // 例外処理 } }); ちなみにInterceptorsはaxiosのrequest前処理も共通化することができる Nuxt.jsを使っていたらpluginが用意されているのでそちらを使用するのが良さそうです →nuxt/axios Vue.jsでのエラーハンドリングについて、ご意見や事例があればぜひお聞かせください
- 投稿日:2021-06-20T15:58:34+09:00
npm package をアップデートしたい時の便利なコマンド
npm package をアップデートしたい時のコマンド npm packageをアップデートしたい時によく使用するコマンドについてまとめます。 今回packageを見ていくのは vue-hackernews-2.0 をです。 $ git clonse https://github.com/vuejs/vue-hackernews-2.0.git $ cd vue-hackernews-2.0 npm outdated outdatedコマンドは、レジストリをチェックして、インストールされている(または特定の)パッケージが現在古くなっているかどうかを確認する。 デフォルトでは、ルートプロジェクトの直接の依存関係と構成されたワークスペースの直接の依存関係のみが表示する。 --allすべての古いメタ依存関係も検索するために使用する。 実行 $ npm outdated Package Current Wanted Latest Location autoprefixer 7.2.6 7.2.6 10.2.6 vue-hackernews-2.0 babel-loader 7.1.5 7.1.5 8.2.2 vue-hackernews-2.0 chokidar 1.7.0 1.7.0 3.5.2 vue-hackernews-2.0 cross-env 5.2.1 5.2.1 7.0.3 vue-hackernews-2.0 css-loader 0.28.11 0.28.11 5.2.6 vue-hackernews-2.0 file-loader 1.1.11 1.1.11 6.2.0 vue-hackernews-2.0 firebase 4.6.2 4.6.2 8.6.8 vue-hackernews-2.0 lru-cache 4.1.5 4.1.5 6.0.0 vue-hackernews-2.0 rimraf 2.7.1 2.7.1 3.0.2 vue-hackernews-2.0 route-cache 0.4.3 0.4.3 0.4.5 vue-hackernews-2.0 stylus-loader 3.0.2 3.0.2 6.1.0 vue-hackernews-2.0 sw-precache-webpack-plugin 0.11.5 0.11.5 1.0.0 vue-hackernews-2.0 url-loader 0.6.2 0.6.2 4.1.1 vue-hackernews-2.0 vue 2.6.12 2.6.14 2.6.14 vue-hackernews-2.0 vue-server-renderer 2.6.12 2.6.14 2.6.14 vue-hackernews-2.0 vue-template-compiler 2.6.12 2.6.14 2.6.14 vue-hackernews-2.0 webpack 3.12.0 3.12.0 5.39.1 vue-hackernews-2.0 webpack-dev-middleware 1.12.2 1.12.2 5.0.0 vue-hackernews-2.0 webpack-merge 4.2.2 4.2.2 5.8.0 vue-hackernews-2.0 webpack-node-externals 1.7.2 1.7.2 3.0.0 vue-hackernews-2.0p current: 現在インストールされているバージョン wanted: 存在するバージョンのうち、 package.json に記載された semver 条件を満たす最新のバージョン。 Latest: レジストリで最新としてタグ付けされているパッケージのバージョン Location: 物理ツリーのどこにパッケージが配置されているか ※ semverとは? Semantic Versioning の略 3.14.1 一番左側の数字をメジャーバージョン、真ん中の数字をマイナーバージョン、一番右側の数字をパッチバージョン パッチバージョン:後方互換性のあるバグ修正やドキュメントの変更などをした場合、パッチバージョンの値をひとつ大きくする。 マイナーバージョン:後方互換性のある機能追加とドキュメントの追記をした場合、マイナーバージョンの値をひとつ大きくする。 メジャーバージョン:後方互換性のない変更がある場合、メジャーバージョンの値をひとつ大きくする。 npm update このコマンドはtag、semverを尊重して、リストされているすべてのパッケージを最新バージョン(構成で指定)に更新する。 また、不足しているパッケージもインストールする。 マイナーバージョンまで最新化される。 実行 $ npm update npm WARN ajv-keywords@3.5.2 requires a peer of ajv@^6.9.1 but none is installed. You must install peer dependencies yourself. + vue-template-compiler@2.6.14 + vue@2.6.14 + vue-server-renderer@2.6.14 updated 3 packages and audited 1001 packages in 4.595s 12 packages are looking for funding run `npm fund` for details found 15 vulnerabilities (4 low, 8 moderate, 3 high) run `npm audit fix` to fix them, or `npm audit` for details vue-template-compiler@2.6.14 vue@2.6.14 vue-server-renderer@2.6.14 の3つのパッケージがlatest(マイナーバージョンの最新バージョン)に更新されました。 npm audit auditコマンドは、プロジェクトで構成されている依存関係の説明をデフォルトのレジストリに送信し、既知の脆弱性のレポートを要求する。 脆弱性が見つかった場合は、影響と適切な修正が必要な箇所が表示される。 low(低レベル), moderate(中レベル), high(高レベル), critical(致命的) の 4種類に分かれます。(infoというのもあります。) https://docs.npmjs.com/cli/v7/commands/npm-audit#audit-level 基本的に全てに対応したいですが、BreakingChangeなどもありなかなか対応できないものもあります。しかしcriticalとhighについては絶対対応すべきです。 実行 $ npm audit ..... found 15 vulnerabilities (4 low, 8 moderate, 3 high) in 1001 scanned packages run `npm audit fix` to fix 2 of them. 12 vulnerabilities require semver-major dependency updates. 1 vulnerability requires manual review. See the full report for details.. (訳) スキャンした1001個のパッケージから15個の脆弱性(低4個、中8個、高3個)が見つかりました。 npm audit fix` を実行して、そのうちの2つを修正しました。 12個の脆弱性は準メジャーな依存関係の更新が必要です。 1つの脆弱性は手動でのレビューが必要です。詳細は報告書の全文をご覧ください。。 ┌───────────────┬──────────────────────────────────────────────────────────────┐ │ High │ Regular Expression Denial of Service │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Package │ normalize-url │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Dependency of │ css-loader [dev] │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Path │ css-loader > cssnano > postcss-normalize-url > normalize-url │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ More info │ https://npmjs.com/advisories/1755 │ └───────────────┴──────────────────────────────────────────────────────────────┘ More info の URIにアクセスすると脆弱性内容の詳細を見ることができます。 今回は、 https://www.npmjs.com/advisories/1755 Overview The xmlhttprequest-ssl package before 1.6.1 for Node.js disables SSL certificate validation by default, because rejectUnauthorized (when the property exists but is undefined) is considered to be false within the https.request function of Node.js. In other words, no certificate is ever rejected. Remediation Upgrade to version 1.6.1 or later (訳) 概要 Node.jsの1.6.1以前のxmlhttprequest-sslパッケージは、デフォルトでSSL証明書の検証を無効にしています。これは、Node.jsのhttps.request関数内でrejectUnauthorized(プロパティが存在するが未定義の場合)がfalseとみなされるためです。つまり、証明書が拒否されることはない。 対応策 バージョン1.6.1以降にアップグレードする。 正規表現を使ったDoS攻撃(ReDoS)を食らう危険があるらしい。。。 自動で修正する方法 npm audit コマンドには脆弱性のある箇所を自動修正してくれるサブコマンドfixがあります $ npm audit fix found 13 vulnerabilities (3 low, 7 moderate, 3 high) in 1001 scanned packages 12 vulnerabilities require semver-major dependency updates. 1 vulnerability requires manual review. See the full report for details. npm ls lsコマンドは、インストールされているパッケージのすべてのバージョンと依存関係をツリー構造で標準出力に出力する。 実行 今回は npm audit コマンドで脆弱性 high の normalize-url について見てみます。 npm ls <package名> $ npm ls normalize-url vue-hackernews-2.0@ /Users/taimikam/workspace/vue-hackernews-2.0 └─┬ css-loader@0.28.11 └─┬ cssnano@3.10.0 └─┬ postcss-normalize-url@3.0.8 └── normalize-url@1.9.1 npm dedupe ローカルパッケージツリーを検索し、依存関係をツリーのさらに上に移動することで全体的な構造を単純化しようとする。依存関係は、複数の依存パッケージでより効果的に共有できる。 以下のような依存関係ツリーがあるとする。 a +-- b <-- depends on c@1.0.x | `-- c@1.0.3 `-- d <-- depends on c@~1.0.9 `-- c@1.0.10 npm dedupe は以下のように変換する。 a +-- b +-- d `-- c@1.0.10 その他便利packageについて npm-check-updates https://github.com/raineorshine/npm-check-updates npm-check-updatesは、指定されたバージョンを無視して、package.jsonの依存関係を最新バージョンにアップグレードします。 実行 ❯ ncu Checking /Users/hoge/workspace/vue-hackernews-2.0/package.json [====================] 35/35 100% compression ^1.7.1 → ^1.7.4 cross-env ^5.1.1 → ^7.0.3 es6-promise ^4.1.1 → ^4.2.8 express ^4.16.2 → ^4.17.1 firebase 4.6.2 → 8.6.8 lru-cache ^4.1.1 → ^6.0.0 serve-favicon ^2.4.5 → ^2.5.0 vue-router ^3.0.1 → ^3.5.1 vuex ^3.0.1 → ^3.6.2 autoprefixer ^7.1.6 → ^10.2.6 babel-core ^6.26.0 → ^6.26.3 babel-loader ^7.1.2 → ^8.2.2 babel-preset-env ^1.6.1 → ^1.7.0 chokidar ^1.7.0 → ^3.5.2 css-loader ^0.28.7 → ^5.2.6 file-loader ^1.1.5 → ^6.2.0 friendly-errors-webpack-plugin ^1.6.1 → ^1.7.0 rimraf ^2.6.2 → ^3.0.2 stylus ^0.54.5 → ^0.54.8 stylus-loader ^3.0.1 → ^6.1.0 sw-precache-webpack-plugin ^0.11.4 → ^1.0.0 url-loader ^0.6.2 → ^4.1.1 vue-loader ^15.3.0 → ^15.9.7 webpack ^3.8.1 → ^5.39.1 webpack-dev-middleware ^1.12.0 → ^5.0.0 webpack-hot-middleware ^2.20.0 → ^2.25.0 webpack-merge ^4.2.1 → ^5.8.0 webpack-node-externals ^1.7.2 → ^3.0.0 現在のバージョン → 最新バージョン を表示してくれます。 ncu -u を実行すると package.jsonを強制的に最新packageを使用するように書き換えてくれます。 最後に 今回紹介した以外にも npm package を更新するのに便利なコマンドがあると思います。 CIなどと組み合わせて自動更新スクリプトを組むのも良いかもしれませんね。
- 投稿日:2021-06-20T14:53:13+09:00
要素をドラッグ&ドロップするのに便利なSortableJS
はじめに Webサイトを構築する上で、「ドラッグ&ドロップを綺麗に見せたい」と思ったことはないでしょうか。 その希望を叶えてくれる便利なライブラリ "SortableJS" について紹介します。 SortableJSとは 比較的短く、簡素なコードでドラッグ&ドロップを実現することができるライブラリです。 下記リンクにてgithubで無料公開されています。 https://github.com/SortableJS/Sortable 実際どのような機能なのか、利用例はこちら https://yizm.work/sample_code/javascript/sortablejs_howto/ 特徴 Star総数22万7千 おそらく多くの方が利用 幅広いブラウザに対応している Chrome, Edge, Safari, FireFox等、一般に普及しているブラウザに対応しており、スマホのブラウザにも対応 対応ブラウザ・バージョンはこちら 長年保守され続けている 公開されたのは2013年 今日2021年6月において、最終master更新は2021年5月9日 issueがそこそこ解決されている 今までに立てられたissueの数は1500以上 そのうち1200以上のissueがclose 以上の観点より、今後仮に新しいブラウザが出来たとしても対応される可能性が高く、利用しやすいのではないかと思います。 使い方 githubからインストールしたディレクトリを自分のプロジェクト下に置き、以下の階層にあるjsを読み込みます。 Sortable-master/Sortable.min.js htmlからに呼び出すときは以下のようになるかと思います。 js <script type="text/javascript" src="https://yoursite.com/Sortable-master/Sortable.min.js"></script> 実装 以下より引用 https://yizm.work/sample_code/javascript/sortablejs_howto/ 普通の並べ替え html <ul id="sample1" class="samplelist"> <li>これで、並べ替えができる</li> <li>様になっている</li> <li>はずなんだ。</li> </ul> js var sample1 = document.getElementById('sample1'); var sortable1 = Sortable.create(sample1); アニメーション付き js var sample2 = document.getElementById('sample2'); var sortable2 = Sortable.create(sample2, {animation: 100}); ドラッグアンドドロップできるハンドル要素指定 html <ul id="sample3" class="samplelist"> <li><span class="srt_hndl">❤</span>ラベル選択できる?</li> <li><span class="srt_hndl">❤</span>たまーに、コピペしようとして</li> <li><span class="srt_hndl">❤</span>選択できないのってアレだよね。</li> </ul> js var sample3 = document.getElementById('sample3'); var sortable3 = Sortable.create(sample3, {animation: 100, handle:".srt_hndl"}); おわりに webサイトを構築する際、ライブラリの選択基準に「今後も保守され続けるか」という観点はとても重要ではないでしょうか。SortableJSは実装においてとても便利なライブラリであり、そこそこ保守されていますので、ドラッグ&ドロップ実装の際には是非ご検討ください。
- 投稿日:2021-06-20T14:15:32+09:00
Youtubeのもろもろをダウンロードできるyoutube-dlについて色々調べて試してみた。
前回、前々回の続きです。 卒業制作で「気がついたらYoutubeを観て1日が終わってた撲滅ツール!動画内の音声を検索!」というのを作るので「youtube-dl」というツールを調べていきます。 本記事のサマリ 「youtube-dl」のコマンドについて色々試した。 「外部ファイルに記載した複数動画」の字幕のダウンロードは成功した。 「サムネイル画像」のダウンロードに成功した。 Youtubeの動画IDにハイフンが含まれるとダウンロードできないことが判明した。 「youtube-dl」はURLがわからないと字幕のダウンロードができないと思われるので代替案を探す。 「ytsearch」は更新が6年前でうまく動かなったのであきらめる。 Google公式のYoutubeAPIでいこうと思う。 youtube-dlについて広く調べる プレイリストもそのままいけそうだ。 また、「ソフトウエア管理ツール・パッケージでインストールすると古い版が入るので手動でインストールする」という知見も得た。 プレイリストをtxtに落として1行づつ処理するためのシェルスクリプトの例。 「-i」はダウンロードエラーを無視する。 動画IDで指定できるし、スペースで区切ることで複数一括もできる。 動画IDを取得してみる。 C:\bin>youtube-dl --get-title --get-id "https://www.youtube.com/watch?v=mppA6nx_rsQ.f247" 実は島根県民はオイシイと思っている話 #weeklyuko mppA6nx_rsQ 動画IDだけを2つスペースで区切ってダウンロードできるか試す。→できた。 なお「-i」でもdll不足のポップアップエラーは出た(知ってた) C:\bin>youtube-dl --sub-lang ja --write-auto-sub --skip-download -i YiAWVLkdYIo mppA6nx_rsQ [youtube] YiAWVLkdYIo: Downloading webpage [youtube] YiAWVLkdYIo: Downloading MPD manifest [info] Writing video subtitles to: シャープのマスクとIoTの件 #weeklyuko #雑談-YiAWVLkdYIo.ja.vtt [youtube] mppA6nx_rsQ: Downloading webpage [youtube] mppA6nx_rsQ: Downloading MPD manifest [info] Writing video subtitles to: 実は島根県民はオイシイと思っている話 #weeklyuko-mppA6nx_rsQ.ja.vtt ところで最初動画IDを間違って「YiAWVLkdYIo.ja」(.jaが余計)でコマンド実行してしまったらこんなエラーが出た。「youtube-dl "ytsearch:YiAWVLkdYIo.ja"」ということでytsearchというもので検索もできそう? C:\bin>youtube-dl --sub-lang ja --write-auto-sub --skip-download YiAWVLkdYIo.ja mppA6nx_rsQ -i ERROR: 'YiAWVLkdYIo.ja' is not a valid URL. Set --default-search "ytsearch" (or run youtube-dl "ytsearch:YiAWVLkdYIo.ja" ) to search YouTube 試すとこんな感じ。(うっかり動画ダウンロードされてしまった) C:\bin>youtube-dl "ytsearch:YiAWVLkdYIo" [download] Downloading playlist: YiAWVLkdYIo [youtube:search] query "YiAWVLkdYIo": Downloading page 1 [youtube:search] playlist YiAWVLkdYIo: Downloading 1 videos [download] Downloading video 1 of 1 [youtube] YiAWVLkdYIo: Downloading webpage [youtube] YiAWVLkdYIo: Downloading MPD manifest WARNING: Requested formats are incompatible for merge and will be merged into mkv. [dashsegments] Total fragments: 49 [download] Destination: シャープのマスクとIoTの件 #weeklyuko #雑談-YiAWVLkdYIo.f247.webm [download] 75.5% of ~2.10MiB at 4.09MiB/s ETA 00:06 [download] Got server HTTP error: HTTP Error 404: Not Found. Retrying fragment 38 (attempt 1 of 10)... [download] 81.6% of ~2.11MiB at 5.83MiB/s ETA 00:05 [download] Got server HTTP error: HTTP Error 404: Not Found. Retrying fragment 41 (attempt 1 of 10)... [download] Got server HTTP error: HTTP Error 404: Not Found. Retrying fragment 41 (attempt 2 of 10)... [download] 83.7% of ~2.15MiB at 7.49MiB/s ETA 00:05 [download] Got server HTTP error: HTTP Error 404: Not Found. Retrying fragment 42 (attempt 1 of 10)... [download] 100% of 2.19MiB in 00:32 [download] Destination: シャープのマスクとIoTの件 #weeklyuko #雑談-YiAWVLkdYIo.f140.m4a [download] 100% of 3.96MiB in 00:01 [ffmpeg] Merging formats into "シャープのマスクとIoTの件 #weeklyuko #雑談-YiAWVLkdYIo.mkv" ERROR: 字幕だけダウンロードにも使えた。 C:\bin>youtube-dl "ytsearch:YiAWVLkdYIo" --sub-lang ja --write-auto-sub --skip-download [download] Downloading playlist: YiAWVLkdYIo [youtube:search] query "YiAWVLkdYIo": Downloading page 1 [youtube:search] playlist YiAWVLkdYIo: Downloading 1 videos [download] Downloading video 1 of 1 [youtube] YiAWVLkdYIo: Downloading webpage [youtube] YiAWVLkdYIo: Downloading MPD manifest [info] Writing video subtitles to: シャープのマスクとIoTの件 #weeklyuko #雑談-YiAWVLkdYIo.ja.vtt [download] Finished downloading playlist: YiAWVLkdYIo 単語検索して複数一括でダウンロードできるのかな、と試してみるがイマイチ。「protoout」だとダウンロードされず、「プロトアウト」だと1件だけダウンロードできた。最初の1つだけしか取ってこないのかな。 C:\bin>youtube-dl "ytsearch:protoout" --sub-lang ja --write-auto-sub --skip-download [download] Downloading playlist: protoout [youtube:search] query "protoout": Downloading page 1 [youtube:search] playlist protoout: Downloading 1 videos [download] Downloading video 1 of 1 [youtube] Mxd-QARETeM: Downloading webpage [youtube] Mxd-QARETeM: Downloading MPD manifest [download] Finished downloading playlist: protoout C:\bin>youtube-dl "ytsearch:"プロトアウト"" --sub-lang ja --write-auto-sub --skip-download [download] Downloading playlist: プロトアウト [youtube:search] query "プロトアウト": Downloading page 1 [youtube:search] playlist プロトアウト: Downloading 1 videos [download] Downloading video 1 of 1 [youtube] 2ffGMzsvhoA: Downloading webpage [youtube] 2ffGMzsvhoA: Downloading MPD manifest [info] Writing video subtitles to: 体験版あり!自宅で気軽にAR体験: 動画再生WEBサイト『モニエアリング』 (プロトアウトスタジオ2期生 周防さん)-2ffGMzsvhoA.ja.vtt [download] Finished downloading playlist: プロトアウト ところで偶然落とせたこの字幕を前回記事のスクリプトでテキスト化してみると前回試しに字幕をダウンロードした動画より日本語で読める感じだった。 送っ はいでは始めさせていただきます てくお願いしまーすお願いしまーすはいえー私数をと申しますよろしくお願いします 私は ar が好きで趣味でプログラムを書いています後アプリを使って作って イベントで紹介したりしています 今回は家作で気軽に ar 体験ができる もニーアリングといったものを作ったのでそれを紹介させていただきます も兄あーリングというものはいわゆる動画を再生する動画再生サイトになります ですがただ単純に動画を再生するのではなく穴から覗くように動画を見るという体験を 提供します 穴から覗く園動画を見ることで何かを観察しているような感覚を提供します これが実際の画面になります スマホからは壁に穴が開いていて a 壁の向こう側が見えている ように見えますが実際には穴は開いておらず 見えているのは動画の再生画面になります こちら体験版がありまして右側の qr コードから まあスマホで読み込んでいただくと同じように動作を確認することができます こちらどういう仕組みかというと web サーバーを用意しましてその web サーバーに ar の機能と 動画を再生する機能この2つの機能 を持たせておきます それでまあスマホで ar のマーカーを認識させるとそれら二つの機能をうまいこと 使ってですね 穴があいたようなギミックを表現しています こちら2012年にアイデアを思いついて 2017年にやっと施策が出来ました そこから開発を進めていて例えばドアに穴を開けられないかなあみたいなことをやって みたいしていました あと やっぱり穴なのでへこむような表現が必要だなと思って ヘコに ar ってをやるんだろうといったところ開発を進めていました あとはまあより高簡単に実装できないかなあ見たいところをやっていました ここまでくるとまあ何かしら商品にしたいなぁと思って 一番初め考えたのが体に穴が開く服ってどうだろうみたいなことをやってみました こちら 1着あたり90万円 にかかるとまぁ原価だけで90万円かかるということがわかって まああえなくやめました こんな感じで開発と a 失敗を繰り返してまぁ今回のもにエアリングといったものが完成に至りました このもニーアリングなんですけどビジネスとしても使えないかなといったところを模索 しています 例えば印刷物ポスターとかチラシとかそう いったものにこの もにゃリングを追加するとまぁあの 穴が開くえーい ポスターとかチラシとかになるとでその穴から見えるのが正 紹介したい商品の情報とで穴から見るのでやっぱりちゃんとは見えないはチラ見せの 効果が起こって まあそれによって商品への関心度を上げられないかとかあでもすごく商品が関心度が 上がった状態で は本当に誘導 したり集客サイトに誘導するとかあるいはこの another 区 ポスターとかっていうのはあまり見ないのでその物珍しさでは sns などで拡散が 起こったりとか期待できないかなと考えております 今回8今回このもにありんぐですね キャンプファイアーでプロジェクトを公開させていただきました 先ほどのあの印刷物の連携といったところもですねえリ ターンとして用意させて いただきましたのでお今日もパターですね 一度検討頂ければと思います はい以上になりますありがとうございます ありがとうございますございます コメントねー 子供心をくすぐりそうみたいな ポイントは一つ目 家ってどこで読みたいな感覚でいっぱい遊びそうとか そうですねぇ なんかどういった感じの活用になるかと いうのはクラウドファンディングを通して もっとユースケースを探していきたいなみたいな感覚ですね はいそうですねうんこんな感じですん じゃあの こ -i, --ignore-errors ダウンロードエラーを無視する。プレイリストごとダウンロードするような時に使う。 --abort-on-error ダウンロードエラーが発生したら以降の処理を中止する。 --socket-timeout SECONDS タイムアウトの秒数を指定。単位は秒。 --playlist-start NUMBER --playlist-end NUMBER 例えば2を指定するとプレイリスト内の2番目の動画から処理を開始もしくは処理を終える。 --playlist-items ITEM_SPEC --playlist-items 1-3,7,10-13のように処理するプレイリスト内の番号を指定する。 --match-title REGEX --reject-title REGEX 正規表現にマッチした物をダウンロードするかもしくは除外してダウンロードする。 --max-downloads NUMBER 最大ダウンロード数を指定する。 --date DATE 動画のアップロード日時を指定する。 --datebefore DATE --dateafter DATE 指定した日時より以前もしくは以後の物をそれぞれ処理する。 --min-views COUNT --max-views COUNT 最大最小視聴回数をそれぞれ指定する。 --match-filter FILTER ダウンロードする動画をフィルタする。「like_count > 100 & dislike_count < 50 & description」などと使う。 --age-limit YEARS なんのこっちゃと思ったら年齢制限の事。 --download-archive FILE このオプションで指定したファイルにダウンロードした動画のIDを自動で記録する。そして記録された動画は次回実行時にダウンロードをスキップする。 -R, --retries RETRIES リトライ数を指定する。infiniteという値も一応用意されているがこんな値使うな。 --playlist-reverse プレイリスト上の動画を最後からダウンロードする。 --playlist-random プレイリスト上の動画をランダムにダウンロードする。 --external-downloader COMMAND 外部ダウンローダーを指定する。対応してるのはaria2c、avconv、axel、curl、ffmpeg、httpie、wget。 --external-downloader-args ARGS 外部ダウンローダーに引数を渡す。 -a, --batch-file FILE 動画URLを記述したファイルを指定してまとめてダウンロードする。 --id ファイル名を動画IDのみとする。後述の-oと併用するとconflictで停止する。 -o, --output TEMPLATE ファイル名を指定する。使用できる変数は後述のOUTPUT TEMPLATEを参照。 --write-thumbnail サムネイルを作成する。 --write-all-thumbnails サムネイルを全てのフォーマットで作成する。 --list-thumbnails 利用可能なサムネイルを表示する。 --no-warnings 警告を無視する。 -s, --simulate 実行をシミュレートするだけで一切のダウンロードを行わない。 --skip-download 動画のダウンロードを行わない。 -g, --get-url -e, --get-title --get-id --get-thumbnail --get-description --get-duration --get-filename --get-format 指定の物を表示するだけで実行はしない。 -j, --dump-json 実行をシミュレートするだけだが諸情報をJSON形式で表示する。 -J, --dump-single-json 実行をシミュレートするだけだが与えられた引数ごとに諸情報をJSON形式で表示する。 --print-json --quietで実行かつ動画情報をJSONで表示。 -v, --verbose デバッグ用の諸情報を表示する。 --dump-pages デバッグ用にリクエスト結果をbase64でエンコードして表示する。でも長過ぎてデバッグにならない。 --write-pages デバッグ用に中間処理を表示する。 --write-sub 字幕ファイルを生成する。 --write-auto-sub youtubeで字幕ファイルを自動で生成する。 --all-subs 使用可能な全ての字幕をダウンロードする。 --list-subs 使用可能な全ての字幕を表示する。 --sub-format FORMAT 字幕のフォーマットを指定する。 --sub-lang LANGS 字幕の言語を指定する。 -u, --username USERNAME ログイン時のユーザー名を指定する。 -p, --password PASSWORD ログイン時のパスワードを指定する。 --embed-subs 動画ファイルに字幕を埋め込む。 --embed-thumbnail 音声ファイルにサムネイルを埋め込む。 --exec CMD ダウンロード後コマンドを実行する。 --convert-subs FORMAT 字幕フォーマットを変換する。サポートしてるのはsrt、ass、vtt、lrc。 OUTPUT TEMPLATE 断っておくが以下の物が全て使える訳ではない。特定のサイトに特化したような物も多い。 id 動画ID title 動画タイトル url 動画URL release_date YYYYMMDDで表される動画のrelease日時 uploader_id 動画upload者のIDもしくはニックネーム duration 動画の長さ view_count 動画の閲覧数 like_count 動画のポジティブな評価数 dislike_count 動画のネガティブな評価数 average_rating 動画の平均評価数 comment_count 動画のコメント数 age_limit 動画の制限年齢 chapter チャプターのタイトル chapter_number チャプターの数 chapter_id チャプターのID 例えばこんな感じで使う。 -o %(title)s-%(id)s.%(ext)s Windowsのbatch fileで使う場合は -o "%%(title)s-%%(id)s.%%(ext)s" と%をエスケープする。 コメント欄で指摘があったがterminalでは -o '%(title)s.%(ext)s' としないとむしろ動かないらしい。terminalだからLinuxかね。ようはpath部分を’で囲めって事だね。 FAQ using output template conflicts with using title, video ID or auto numberというエラーが出る。 --idと-oを同時に指定するなど矛盾がある。 402エラーが返ってくるけどこれは何。 YouTubeへのアクセス過多。ブラウザでCAPTCHAをクリアする事。 他に何かプログラムはいるのか。 単独で動きはするがffmpegもしくはavconvはあった方が良い。rtmpdumpもあればRTMPプロトコルを扱えるようになるが正直出番はあまりない。 URLに&が含まれているとエラーが返る。 &は特殊文字扱いなのでURLを""で囲む事。 429または402エラーが返る。 アクセス過多でIPがブロックされている可能性が高い。サービス管理者に問い合わせるか--proxyや--source-address等を使う事。 HLSは--hls-prefer-nativeと--hls-prefer-ffmpegのどちらがいいのか。 youtube-dlは最適な方を自動で選ぶようになっているそうな。しかしnativeじゃないと使えないoptionもある。 -o でファイル名を指定。この下の動画をDLする場合は、このようなコマンドになる。 youtube-dl -f bestvideo+251/bestvideo+bestaudio/best --merge-output-format mkv https://www.youtube.com/watch?v=Kb_H5zsVtw0 -o "%%(upload_date)s_%%(title)s_%%(id)s" 上記コマンドでファイル名を指定するとこうなる。 20180424_MEGALOVANIA_Kb_H5zsVtw0.mkv –download-archive [.txtのパス] ダウンロードしたファイル(動画ID)をテキストファイルに保存する。これによってダウンロード済みの動画をスキップしてくれる。 -o –output を -o と省略可能 ファイル名の指定が可能。詳しくは公式リファレンスや詳しく紹介しているサイトを参照。 “%%(upload_date)s_%%(title)s_%%(id)s” と指定して必ずアップロード日を入れるようにしている。こうすることでファイル名でソートしてもちゃんと並ぶ。 念の為動画IDも入れて同じ動画名やライブ放送名でも上書きされることがないようにした。 最大ダウンロード数を変更してみる。 お。最大ダウンロード数オプションがあったので「C:\bin>youtube-dl "ytsearch:"プロトアウト"" --sub-lang ja --write-auto-sub --skip-download --max-downloads 10」なら10本の動画を落としてくるのかな、と思ったけど1本だけしか落としてこなかった。違うっぽい。 サムネイルをダウンロードしてみる。 C:\bin>youtube-dl "ytsearch:"プロトアウト"" --sub-lang ja --write-auto-sub --skip-download --write-thumbnail [download] Downloading playlist: プロトアウト [youtube:search] query "プロトアウト": Downloading page 1 [youtube:search] playlist プロトアウト: Downloading 1 videos [download] Downloading video 1 of 1 [youtube] Mxd-QARETeM: Downloading webpage [youtube] Mxd-QARETeM: Downloading MPD manifest [youtube] Mxd-QARETeM: Downloading thumbnail ... [youtube] Mxd-QARETeM: Writing thumbnail to: 画像分析AIを使ったLINE Botを1時間で作ってみよう(顔認証編) ~プロトアウト体験会-Mxd-QARETeM.jpg [download] Finished downloading playlist: プロトアウト ダウンロードできたけど「--write-auto-sub」が無視されて字幕ファイルはダウンロードできなかったので、どちらかしか使えないっぽい。 サムネイルは検索結果の表示に使いたいだけなので、おそらくサムネイルURLさえわかればダウンロードまではいらないかな。 外部ファイルに動画URLを記載してダウンロードしてみる。 動画IDだけの.txtを準備して引数で渡してみる。 C:\bin>youtube-dl --sub-lang ja --write-auto-sub --skip-download --batch-file urls.txt [youtube] Mxd-QARETeM: Downloading webpage [youtube] Mxd-QARETeM: Downloading MPD manifest [youtube] YiAWVLkdYIo: Downloading webpage [youtube] YiAWVLkdYIo: Downloading MPD manifest [info] Writing video subtitles to: シャープのマスクとIoTの件 #weeklyuko #雑談-YiAWVLkdYIo.ja.vtt [youtube] mppA6nx_rsQ: Downloading webpage [youtube] mppA6nx_rsQ: Downloading MPD manifest [info] Writing video subtitles to: 実は島根県民はオイシイと思っている話 #weeklyuko-mppA6nx_rsQ.ja.vtt 1行目の動画字幕がダウンロードされなかった。 1行目は無視なのかな、とタイトル行として「url」と書いてみたが怒られた。 C:\bin>youtube-dl --sub-lang ja --write-auto-sub --skip-download --batch-file urls.txt ERROR: You've asked youtube-dl to download the URL "url". That doesn't make any sense. Simply remove the parameter in your command or configuration. Add -v to the command line to see what arguments and configuration youtube-dl got. urls.txtの動画の順番を入れ替えてみる。1行目、というわけではなく特定の動画がダウンロードできていない。たしかにログもそんな感じで「[info] Writing video subtitles to:」の直前までは行けてる感じがする。 C:\bin>youtube-dl --sub-lang ja --write-auto-sub --skip-download --batch-file urls.txt [youtube] YiAWVLkdYIo: Downloading webpage [youtube] YiAWVLkdYIo: Downloading MPD manifest [info] Writing video subtitles to: シャープのマスクとIoTの件 #weeklyuko #雑談-YiAWVLkdYIo.ja.vtt [youtube] mppA6nx_rsQ: Downloading webpage [youtube] mppA6nx_rsQ: Downloading MPD manifest [info] Writing video subtitles to: 実は島根県民はオイシイと思っている話 #weeklyuko-mppA6nx_rsQ.ja.vtt [youtube] Mxd-QARETeM: Downloading webpage [youtube] Mxd-QARETeM: Downloading MPD manifest さきほど「youtube-dl "ytsearch:"プロトアウト""」でダウンロードできた動画なのでそもそもがダウンロードできないということはあるまい。となると記述方法があやしい。動画IDを見ると「Mxd-QARETeM」。ハイフンがあやしい。ダブルクォーテーションで囲ってみる。 C:\bin>youtube-dl --sub-lang ja --write-auto-sub --skip-download --batch-file urls.txt ERROR: '"YiAWVLkdYIo"' is not a valid URL. Set --default-search "ytsearch" (or run youtube-dl "ytsearch:"YiAWVLkdYIo"" ) to search YouTube ありゃ。「'」かな。 C:\bin>youtube-dl --sub-lang ja --write-auto-sub --skip-download --batch-file urls.txt ERROR: "'YiAWVLkdYIo'" is not a valid URL. Set --default-search "ytsearch" (or run youtube-dl "ytsearch:'YiAWVLkdYIo'" ) to search YouTube ぬーん。エスケープ文字はあんまり使いたくないなぁ。 まずはURLフルパスも試してみよう。 C:\bin>youtube-dl --sub-lang ja --write-auto-sub --skip-download --batch-file urls.txt [youtube] YiAWVLkdYIo: Downloading webpage [youtube] YiAWVLkdYIo: Downloading MPD manifest [info] Writing video subtitles to: シャープのマスクとIoTの件 #weeklyuko #雑談-YiAWVLkdYIo.ja.vtt [youtube] mppA6nx_rsQ: Downloading webpage [youtube] mppA6nx_rsQ: Downloading MPD manifest [info] Writing video subtitles to: 実は島根県民はオイシイと思っている話 #weeklyuko-mppA6nx_rsQ.ja.vtt [youtube] Mxd-QARETeM: Downloading webpage [youtube] Mxd-QARETeM: Downloading MPD manifest エスケープ文字試します… C:\bin>youtube-dl --sub-lang ja --write-auto-sub --skip-download --batch-file urls.txt [youtube] YiAWVLkdYIo: Downloading webpage [youtube] YiAWVLkdYIo: Downloading MPD manifest [info] Writing video subtitles to: シャープのマスクとIoTの件 #weeklyuko #雑談-YiAWVLkdYIo.ja.vtt [youtube] mppA6nx_rsQ: Downloading webpage [youtube] mppA6nx_rsQ: Downloading MPD manifest [info] Writing video subtitles to: 実は島根県民はオイシイと思っている話 #weeklyuko-mppA6nx_rsQ.ja.vtt [generic] watch?v=Mxd%-QARETeM: Requesting header WARNING: Falling back on generic information extractor. [generic] watch?v=Mxd%-QARETeM: Downloading webpage [generic] watch?v=Mxd%-QARETeM: Extracting information ERROR: Unsupported URL: https://www.youtube.com/watch?v=Mxd%-QARETeM あれ、URLが違うって言われた。 .txtを使わずに直でやってみよう。 C:\bin>youtube-dl --sub-lang ja --write-auto-sub --skip-download "https://www.youtube.com/watch?v=Mxd-QARETeM" [youtube] Mxd-QARETeM: Downloading webpage [youtube] Mxd-QARETeM: Downloading MPD manifest C:\bin>youtube-dl --sub-lang ja --write-auto-sub --skip-download "https://www.youtube.com/watch?v=YiAWVLkdYIo" [youtube] YiAWVLkdYIo: Downloading webpage [youtube] YiAWVLkdYIo: Downloading MPD manifest [info] Writing video subtitles to: シャープのマスクとIoTの件 #weeklyuko #雑談-YiAWVLkdYIo.ja.vtt うげ。だめか。試しにハイフンのないURLを試すと問題ないのでやはりハイフンがある動画IDがだめっぽい。 先のサイトのコメント欄に以下のことが書いてあった。 outputオプションについて、Windowsのバッチでは、 -o "%%(title)s-%%(id)s.%%(ext)s" で二重引用符と%2つが必要みたいです。ずっと%一つでなんで動かないのかわからなくて困ってたら公式ページに書いてましたw なん、だと。 C:\bin>youtube-dl --sub-lang ja --write-auto-sub --skip-download "https://www.youtube.com/watch?v=Mxd%%-QARETeM" [generic] watch?v=Mxd%%-QARETeM: Requesting header WARNING: Falling back on generic information extractor. [generic] watch?v=Mxd%%-QARETeM: Downloading webpage [generic] watch?v=Mxd%%-QARETeM: Extracting information ERROR: Unsupported URL: https://www.youtube.com/watch?v=Mxd%%-QARETeM だめかー(´・ω・`) ちがうだろうなーと思いつつ「\」のエスケープ文字も試してみましたがダメ。動作見てるとdll不足エラーが出るので、Youtubeにアクセスはしていて、Windowsにファイルとして生成するときに失敗してる? ワンチャンWindowsだけの事象でLinuxだったら問題なかったりしないかしら。 そもそもyoutube-dl単品で単語検索でいけるのか調べる ちょっと脇道に逸れすぎましたので軌道修正。 日時や視聴回数、いいね回数でフィルタできそう?(アウトプットファイル名に使えるだけ?) てことは検索単語っぽいこともいけそうなんだけどな。そしてそれが使えるなら使うツールを少なくできるはず。 YouTube Data APIに投げた後にyoutube-dlでダウンロードしてるってことはyoutube-dl単品では無理ってことなのかな。 参考になりそうな記事。Pythonで書かれてますが、そもそもyoutube-dlがPython必要なのでがんばって参考にするか? まずは、キーワードから上位検索結果の情報をdict型として取得します。 少々ややこしいdict型のデータですがURLの一部を取り出すことができます。URLが直接受け取れるわけではなくIDとして受け取れるので、共通のURLとつなげてやればよいわけです。 検索結果にチャンネルが出てくる場合があるのでKeyErrorを用いることで回避しました。 APIを使いすぎると、制限がかかるのでほどほどにしといた方がよさそうです。 特定の動画をDLしたい時 youtube-dl -f bestvideo+251/bestvideo+bestaudio/best --merge-output-format mkv [動画URL] -o "%%(upload_date)s_%%(title)s_%%(id)s" 特定のチャンネルの動画を一括でDLしたい時 youtube-dl -f bestvideo+251/bestvideo+bestaudio/best --merge-output-format mkv [チャンネルURL] -o "%%(upload_date)s_%%(title)s_%%(id)s" 特定のチャンネルの動画の最新5件のみDLしたい時 youtube-dl -f bestvideo+251/bestvideo+bestaudio/best --merge-output-format mkv [チャンネルURL] --playlist-end 5 -o "%%(upload_date)s_%%(title)s_%%(id)s" 特定のチャンネルの動画の最新10件をスキップしてDLしたい時 youtube-dl -f bestvideo+251/bestvideo+bestaudio/best --merge-output-format mkv [チャンネルURL] --playlist-start 10 -o "%%(upload_date)s_%%(title)s_%%(id)s" チャンネル一括はできそう。 そういえば「ytsearch」というものもあるんでした。こちらも調べてみましょう。 Search YouTube from the command-line without an API key. It can fetch URLs and various meta content in whatever output format you'd like. Out of the box it pairs well with ytdl. APIキーを使わずに、コマンドラインからYouTubeを検索できます。URLや様々なメタコンテンツを取得し、お好みの出力形式で表示することができます。初期設定では、ytdlとの組み合わせが便利です。 インストールしてみましたがサンプルコマンドすらうまくいかない。うーん、Nodistのせいかなぁ。それか警告にもありますが6年前で更新止まってるので色々組み合わせが悪い? これはちょっと使いづらいのでおとなしくYoutube APIを使いましょう。ちょっと検索数によるAPIロックが気になるけど…。 PS C:\Users\ShinsukeSutou\Desktop\vtt-to-text> npm install -g ytsearch npm WARN deprecated timelabel@1.2.0: Development of this module has been stopped. C:\Program Files (x86)\Nodist\bin\ytsearch -> C:\Program Files (x86)\Nodist\bin\node_modules\ytsearch\ytsearch.js + ytsearch@1.0.4 updated 1 package in 0.674s PS C:\Users\ShinsukeSutou\Desktop\vtt-to-text> ytsearch "the beatles" -p -l 5 Error: Unquoted attribute value Line: 1 Column: 12 Char: e at error (C:\Program Files (x86)\Nodist\bin\node_modules\ytsearch\node_modules\sax\lib\sax.js:642:8) at strictFail (C:\Program Files (x86)\Nodist\bin\node_modules\ytsearch\node_modules\sax\lib\sax.js:662:22) at SAXParser.write (C:\Program Files (x86)\Nodist\bin\node_modules\ytsearch\node_modules\sax\lib\sax.js:1240:11) at Parser.exports.Parser.Parser.parseString (C:\Program Files (x86)\Nodist\bin\node_modules\ytsearch\node_modules\xml2js\lib\xml2js.js:273:29) at Parser.parseString (C:\Program Files (x86)\Nodist\bin\node_modules\ytsearch\node_modules\xml2js\lib\xml2js.js:6:61) at exports.parseString (C:\Program Files (x86)\Nodist\bin\node_modules\ytsearch\node_modules\xml2js\lib\xml2js.js:296:19) at IncomingMessage.<anonymous> (C:\Program Files (x86)\Nodist\bin\node_modules\ytsearch\node_modules\youtube-search\index.js:39:7) at IncomingMessage.emit (events.js:326:22) at endReadableNT (_stream_readable.js:1241:12) at processTicksAndRejections (internal/process/task_queues.js:84:21) PS C:\Users\ShinsukeSutou\Desktop\vtt-to-text> 次回へのTODO 画面フロントを考える。 サーバー側で動かす実装方法を調べる。 Linuxサーバーに乗せる? そこまでするよりユーザーのクライアント動作としてしまう? 自動字幕ではなく、ちゃんと字幕がある場合はそちらをダウンロードするような仕組みを考える。 公式字幕つきの動画をみつけてコマンドが正しく動くか確認する。 Youtube側の仕様変更の際のバージョンアップマニュアルを作成する。 ハイフンがある動画IDの字幕がダウンロードできない点の対応。 ワンチャンLinuxだったら問題ない可能性あり?
- 投稿日:2021-06-20T13:38:02+09:00
vue環境構築(npm,yarnインストールからサーバー起動まで)
npm, yarnインストール Mac OSの場合 以下に従ってhomebrewを入れる。 https://brew.sh/index_ja.html 以下でインストールを確認 $ brew -v # => 1.1.0 # Homebrew/homebrew-core (git revision bb24; last commit 2016-11-15) nodebrewを入れる。 $ brew install nodebrew # ... $ nodebrew -v # => 0.9.6# ...(いろいろ表示される) 安定版のnodeをインストールする。 $ nodebrew install-binary stable PATHを通す $ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bash_profile # bashの場合 # $ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.zprofile # zshの場合 # *** ターミナル再起動 *** nodeのバージョン確認 $ node -v yarnも入れる $ npm install --global yarn # ...(いろいろ表示される) $ yarn --version Windows の場合 以下からnodeのインストーラ(LTS版)を取得して、インストールする。 https://nodejs.org/ja/ インストール確認 PS> node --version ::: v14.17.1 yarnも入れる。 PS> npm install --global yarn ::: ... PS> yarn --version ::: 1.22.10 WSLの場合 sudo apt install -y nodejs npm sudo npm install -g n sudo n lts # --- terminal reboot --- # sudo apt purge -y nodejs npm sudo apt autoremove -y node -v # -> v14.17.0 sudo apt install -y curlcurl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list sudo apt update && sudo apt install yarn yarn --version # -> 1.22.5 vue/cliインストール $ npm install -g @vue/cli # $ sudo npm install -g @vue/cli # Linux, WSLの場合 スクリプト実行権限の設定(PowerShellのみ) この項目は、自己責任で実施するか、PowerShell以外を使ってください。 PowerShellでvueコマンドを使う場合は、実行権限の変更が必要。 管理者モードでPowerShellを起動する。以下で実行権限を確認し、 PS> PowerShell Get-ExecutionPolicy Restricted 以下で変更する。 PS> PowerShell Set-ExecutionPolicy RemoteSigned PS> PowerShell Get-ExecutionPolicy RemoteSigned vue/cliでプロジェクト作成 $ vue create my-project 設定は以下の通り。 Vue CLI v4.5.13? Please pick a preset: Manually select features ? Check the features needed for your project: Choose Vue version, Babel, TS, Router, Vuex, CSS Pre-processors, Linter, Unit, E2E ? Choose a version of Vue.js that you want to start the project with 3.x ? Use class-style component syntax? No ? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes ? Use history mode for router? (Requires proper server setup for index fallback in production) Yes ? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with dart-sass) ? Pick a linter / formatter config: Standard ? Pick additional lint features: Lint on save, Lint and fix on commit ? Pick a unit testing solution: Jes t? Pick an E2E testing solution: Cypress ? Where do you prefer placing config for Babel, ESLint, etc.?: In dedicated config files ? Pick the package manager to use when installing dependencies: (Use arrow keys): Use Yarn 以下一つずつ説明。 まず、マニュアルにする。 ? Please pick a preset: Manually select features 次で設定する項目を選ぶ。PWA以外は有効とした(PWAはスマホ対応なので要らないと考えた)。 ? Check the features needed for your project: Choose Vue version, Babel, TS, Router, Vuex, CSS Pre-processors, Linter, Unit, E2E vueのversionは新しい方を選択。 ? Choose a version of Vue.js that you want to start the project with 3.x # vueは3.xを使用 Noとする。vue3では、class-styleではなくcomposition APIという方がスタンダードらしい。 React Hooksのうな型推論の恩恵を受けれるというのが自分的には好印象だったので。 https://qiita.com/jesus_isao/items/0b305c7d90c45ad66c1b ? Use class-style component syntax? No ここはデフォルトのYes。 ? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes サーバー側で対応が必要だが、SPAで操作履歴を見るためには、historyがあった方が都合よさそうなのでYes。 ? Use history mode for router? (Requires proper server setup for index fallback in production) Yes 生CSSは辛そうなので、新しいdart-SCSSを使ってみる。 ? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with dart-sass) Linterの設定。airbnbは結構厳しいみたいなので、Standardにした。 Prettierは後でインストールする。 忘れないよう保存時に強制適用にする。 ? Pick a linter / formatter config: Standard? Pick additional lint features: Lint on save, Lint and fix on commit テストフレームワークは標準になってきているJestを設定 ? Pick a unit testing solution: Jest E2EテストはCypressが良いらしい。 ? Pick an E2E testing solution: Cypress 各設定ファイルは別ファイルに合った方がいいかな。 ? Where do you prefer placing config for Babel, ESLint, etc.?: In dedicated config files yarn, npmどちらを使うか聞かれたらyarnを選択。 ? Pick the package manager to use when installing dependencies: (Use arrow keys): Use Yarn サーバー起動 $ cd my-project$ yarn serve
- 投稿日:2021-06-20T11:14:53+09:00
関数コンポーネントで学ぶReact公式チュートリアル
関数コンポーネント版React公式チュートリアル Reactの公式チュートリアルではクラスコンポーネントが使われていますが、最近は関数コンポーネントを利用することがほとんどだと思います。折角初心者の方に教えるのであれば、使えるものを教えたいと思ったので、公式チュートリアルを一通り関数コンポーネントを使った形に書き換えてみました。 以下にはコードと関数コンポーネントで書く際の注意点のみ記述します。 コードの詳しい解説については、公式の方を確認してください。 スターターコード サンプルコードに関してはCodeSandboxに用意しました。以降で操作するファイルは、src/index.jsになります。 CodeSandbox 描画したいJSXを、クラスコンポーネントではrenderメソッドに記述しましたが、関数コンポーネントでは返り値とするのでreturn文のなかに記述します。 また、以下のコードでは関数の定義は、基本的にアロー関数を変数に代入する形でおこないます。 const コンポーネント名=(prop)=>{ /* 処理 */ return <Component value={props.value}/> } この場合、定義順などに気をつける必要がありますが、同名コンポーネントの定義を防止できるなど利点もあるので、この形を利用していきます。 JavaScriptの関数定義に興味のある方は、@raccy様の記事を参考にしてみてください。 ゲームを完成させる データをProps経由で渡す BoardのrenderSqueareを以下のように書きえましょう。 const renderSquare = (i) => <Square value={i} />; こうすることでSquareの中で、引数iの値をprops.valueとして扱うことができます。実際にSquareを書き換えてprops.valueの値を反映させてみましょう。 const Square = (props) => { return <button className="square">{props.value}</button>; }; 各マスに数字が表示されるようになったと思います。 インタラクティブなコンポーネントを作る クラスコンポーネントではコンストラクタのthis.stateでまとめてステートを管理しますが、関数コンポーネントではHook(今回は'useState'関数)と言われるものを利用して一つ一つステートを定義します。 const [state, setState]=useState(初期値); 変数名(state)は何でもいいですが、setStateの部分は必ずset+変数名(キャメルケース)にしてください)。 ステートの値が使いたい場合は、普通の変数と同じようにstateを使いましょう。 しかし、ステートの値を変更する際は直接代入するのではなく、各ステートに対応した関数を使う点には注意してください。上記のコードを用いると以下のようになります。 //Bad state=1; //これだとDOMが更新されない //そもそもconstで定義しているので代入はできませんが・・・ //Good setState(1); //ちゃんとDOMが更新されます Squareを書き換えると以下のようになります。 const Square = (props) => { const [value, setValue] = useState(""); return ( <button className="square" onClick={() => setValue("X")}> {value} </button> ); }; State のリフトアップ これについては、ステートの定義方法が違うだけでクラスコンポーネントと同じ手法を用います。以下に変更結果を示します。 完成品 const Square = (props) => { return ( <button className="square" onClick={props.onClick}> {props.value} </button> ); }; const Board = (props) => { const [squares, setSquares] = useState(Array(9).fill(null)); const handleClick = (i) => { const newSquares = squares.slice(); newSquares[i] = "X"; setSquares(newSquares); }; const renderSquare = (i) => ( <Square value={squares[i]} onClick={() => { handleClick(i); }} /> ); const status = "Next player: X"; return ( <div> <div className="status">{status}</div> <div className="board-row"> {renderSquare(0)} {renderSquare(1)} {renderSquare(2)} </div> <div className="board-row"> {renderSquare(3)} {renderSquare(4)} {renderSquare(5)} </div> <div className="board-row"> {renderSquare(6)} {renderSquare(7)} {renderSquare(8)} </div> </div> ); }; 手番の処理 一部公式とは違う手法でやっていますが、基本同じです。 (異なる点:テンプレート文字列) const Board = (props) => { const [squares, setSquares] = useState(Array(9).fill(null)); const [xIsNext, setXIsNext] = useState(true); const handleClick = (i) => { const newSquares = squares.slice(); newSquares[i] = xIsNext ? "X" : "O"; setSquares(newSquares); setXIsNext(!xIsNext); }; const renderSquare = (i) => ( <Square value={squares[i]} onClick={() => { handleClick(i); }} /> ); const status = `Next player: ${xIsNext ? "X" : "O"}`; //以下変更なし ゲーム勝者の判定 Board内で関数を定義する、引き分けを追加するなど変更点はありますが大体同じです。 const Board = (props) => { const [squares, setSquares] = useState(Array(9).fill(null)); const [xIsNext, setXIsNext] = useState(true); const handleClick = (i) => { const newSquares = squares.slice(); if (calculateWinner(newSquares) || newSquares[i]) { return; } newSquares[i] = xIsNext ? "X" : "O"; setSquares(newSquares); setXIsNext(!xIsNext); }; const renderSquare = (i) => ( <Square value={squares[i]} onClick={() => { handleClick(i); }} /> ); const calculateWinner = (squares) => { const lines = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6] ]; for (let i = 0; i < lines.length; i++) { const [a, b, c] = lines[i]; if ( squares[a] && squares[a] === squares[b] && squares[a] === squares[c] ) { return squares[a]; } } return null; }; const status = () => { const winner = calculateWinner(squares); if (winner) { return "Winner: " + winner; } else if (squares.reduce((x, y) => x && y)) { return "Draw"; } else { return `Next player: ${xIsNext ? "X" : "O"}`; } }; return ( <div> <div className="status">{status()}</div> <div className="board-row"> {renderSquare(0)} {renderSquare(1)} {renderSquare(2)} </div> <div className="board-row"> {renderSquare(3)} {renderSquare(4)} {renderSquare(5)} </div> <div className="board-row"> {renderSquare(6)} {renderSquare(7)} {renderSquare(8)} </div> </div> ); }; タイムトラベル機能の追加 State のリフトアップ、再び historyステートはオブジェクトのリストではなく、2次元リストに変更しました。 その他定義順や記法が一部異なります。 const Board = (props) => { const renderSquare = (i) => ( <Square value={props.squares[i]} onClick={() => { props.onClick(i); }} /> ); return ( <div> <div className="board-row"> {renderSquare(0)} {renderSquare(1)} {renderSquare(2)} </div> <div className="board-row"> {renderSquare(3)} {renderSquare(4)} {renderSquare(5)} </div> <div className="board-row"> {renderSquare(6)} {renderSquare(7)} {renderSquare(8)} </div> </div> ); }; const Game = (props) => { const initSquares = Array(9).fill(null); const [history, setHistory] = useState([initSquares]); const [xIsNext, setXIsNext] = useState(true); const calculateWinner = (squares) => { const lines = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6] ]; for (let i = 0; i < lines.length; i++) { const [a, b, c] = lines[i]; if ( squares[a] && squares[a] === squares[b] && squares[a] === squares[c] ) { return squares[a]; } } return null; }; const status = (squares) => { const winner = calculateWinner(squares); if (winner) { return "Winner: " + winner; } else if (squares.reduce((x, y) => x && y)) { return "Draw"; } else { return `Next player: ${xIsNext ? "X" : "O"}`; } }; const current = history.slice(-1)[0]; const winner = calculateWinner(current); const handleClick = (i) => { const squares = current.slice(); if (winner || squares[i]) return; squares[i] = xIsNext ? "X" : "O"; setHistory([...history, squares]); setXIsNext(!xIsNext); }; return ( <div className="game"> <div className="game-board"> <Board squares={current} onClick={handleClick} /> </div> <div className="game-info"> <div>{status(current)}</div> <ol>{/* TODO */}</ol> </div> </div> ); }; 過去の着手の表示&Key を選ぶ 関数の定義順や場所に注意したこと以外に変更はありません。 これがチュートリアルの最後であるため、全文載せておきます。 import React, { useState } from "react"; import ReactDOM from "react-dom"; import "./index.css"; const Square = (props) => { return ( <button className="square" onClick={props.onClick}> {props.value} </button> ); }; const Board = (props) => { const renderSquare = (i) => ( <Square value={props.squares[i]} onClick={() => { props.onClick(i); }} /> ); return ( <div> <div className="board-row"> {renderSquare(0)} {renderSquare(1)} {renderSquare(2)} </div> <div className="board-row"> {renderSquare(3)} {renderSquare(4)} {renderSquare(5)} </div> <div className="board-row"> {renderSquare(6)} {renderSquare(7)} {renderSquare(8)} </div> </div> ); }; const Game = (props) => { const initSquares = Array(9).fill(null); const [history, setHistory] = useState([initSquares]); const [xIsNext, setXIsNext] = useState(true); const [stepNumber, setStepNumber] = useState(0); const calculateWinner = (squares) => { const lines = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6] ]; for (let i = 0; i < lines.length; i++) { const [a, b, c] = lines[i]; if ( squares[a] && squares[a] === squares[b] && squares[a] === squares[c] ) { return squares[a]; } } return null; }; const status = (squares) => { const winner = calculateWinner(squares); if (winner) { return "Winner: " + winner; } else if (squares.reduce((x, y) => x && y)) { return "Draw"; } else { return `Next player: ${xIsNext ? "X" : "O"}`; } }; const current = history[stepNumber]; const winner = calculateWinner(current); const handleClick = (i) => { const slicedHistory = history.slice(0, stepNumber + 1); const squares = current.slice(); if (winner || squares[i]) return; squares[i] = xIsNext ? "X" : "O"; setHistory([...slicedHistory, squares]); setXIsNext(!xIsNext); setStepNumber(slicedHistory.length); }; const jumpTo = (step) => { setStepNumber(step); setXIsNext(step % 2 === 0); }; const moves = history.map((step, move) => { const desc = move ? "Go to move #" + move : "Go to game start"; return ( <li key={move}> <button onClick={() => jumpTo(move)}>{desc}</button> </li> ); }); return ( <div className="game"> <div className="game-board"> <Board squares={current} onClick={handleClick} /> </div> <div className="game-info"> <div>{status(current)}</div> <ol>{moves}</ol> </div> </div> ); }; // ======================================== ReactDOM.render(<Game />, document.getElementById("root")); まとめ 以上が関数コンポーネント化させたReact公式チュートリアルになります。関数内で関数を定義したり、クロージャーを用いていたりと見慣れない書き方かもあるかもしれませんが、Reactや関数型プログラミングではよく見る光景なので慣れておくといいと思います。 一部筆者の好みで改変したため、こうした方が公式に近い、より良い書き方があるという方は、コメントなどでアドバイスをいただけると幸いです。 Reactは非常に便利なライブラリですので、これを活用してどんどんアプリケーションを作っていきましょう!
- 投稿日:2021-06-20T10:12:55+09:00
Youtube字幕ファイル(.vtt形式)からテキストだけを抜き出す。
昨日の続きです。 卒業制作で「気がついたらYoutubeを観て1日が終わってた撲滅ツール!動画内の音声を検索!」というのを作ります。 Youtubeの字幕ファイルを抜き出すところまで成功しましたが、できたのはvtt形式のファイルだけでした。 使いたいのはテキスト本文だけなのでどうしよう、というのが今回の話です。 vttとttml形式の違いを調べる。 HTML5 の 要素で使えるキャプションファイルには、「WebVTT」と「TTML」の2種類があります。WebVTT 形式のほうがフォーマットとしてはシンプルで、字幕の表示タイミング (タイムコード) と文面を、時系列に記述するだけです。 どうやらvttでも問題ない、というかいっそそちらのほうが可読性ありそうな感じです。今できないことをがんばってttmlにこだわる必要もないので、vttで行きましょう。 vttからテキストだけを抽出する 以下のサイトで簡単に変換できるようです。(システムとして考えるとコード公開されてないと組み込みにくい気がしますが一旦やれるか否かの確認をします。) 簡単にできましたー。同じ文章が繰り返したりしているけど、これはYoutubeから抜いたときからそうなのでおいおい重複を削除できるかTODOに追加しておきましょう。 その発言の時刻があったほうが動画の場所をみつけやすいかな、とも思いましたが流石にvttのままはそもそもの日本語が読みにくいので、やはりtxtにしてしまいましょう。 Subtitles Converterのサンプルコードを探す npmで「Subtitles Converter」で検索してみます。 Convert and modify subtitle files with NodeJS. 字幕ファイルの変換と修正をNodeJSで行います。 There are many subtitle tools written in node, but there are none that support all popular subtitle and caption formats. subtitle-converter builds off of the work of others (most notably node-captions, node-webvtt, and subtitles-parser) in order to ultimately become the only subtitle module necessary to include in your node project. nodeで書かれた字幕ツールはたくさんありますが、一般的な字幕やキャプションのフォーマットをすべてサポートしているものはありません。 subtitle-converterは、他の人たち(特にnode-captions, node-webvtt, subtitles-parser)の仕事をベースにして、最終的にあなたのnodeプロジェクトに含める必要のある唯一の字幕モジュールになるようにしています。 Currently supported input file types: dfxp, scc, srt, ttml, vtt, ssa, ass 現在サポートされている入力ファイルタイプ: dfxp, scc, srt, ttml, vtt, ssa, ass Currently supported output file types: srt, vtt 現在サポートされている出力ファイルの種類: srt, vtt All output files are encoded with UTF-8. In the future we may support more encoding types. すべての出力ファイルは UTF-8 でエンコードされています。将来的には、より多くのエンコードタイプをサポートするかもしれません。 見つけたー! と思ったけど出力ファイルがtxtじゃない(´・ω・`) Universal Subtitle Parser ユニバーサルサブタイトルパーサー Identifies a subtitle type by its text and converts it to a JavaScript object. 字幕タイプをテキストで識別し、JavaScriptオブジェクトに変換します。 おや? アウトプット形式をtxtで調べてたけどaudacityかassでも可読性あってこれで十分だな。というか時刻もあってむしろ良い。 うーん、npmを「ass vtt」で検索してみたけどassをvttにするのは見つかるけど逆はないな。 見つけたー! assでもいいかな、と思ったけどtxtに変換できるの見つけたー! 「keywords:captions」で検索するのが正解だったか。 npm i -S vtt-to-text でインストールしてサンプルコードの中にvttの中身をコピペして実行。できた! 前述のサイトであった文章の重複もなくってる! これはうれしい誤算。 今回対応したTODO vttとttml形式の違いを調べる。→済 参考記事の"xmlでもあるttml形式が個人的には加工しやすそうだなと感じた。"が本当か調べる。→済 vttフォーマットでテキストを検索してカウントできるかやってみる。→テキスト化できたので済 次回へのTODO サーバー側で動かす実装方法を調べる。 Linuxサーバーに乗せる? そこまでするよりユーザーのクライアント動作としてしまう? 自動字幕ではなく、ちゃんと字幕がある場合はそちらをダウンロードするような仕組みを考える。 公式字幕つきの動画をみつけてコマンドが正しく動くか確認する。 Youtube側の仕様変更の際のバージョンアップマニュアルを作成する。
- 投稿日:2021-06-20T09:54:04+09:00
jQueryを使った企業の宣伝サンプルWebページ
jQueryを使った企業の宣伝サンプルWebページを作成いたしました。 <!DOCTYPE html> <!-- 作成者 乃木坂好きのITエンジニア --> <!-- 作成日 2021/6/20 --> <html lang="ja"> <head> <meta charset="utf-8"> <title>アニメーション2</title> </head> <script src="jquery.js"></script> <script> $(function(){ $('h1').click(function(){ $('h1').css('color','blue'); }); }); $(function(){ $('h2').click(function(){ $('h2').css('background-color','yellow'); }); }); $(function(){ $('h3').click(function(){ $('h3').css('color','black'); $('h3').css('background-color','aqua'); }); }); $(function(){ $('h5').click(function(){ $('h5').css('color','black'); $('h5').css('background-color','lightgreen'); }); }); $(function(){ for(var num=0;num<10;num++){ $('h6').fadeOut(); $('h6').fadeIn(); } }); function fuori(){ let i = 0; while (i< 1){ window.open('http://www.fuoriclasse2.com','flora','width=480,height=360'); i++; } } </script> <style> .euro2020 { color:red; font-size:80px; text-align:center; padding:120px; } .fra { color:black; font-size:70px; text-align: center; padding:100px; } .esp { color:lawngreen; font-size:50px; text-align: center; color:darkred; padding: 100px; } .por { background-color:lightgreen; text-align: center; color:darkred; font-size:50px; padding: 100px; } .freestyle1 { background-color:floralwhite; text-align: center; color:mediumvioletred; font-size:50px; padding: 100px; } .fuori { background-color:lightgreen; text-align: center; border: 1px solid #b7a077; border-radius: 5px; color:deeppink; font-size:40px; padding: 50px; float:right; } #aono1 { font-size:50px; color:red; background-color:aquamarine; text-align: center; padding:200px; } .aono2 { font-size:10px; color:blue; } </style> <body> <h1 class="euro2020">EURO2020開催中</h1> <h2 class="fra">フランスがやらかした</h2> <h3 class="esp" >スペインもやらかした</h3> <h4 class="por">ポルトガル・・・・</h4> <h5 id="aono1" onLoad="tm()"><img src="aono.jpg" alt="ダークヒーロー青野" width="800",height="600" ><br><br>フリースタイル社長の<span>青野豪淑</span>です</h5> <h6 class="freestyle1">フリースタイルはサッカーゲームを作ります。<br>fuori-classeの皆さん期待してください</h6> <br> <a class="fuori" onclick=fuori()>fuori-classe</a> <br> <br> <br> <br> <br> <br> <br> </body> <footer> <small class="aono2">© 青野豪淑</small> </footer> jQueryの機能を使うとタグの文字を変えたり、タグ部分の背景色を変えたり、for文で文字の表示、非表示が出来ます。動的にWebページが出来て便利です。
- 投稿日:2021-06-20T09:16:32+09:00
SAP Cloud SDK for JavaScriptを使ってみる (1)概要
はじめに SAP Cloud SDKとは、SAP BTP上で動くアプリケーションを開発するためのライブラリやツールのセットです。私はこれまであまり使う機会がなかったのですが、CAPとの使い分けなども気になったので調べてみました。 以下の予定で、SAP Cloud SDKを使ってみてわかったことをまとめたいと思います。 概要(この記事) サービスを作成する テストを書いてみる CI/CDの設定をする SAP Cloud SDKにはJava版とJavaScript版があります。Java版とJavaScript版ではJava版の方が機能が多く、新しい機能も先に実装される傾向にあります。 参考:Feature Matrix 私にJavaのスキルがないため、この記事ではJavaScript版を対象とします。 SAP Cloud SDKの特徴 SAP Cloud SDKで提供されるものは、大きく3つに分けられます(以下の図を参照)。土台となるのはBTPへの接続を提供するConnectivityの部分です。Connectivityは、BTP上に登録されたDestinationの取得や、認証などを(開発者が意識しなくてもいいように)裏で行ってくれます。Connectivitiyを利用することで可能になるのがODataサービスやOpen APIへの接続です。SAP Cloud SDKでは、事前定義されたクライアントライブラリ、またはジェネレーターを使用して生成したライブラリを使用して簡単にODataサービスやREST APIにアクセスすることができます。 OData ClientやOpen API Clientを使用する利点 ODataサービスやREST APIへは、SAP Cloud SDKのライブラリを使わず直接アクセスすることもできます。このときの問題点は、エンティティやプロパティを正確に指定しないと実行時にエラーになってしまうことです。 SAP Cloud SDKはODataサービスやREST APIを利用するためのTypeScriptで書かれたライブラリを提供します。このライブラリを使用してODataサービスやREST APIにアクセスすることで、コーディング時に型のチェックが可能になります。また、TypeScriptを使用することによりオートコンプリートやスペルミスのチェックも可能になります。 SAP Cloud SDKの用途 SAP Cloud SDKはBTPへの接続や、ODataサービスやREST APIへの接続を簡単にするというのが利点です。一方、SAP Cloud SDKで開発したサービス自体もHTTPリクエストを受け、結果を返すというAPIの働きをします。 このことから、以下の用途が考えられます。 ①バックエンドへのプロキシとなるサービスの開発 SAP Cloud SDKで開発したサービスで、バックエンドへのアクセスを中継します。 元のAPIよりも項目を絞って簡素にしたり、バリデーションや代入など独自のロジックを追加したりすることができます。また、複数のサービスへのアクセスを一か所にまとめることで、サービスを使用するアプリ側からどのバックエンドに接続しているかを意識しなくて済みます。 フロントエンドをUI5で実装する場合、SAP Cloud SDK作成されるサービスはODataではなく($metadataを持たない)REST APIになるため、以下のような点に留意が必要です。 Fiori elementsやSmart Controlが使えない コントロールにデータを直接バインドすることができない(データを取得してからJSONオブジェクトをバインドする必要がある) また、フロントエンドアプリを置かずにSAP Cloud SDK作ったサービス自体直接呼んで使うこともできます(webhookとして使用したり、workflowやintegration flowの中から呼ぶなど)。 ②CAPのイベントハンドラの中で利用 CAPのイベントハンドラの中でSAP Cloud SDKのライブラリを使ってバックエンドにアクセスします。この方法については以下のブログで紹介されています。 Part 2: How to build an extension application for SAP S/4HANA Cloud using CAP, SAP Cloud SDK and SAP Fiori elements CAPの場合、ODataとして公開されるのでUI5からの利用も簡単です。Fiori elementsやSmart Controlを使いたい場合はこちらの方法となるでしょう。 SAP Cloud SDK for Java Scriptのプロジェクト構成 SAP Cloud SDK for Java Scriptは、NestJSをベースとしたプロジェクト構成になっています。以下の枠で囲った部分がNestJSで生成される部分、それ以外がSAP Cloud SDK独自の部分です。 独自の部分としてはCloud Foundryにデプロイするための設定やCI/CDのための設定などがあります。 サービスの実装はsrcフォルダの中で行います。NestJSのフレームワークに従い、Module、 Controller、Serviceの3つのファイルを使用します。 Module、Controller、Serviceの関係は以下のようになっています。リクエストに対するルーティングを行うControllerと、Controllerから呼ばれるService、それらを束ねるModuleが1セットになっています。 app始まりのデフォルトで作られるファイルは基本的に触らず、以下のように目的ごとにModuleを作ってそれをapp.module.tsに追加していく形になります。なお、spec.tsとつくファイルはテストで使用するものです。 サービスの開発手順については次回説明します。 プロジェクトの生成方法 現状、SAP Cloud SDKのプロジェクトの生成はSAP Cloud SDK CLIを使用して行います。 「現状」というのは、実はCLIはdeprecateされたとなっており、別の手段が今後提供されるものと思われます。 ドキュメントを見ても代替手段についてはまだ記載がないので、この記事ではCLIを使います。 CLIのインストール npm install -g @sap-cloud-sdk/cli プロジェクトの生成 sap-cloud-sdk init リファレンス ドキュメント APIリファレンス ブログ
- 投稿日:2021-06-20T07:51:36+09:00
インタープリタを作る その29
概要 インタープリタを作ってみた。 avrインタープリター書いてみた。 相対ジャンプrjmpの実装。 サンプルコード rjmp: function(k) { this.PC += k + 1; }, 成果物 以上。
- 投稿日:2021-06-20T07:11:57+09:00
Youtubeの自動字幕をテキストで抜き出すのに四苦八苦してやっとできた話。
プロトアウトスタジオの卒業制作でYoutubeの音声からテキスト化したいので、その実現可能性を探ります。 概要 Youtubeから動画や字幕をダウンロードできる「ytdl-org/youtube-dl」を導入しようとする。 Linus、Macだと簡単そうなんだけどWindowsはexeが準備されており、dllが不足のエラーが出る。 「Microsoft Visual C++ 2010 再頒布可能パッケージ」をインストールすると良いらしいがMicrosoftのページが404になっている。 2015とか2012をインストールするがうまくいかない(下位互換があると思い込んでいたが、実はなかった) 「Microsoft Visual C++ 2010 Service Pack 1 再頒布可能パッケージ MFC のセキュリティ更新プログラム」をインストールしたらうまくいった! dll不足エラーは出るけれどOK押して抜け続ければYoutubeの自動字幕をvttファイルでダウンロードできた。 コマンド 自動字幕をダウンロードするコマンド(Windowsのコマンドプロントで実行) youtube-dl --sub-lang ja --write-auto-sub --skip-download "https://www.youtube.com/watch?v=mppA6nx_rsQ.f247" 普通の字幕のダウンロードコマンド(Windowsのコマンドプロントで実行) ※公式字幕つきの動画をみつけられなかったので未検証。 youtube-dl --sub-lang ja --write-sub --skip-download "https://www.youtube.com/watch?v=YiAWVLkdYIo" 試行錯誤の流れ 「ytdl-org/youtube-dl」を試してみます。 まずはGitHubからZIPでダウンロードして、手元で解凍して、Visual Studio Codeで中身を確認。 「Python extension for Visual Studio Code」の導入を勧められたのでインストールしました。 ちょっといきなりコードみてもわからない感じだったので、ちゃんと説明文を読んでいきます。 DESCRIPTION youtube-dl - download videos from youtube.com or other video platforms youtube-dl - youtube.comや他のビデオプラットフォームからビデオをダウンロードします。 To install it right away for all UNIX users (Linux, macOS, etc.), type: すべてのUNIXユーザー(Linux、macOSなど)にすぐにインストールするには、次のように入力します。 sudo curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl sudo chmod a+rx /usr/local/bin/youtube-dl If you do not have curl, you can alternatively use a recent wget: curl がない場合は、最近の wget を使用することもできます。 sudo wget https://yt-dl.org/downloads/latest/youtube-dl -O /usr/local/bin/youtube-dl sudo chmod a+rx /usr/local/bin/youtube-dl Windows users can download an .exe file and place it in any location on their PATH except for %SYSTEMROOT%\System32 (e.g. do not put in C:\Windows\System32). Windowsユーザーは、.exeファイルをダウンロードして、PATH上の%SYSTEMROOT%\System32以外の任意の場所に置くことができます(例:C:\Windows\System32に置かない)。 You can also use pip: また、pip を使用することもできます。 sudo -H pip install --upgrade youtube-dl This command will update youtube-dl if you have already installed it. See the pypi page for more information. このコマンドは、すでにインストールされている場合、youtube-dlをアップデートします。詳しくはpipiのページをご覧ください。 macOS users can install youtube-dl with Homebrew: macOSをお使いの方は、Homebrewでyoutube-dlをインストールできます。 brew install youtube-dl Or with MacPorts: または、MacPortsで。 sudo port install youtube-dl Alternatively, refer to the developer instructions for how to check out and work with the git repository. For further options, including PGP signatures, see the youtube-dl Download Page. また、gitリポジトリのチェックアウト方法や作業方法については、開発者向けの説明書をご参照ください。PGP署名を含むその他のオプションについては、youtube-dlダウンロードページをご覧ください。 DESCRIPTION 説明 youtube-dl is a command-line program to download videos from YouTube.com and a few more sites. It requires the Python interpreter, version 2.6, 2.7, or 3.2+, and it is not platform specific. It should work on your Unix box, on Windows or on macOS. It is released to the public domain, which means you can modify it, redistribute it or use it however you like. youtube-dlは、YouTube.comやその他いくつかのサイトからビデオをダウンロードするためのコマンドラインプログラムです。Pythonインタープリタのバージョン2.6、2.7、3.2+が必要で、プラットフォームを選びません。Unix、Windows、macOSのいずれでも動作します。これはパブリックドメインとして公開されていますので、あなたが好きなように変更したり、再配布したり、使用したりすることができます。 youtube-dl [OPTIONS] URL [URL...] クライアント側での動作の説明っぽいですね。 サーバーサイドに置きたい場合どういう感じになるのかしら(知識不足)。 とりあえずクライアントに入れて先に進めていきましょう。 Windows用のexeをダウンロードしてダブルクリック。 エラーです(´・ω・`) 1 (と2) の配置場所でコマンドプロンプトを開いて youtube-dl --version を実行し、バージョンが表示されれば完了です。 あ、これかー。 → 同じ結果orz 「MSVCR100.dll」で検索すると、「Microsoft Visual C++ 2010 再頒布可能パッケージ」というファイルをダウンロード&インストールすればよいという記事を発見。 記事で紹介されていたリンク先はなくなっていたので、"Visual Studio 2015 の Visual C++ 再頒布可能パッケージ"をインストール。 って、うぉーい。 私の環境だと「2017」がインストールされてますね。 Adobeのサイトには「Microsoft Visual C++ 2012 再頒布可能パッケージ」と書かれている。さっき2015でダメだったからダメそうだけど一応やってみる。 なんでや工藤! が、youtube-dlは起動せずorz OSを再起動してもダメで、x86のほうもインストールして再起動してみましたがダメ。 コントロールパネルを見ると以下のようにインストールされている。 Cドライブで「MSVCR100.dll」を検索してみる。「C:\Windows\System32」に入ってるなぁ… 以下のサイトで紹介されてるのはGitHubじゃない公式サイト?からのダウンロードだけどexeの更新日時は同じ。予想はしてたが、症状変わらず。 ところでこのツール、サイトを読む感じ法律的に揉めてたのかな? youtube-dl downloads Latest (v2021.06.06) downloads: youtube-dl youtube-dl.exe youtube-dl-2021.06.06.tar.gz See the right for more resources. As everybody already knows our dev repository has been reinstated not so long ago. You can read the full story here. すでにご存じのように、少し前に私たちの開発用リポジトリが復活しました。この記事の全文はこちらをご覧ください。 We would like to thank GitHub for standing up for youtube-dl and making it possible to continue development without dropping any features. We appreciate GitHub taking potential legal risks in this regard. 私たちは、GitHubがyoutube-dlを支持し、いかなる機能も落とさずに開発を続けることを可能にしてくれたことに感謝します。GitHubが潜在的な法的リスクを冒してくれたことに感謝します。 We would also like to thank EFF and personally Mitch Stoltz for invaluable legal help. また、EFF、そして個人的にはMitch Stoltz氏の貴重な法的支援にも感謝しています。 We would also like to heartily thank our main website hoster Uberspace who is currently being sued in Germany for hosting our essentially business card website and who have already spent thousands of Euros in their legal defense. また、現在ドイツで訴訟を起こされているメインのウェブサイトホスティング会社Uberspaceにも心から感謝します。Uberspaceは、私たちの本質的なビジネスカードのウェブサイトをホスティングしてくれたことで、すでに数千ユーロの弁護費用を費やしています。 We also appreciate massive amount of support received lately and we are sorry we could not physically respond to everybody. また、最近いただいた多くのご支援にも感謝しており、皆様にお応えできないことを申し訳なく思っています。 Finally, we would like to thank all youtube-dl users and contributors for using and improving youtube-dl. 最後に、youtube-dlを使用し、改善してくださっているすべてのyoutube-dlユーザーとコントリビューターに感謝したいと思います。 Thank you all. ありがとうございました。 youtube-dl is back! youtube-dlが戻ってきました! とりあえずGoogle先生に軒並み聞いてみる。 YouTubeに変更があるとyoutube-dlは使えなくなることがあります。その場合、アップデートする仕組みが用意されています。 システムに組み込むならYoutube側の仕様変更も考慮しなきゃですね。。。 うーん、ここまでみんなに使われていて、最近の記事もあるからWindows10だから起動できないってこともなさそうなんだけど… あ、ダウンロードしたファイルだからセキュリティ解除かな。 違うorz ちなみに実行するコマンドプロントも「管理者として実行」にしてみてますが、変わらず。うーん。 GUIで実装してるツールも見つけましたが、同じエラーが出現。 Visual C++ 再頒布可能パッケージは下位互換性がない! 2015 がインストールしてあるからといって 2013 以前が必要なソフトウェアが動くわけではない このバージョンは Visual Studio のバージョンと紐付いている (実際には Visual C++ 自体のバージョンだが便宜上こう表現する) 例えばVisual Studio 2015 でビルドされたアプリケーションならば Visual C++ 2015 再頒布可能パッケージが必要 あ、そういうものなのか。じゃあ2010がなんとしてでも必要ってことか。 ん…セキュリティ更新プログラムはあるな。大本が404なんだからこっちをダウンロードしろってことかも? Microsoft Visual C++ 2010 Service Pack 1 再頒布可能パッケージ MFC のセキュリティ更新プログラム (2011/08/09)(最新版) x86版インストールしたら成功! な、長かった… 「セキュリティ更新プログラム」という名前だったので、「大本」が先に必要なんだと思ってた。 というかMicrosoftさん、最初のダウンロードリンク404にするんじゃなくて、最新版へ案内してくださいよ…。 またエラー出るー! 今回は色々なdllがないとどんどん出てくる。 と思ったけど「OK」押してたらなんか進んだ。 ダウンロードできたー!!! 音声と動画が分かれるのはそういう仕様で、マージすることもできるらしいですが、今回は無視。 x64をインストールしてみましたが事象は変わらず、「〇〇.dllがありません」を13回くらい「OK」で抜けて、もう3回くらい出るのを抜けるとダウンロードできます。 これから字幕だけ抜いていきますけどその時には関係ないdllだといいな… さて、コマンドを投入…失敗。でもこれはすぐに原因がわかって、Windowsなので文字列はダブルクォーテーションで囲む必要があるだけでした。 で、日本語の字幕があることが確認できました。(元動画を目視すると自動生成の字幕でした) では字幕だけダウンロード開始。残念ながら7回dllエラーは出ます。 ダウンロードできた! しかし、ttmlフォーマットを指定してのはできませんでした。引数なしではダウンロードできているvttも引数つけるとダウンロードできないので引数が変わってる可能性あり? うーん、でもGitHub見る限り合ってる気がする。 ダブルクォーテーションで囲ってみたり(でもjaが囲ってないんだから関係ないはず)、GitHubにある例の「ass/srt/best」にしてみたけど変わらず。 これはもうdllがないのがダメなのかな。 そういえば「Pythonインタープリタのバージョン2.6、2.7、3.2+」という条件がありました。あれ、ひょっとしてこれがdllエラーの原因?と思ってインストールしてみましたが、変わらず。 インストールしたのは以下のバージョンです。 Python 3.2.5 - May 15, 2013 Python 2.7.18 - April 20, 2020 (これだけPATHを通すオプションが選べたのでインストールを選択しました) Python 2.6.6 - Aug. 24, 2010 インストール方法は以下を参考にさせていただきました。 今回完了したTODO まずは動かしてみる→済 Pythonインタープリタのバージョン2.6、2.7、3.2+の導入方法を調べる→済 フォーマット引数での指定方法があっているか確認する。→済 次のTODO vttとttml形式の違いを調べる。 参考記事の"xmlでもあるttml形式が個人的には加工しやすそうだなと感じた。"が本当か調べる。 vttフォーマットでテキストを検索してカウントできるかやってみる。 サーバー側で動かす実装方法を調べる。 Linuxサーバーに乗せる? そこまでするよりユーザーのクライアント動作としてしまう? 自動字幕ではなく、ちゃんと字幕がある場合はそちらをダウンロードするような仕組みを考える。 公式字幕つきの動画をみつけてコマンドが正しく動くか確認する。 Youtube側の仕様変更の際のバージョンアップマニュアルを作成する。
- 投稿日:2021-06-20T05:55:35+09:00
初心者がWEBデザインするなら、STUDIO !(ヘッダー編)
今回はwebサイト制作で挫折しまくり初心者の私が、見つけた救世主"STUDIO"を使用して レスポンシブ対応からSEO対策まで備えたウェブサイトを作成します! しかも全て、、、、、、、ノーコードで!!!最高すぎませんか? STUDIOは日本に本社を構えるSTUDIO株式会社が2018年4月に正式リリースした国産のホームページ制作ツールです。2020年6月にはCMS機能も追加されました! 公式サイトSTUDIOで登録を行い基本無料で作成から公開までできる。 また有料コンテンツでは追加機能や独自ドメイン接続、外部アプリ連携などもできます。 STUDIOについてメリット、デメリットを説明します。 メリット ・ノーコードでデザインができる ・テンプレだけでなく自由度の高いデザインができる ・動きのあるデザインも簡単に作れ、細かく設定できる ・デザイン⇨公開までが簡単 ・レスポンシブ対応が簡単にできる ・チュートリアル動画も無料で充実 デメリット ・html、cssの必要最低限の知識がいる ・STUDIOに実装されていない機能は使えない 今回はheader部分を作成しレスポンシブ対応にさせました。モバイル対応のところは憧れのハンバーガーメニューを実装しました。STUDIOを使ってわずか3時間程で完成しました。 操作方法に慣れるまで大変かもしれませんが、それでも圧倒的に目で確認しながらレイアウトできると、楽しいですし分かりやすいと思います。 ではheaderの作成方法について ①、左作業スペースにあるメニューからボックスを選ぶ初期値は ②、右側の作業メニューでタブの種類変更ができるので、headerに変更。大きさは適当に横100%、縦70pxとかに始めはしてます。 ③、まず私は企業のロゴを配置したかったのでボックスからimageを選択 右クリックでimageボックスに入れる画像を選ぶ。アップロードした画像を指定 ④、headerのボックスにimageを入れます ※この時 imageボックスの横幅、縦幅がhederボックスより大きいと入らないので注意! ⑤、上記と同じような手順でメニューバーも作って行きます。テキストをlistボックスに入れて 一つ目の文字サイズ、文字間を調節したらコピーアンドペーストで必要項目分作ると時短です。 ⑥、メニュー項目ができたら、nuvボックスを作って中に入れて行きます。 要素を入れたら横並びに指定しましょう。padding等間隔調整もここでします。上部メニューに項目がありますので各要素ごとに行ってください。 ⑦、nuvボックスをheaderボックスに入れます。あとはボックス外側にある整列ようタブで調整するだけです。 ⑧、完成です。 ちなみに私が作ったのはこちら(PC用header) (モバイル用header)ハンバーガーメニューで メニューを開く動きからシャドーなど簡単にボタンで操作できます。 上記説明で分かりずらい方もいらっしゃると思うのでまずは自分でチュートリアルも見ながら、STUDIOを使ってみることをオススメします!ハンバーガーメニューはチュートリアルの項目にもありますのでぜひ作ってみてください! では、また作成次第更新して行きます
- 投稿日:2021-06-20T03:33:47+09:00
Swiper V6で思い通りに動かなかった場合
SwiperのV6系を導入したあと、動くには動くが左右のボタンがnext,prevのボタンが動かなかったりページャーが出なかったり、 エフェクトが思い通りになってなかったりと思いの外ハマったので記事にする。 公式ドキュメント インストール $ npm install swiper 利用 NGなパターン // import Swiper JS import Swiper from 'swiper'; // import Swiper styles import 'swiper/swiper-bundle.css'; const swiper = new Swiper(...); 上記がうまく行かない原因だった。 下の方にあるようにbundleを含むimportをするとうまく動いた。 OKなパターン // import Swiper bundle with all modules installed import Swiper from 'swiper/bundle'; // import Swiper styles import 'swiper/swiper-bundle.css'; // init Swiper: const swiper = new Swiper(...); という結末
- 投稿日:2021-06-20T01:34:34+09:00
Javascriptの関数、メソッド、コールバック、PROMISE等
javascriptを学ぶ上でのつまずきの石 長きに渡る酸いも甘いも経験したプログラマー諸先輩からすれば、どうってことの無いものでも、勉強を始めたばかりの若輩者には大きな躓きの石になるものが多々あり、そういった若輩者でも経験を積んでいくうちに、そういった事も忘れてしまい、何が難しかったのかも分からなくなってしまうことが多い。 そういった事もあり、まだフレッシュなうちに躓いてきた石についてログっておくことは、他に躓いてる人たちのためにもなるかなぁという気持ちで書いてみるのじゃ。 Javascritを学んでいると、なかなかに奇々怪々で理解の難しいトピックが登場する。日常では絶対に使わない用語なので、当然じゃ。 関数 メソッド コールバック 非同期処理 PROMISE, Async, Await この辺りがなかなか理解が難しいよね。 まず、以前に書いたけど、日本語がムズすぎる問題。 functionは関数とかいう分かりにくい言葉に訳してるのに、なぜメソッドやコールバックはカタカナなんじゃ! 漢字とカタカナが混在し、言葉からそいつが何者なのかよくイメージできへん。 上にリストアップしたトピックは、全部つながっていて、 まず理解すべきは”Object”。 Javascritの世界は、すべてオブジェクトで出来ているといっても過言ではない。配列も文字列もJavascritではオブジェクト。 let person = { id: 007, firstName: "John", lastName: "Doe", age: 18, hobby: "neru" }; お馴染みだよね。 関数 数字やデータを色々と処理してアウトプットしてくれるやつ。何か入れると、何かが出てくる不思議な箱。 let x = myFunction(4, 3); function myFunction(a, b) { return a * b; } console.log(x); // 12 メソッド オブジェクトに入った(登録されている)関数をメソッドという。つまり、メソッドは関数。それだけ! const person = { firstName: "John", lastName: "Doe", hobby: "Neru", fullName() { //このfullNameがメソッド。ただ、ここのthisの理解は重要。 return this.firstName + " " + this.lastName; } }; const name = person.fullName(); console.log(name) //John Doe 重要なのは、自作する関数以外にJavascriptに元々登録されている関数(つまりメソッド)が沢山あって、例えば、下記コードはドキュメントオブジェクトに元々登録されているquerySelectorっていうメソッドに.exampleという引数を渡して使ってるだけ。こういうオブジェクトとメソッドの関係はいちいち意識したほうが良いと思うよ。 document.querySelector(".example") コールバック コールバックは関数に引数として渡される関数 みたいな言い方をよくされてるけど、よくわからんよね。 コールバックの基本は、親関数の中で何らかの処理がなされ、その結果を引数として渡される。で、それを受け取った時点でファンクション発動。 下記が超シンプルなコールバックの形。 numberDisplay の中で、x+y がなされ、それがコールバックの引数にパスされる。で、パスを受け取った瞬間に値をコンソールに表示するっていう関数が実行されている。 値がパスされてから、何らかの仕事をするっていうのが基本形。サッカーをイメージすると分かりやすい。タマが来ないとドリブルもシュートも出来ないからね。 ボールを奪う→ドリブルする→パスを出す→シュートを打つ→裸になる→踊る というファンクションを連続で繰り出しているわけなのじゃ。 const numberDisplay = (x,y,callback)=>{ let sum = x + y; callback(sum); } numberDisplay(1,3,(num)=>{ console.log(num); }) 実際によく使われるパターンが、エラーと成功時のデータの二つを返すタイプ。 下の例では、promptが空の時はエラーメッセージを返し、ちゃんと値が入ってる場合はそのデータを返すというコールバック。 因みに何故コールバックに()を付けないかというと、()をつけた瞬間に(つまり”空であっても”引数を渡した瞬間に)その関数は実行されてしまうからである。 説明したように、何かを処理し、それを引数として受け取った時点でコールバックは初めて実行されないといけないからだ。 イメージとしては、親、子、孫関数があるとして、孫が処理したデータを子に渡し、その子がさらに処理したデータを親に渡すっていうイメージ。 ただ、promise登場以前は、このネスト構造が深くなると、地獄と化していたわけです。 function processUserInput(callback){ var data = prompt('Please enter your name.'); if(!data){ callback('Error!', undefined); }else{ callback(undefined, data); }} processUserInput((error, data)=>{ if(!error===undefined){ console.log(error); }else{ alert(`Hello ${data}`); } }) コールバックヘル この地獄に嵌った罪深いノビグラマーたちを救うために現れたのが、promiseである。 Promise Promiseは、いってしまえば、ただのオブジェクト。ただ、地獄の沙汰を救うために、色々なメソッドが用意されたオブジェクトっていう事になる。 この処理が終わったらこれ、で、これが終わったらこれ、もしエラーが帰ってきたらこの処理 っていう感じで、てきぱきと仕切ってくれるのである。 この図でいうと、最初のPromiseの処理で、結果がエラーの時はreject、上手くいった時はfulfillという、いわば合図を送ってくれる。これがPromiseが帰ってくるとかと色んな所で解説してるけども、よくわからん事の本質である。 pendingっていうのは、多少時間がかかる処理であるとイメージして欲しい。この辺りが非同期処理の理解にかかわってくるのじゃ。 で、成功の合図があった暁にはPromiseオブジェクトにあらかじめ入ってる.thenメソッドにて登録された関数を順番に実行してくれる。 地獄のネストからの生還である。 下にw3schoolからのサンプルコードを載せておく。 let myPromise = new Promise(function(resolve, reject) { let x = 0; // 何らかのコード (try to change x to 5) if (x == 0) { resolve("OK"); } else { reject("Error!"); } }); myPromise.then((resolve)=>{ document.getElementById("demo").innerHTML = resolve; //OK(xが0の場合) }).catch((reject)=>{ document.getElementById("demo").innerHTML = reject; //Error!(xが0以外の場合) }) 見てみると、コールバックで解説したように、エラー時と成功時に別の引数を次の関数に渡しているのが分かるじゃろう。 解説しようと思っとったが、バイトの時間なので詳細はまた次の機会じゃ。 w3schoolはシンプルで分かり易いので、リファレンスにおすすめじゃ。 w3school じゃあの。
- 投稿日:2021-06-20T01:09:57+09:00
Redux の store や connect を偽装してみた
はじめに お友達のフリーランスエンジニアが、RubyエンジニアからJSエンジニアReact案件に移行できたと聞いた記念で、redux を学んでみます。 元サンプル Redux サンプルとしてシンプルなTODOアプリを動かします。 CodeSandbox というWebで動作する。とてもわかりやすいTODOサンプルを作っていただいているページがありましたので、こちらでReact-Reduxを動かします。 FN2004004 | React + Redux入門 07: React ReduxのフックuseDispatch()とuseSelector()を使う | HTML5 : テクニカルノート http://www.fumiononaka.com/Business/html5/FN2004004.html react-redux-todos-02 - CodeSandbox https://codesandbox.io/s/react-redux-todos-02-tpvcw?from-embed ここからFork して動くサンプルを作っていきます。 初期状態 単なるForkコピーです。 react-redux-todos_1 - CodeSandbox https://codesandbox.io/s/react-redux-todos1-mlowe これを改良していきます。 Action を削除 AddTodo.js からリンクしている、addTodo があるのでこれを消す。 これは簡単 react-redux-todos_2 - CodeSandbox https://codesandbox.io/s/react-redux-todos2-jydzh?file=/src/index.js react だけで書き直す Reduxは、store オブジェクトを下位オブジェクト全てに伝搬するという Provider store の機能で成り立っていて アプリケーション唯一の store を下位オブジェクトに配っているだけ。 アプリケーション唯一の state を下位に配るのと大差ない感じなのでそのように改良してみます。 ルートのindex.js から Provider store を削除して App.js で 単独の State をつくり、AddTodo に onAddTodo 関数を定義する AddTodo.js 側は、いままで、配布されていた Store.dispatch を受け取るのをやめて 単に、onAddTodo をうけとって追加するようにする Todo コンポーネントも暗黙でstoreをProviderを使って渡されていた todos を自前で渡すようにする Connectという仕組みも削除する。これで、reducer フォルダの中身も削除できる react-redux-todos_3 - CodeSandbox https://codesandbox.io/s/react-redux-todos3-lxfkt?file=/src/components/App.js Redux をつかったアプリケーションよりもかなりシンプルで理解しやすい記述になった感じです。 reduxを偽装してみる。 一番はじめの react-redux-todos_1 から、新たに作りなおします。 ルートのindex.js から Provider store を削除してApp.js で 単独の State をつくり、AddTodo に onAddTodo 関数を定義するのは同じ AddTodo.js を改良なしに使うために上位からdispatch を渡すためにstore = { dispatch, todos } のオブジェクトをつくります。 これで、AddTodo.js と TodoList.js の connect を削除して、 dispatch の中で、message を受け取って、reducer/todos.js にわたすと redux を排除したプロジェクトで同じ機能で動きます。 react-redux-todos_4 - CodeSandbox https://codesandbox.io/s/react-redux-todos4-4sr5e Provider だけ取除いてみる 一番はじめの react-redux-todos_1 から、新たに作りなおします。 そもそも、Provider ってなんなのか、っていうと、バケツリレーって仕組みですね。 Provider抜きでう動かしてみます。 ルートのindex.js のProviderを消して、App.js で store 次々に渡しています。 react-redux-todos_5 - CodeSandbox https://codesandbox.io/s/react-redux-todos5-umsyk?file=/src/index.js 元のサンプルはバケツリレーを隠蔽しようとして、Provider って仕組みを使っているのですが、実際のところ、バケツリレーのほうがデータの流れがみえてわかりやすいん。 Provider は状態変数をこっそり下位オブジェクトにわたす仕組みなので、コンポーネントの独立性が下がり、再利用性が下がり、他のところに移動させると動かない、という場合があるので、 Provider を使うと少し記述量は減るけど不可思議な挙動になってしまってコンポーネントの独立性が失われる仕組みなので あまりおすすめしません。 わかりやすいコードを書きたいならバケツリレーで一つずつちゃんとオブジェクトを渡しましょう。 バケツリレーをもう少し書きやすく Reactのスプレッド構文を使うと、store の記載がいくらか減ります。 react-redux-todos_6 - CodeSandbox https://codesandbox.io/s/react-redux-todos6-p3pq9?file=/src/components/App.js Store を偽装する これらを踏まえて、 再度、react-redux-todos_1 からフォークして、storeを書き換えてみます。 store を、ルートのindex.js で置き換えて、 reducer/index.js の combineReducers 消したり AddTodo.js と TodoList.js の connect を削除したりして引数の取り出しをしたりして、 いろいろあれやこれやしてみて、reduxコードを活かしながらstore部分をつくりこんで 他の部分は 最小限の修正で redux をプロジェクトから除外して、store を偽装してみました。 react-redux-todos_7 - CodeSandbox https://codesandbox.io/s/react-redux-todos7-e2msk?file=/src/index.js もう少し偽装Storeを改良する store 内に todo というプロパティが埋め込まれているので、ここを改良します。 react-redux-todos_8 - CodeSandbox https://codesandbox.io/s/react-redux-todos8-xsojj?file=/src/index.js さらに改良して、connectも作り込む react-redux-todos_9 - CodeSandbox https://codesandbox.io/s/react-redux-todos9-shpc9?file=/src/components/AddTodo.js これで、react-reduxの connect や store を自前で実装できていると思います。 終わり 以上、サンプルでした。