🖍️

【独学・未経験】Pythonデスクトップアプリを作成したから見て欲しい。 (ポートフォリオ )

2021/03/28に公開

最初に

エンジニアに未経験から転職するにはポートフォリオが必要と聞いて、少しづつ作成していた物が自分の中でこれ以上作っても大きくは変わらないと思い、一旦区切りを付けるため公開することにしました。

最近見かける「フレームワークを使用して・AWS・Docker・CI/CD等を使用してWebアプリを公開してURLはこれです。」みたいな派手さはないです。

ポートフォリオを公開するサイトを立て、そこに載せる事をネットで見かけた人の多くが行っていたのですが、自分が納得する出来栄えで作成するにはもう少し技術力が必要だと感じたのでGitHubにレポジトリでコードを残して、READMEにポートフォリオを作成する上でこだわった点・苦労した点を載せる事にしました。

作者

年齢は25歳です。
エンジニアを目指してプログラミングを学習している初学者です。
フロントエンド・バックエンド両方好きです。
機能的でデザイン性のあるアプリを作れるようになりたいと考えています。
大学は文系で希望したゼミに行けなく、教授が好きな物作って良いという事だったので、その際にPythonを勉強しました。
JavaScriptは最近、勉強しました。そのアウトプットとして今回のアプリはフロントエンドをHTML・CSS(SCSS)・JavaScriptで作成しました。

全く見当違いなポートフォリオの作り方をしているかも知れないのでとても不安です。人事の方に今自分が出来る事が上手く伝われば良いと思います。
ここをこうしたらもっと上手く伝わる等のフィードバックを頂けると幸いです。

ポートフォリオとして

Frontend

GitHubレポジトリ:YT_Download

アプリの概要

Youtubeをダウンロードして、音声の情報(曲名、アーティスト名、ジャンル、年代、アートワーク、歌詞)を自動で取得する。AACファイルにiTunesに対応した形式で書き込む。

どんな意図で作ったのか

昔Apple MusicやSpotifyの音楽ストリーミングサービスが出る前に、友人がYoutubeから音楽をipodに入れるのにArea 61 ダウンローダーというアプリを使用していました。

作業内容

  • アプリを使って動画をダウンロード
  • 別のアプリで動画から音声をAAC形式で抽出
  • iTunesに登録後に曲情報をgoogleで検索して貼り付ける

動画をダウンロードして、音声を抽出して曲情報をgoogleで検索して、アートワークの画像を貼り付けてと一曲ずつ丁寧にiTunes上で作業を行なっていました。3曲追加するだけでも15分ほど掛かっていました。

当時はプログラミングのアプリを作るのは夢のまた夢みたいな感覚で友人は大変な作業を通して音楽を聞いていました。現在はプログラミングに関する情報も整備されて独学でも学びやすい環境になり、その事を思い出してポートフォリオも兼ねて作成しました。

このアプリを使えば友人が15分かけていた作業を20秒ほどで終わらせる事が出来ます。

こだわった箇所

フロントエンド

radioButton

  • シンプルなUIでユーザの選択肢を減らした。

「ヒックの法則」では選択肢が多過ぎるとユーザーは迷ってしまい、こちらが望む行動を起こさないと言われています。
ユーザが選択するのはダウンロード時に音声のみか、モバイル向けの画質で一番画質が良いもの、一番画質が良いものの3つのみに絞る事ユーザが判断する時間を減らしユーザにプレッシャーを与えないようにしました。そうする事でユーザがスムーズにアプリを使用出来るよう作成しました。

progress

  • 待機処理が入る際はどれくらいで終わるか表示する。

ローディングのアニメーションを挟む事でユーザへのストレスを軽減しました。
また重い処理が入る際はプログレスバーを表示してどれくらいの目安で処理が終わるのかを表示しています。

苦労した箇所

サーバから応答があるまでローディングアニメーションを表示させる事については記事が多くあったのですが、正確な進捗状況をサーバから受け取りそれを表示するという事について書かれた記事はなかったので実装する上で少し苦労しました。Eelではサーバ側からフロントのJavaScript関数を実行出来るため、「Python側の変数(進捗状態)JavaScriptの関数に引数として渡してDOM内容を書き換える作業」を1秒毎に行わせてプログレスバーを実装しました。

Thumbnail

  • 意図しない動画のダウンロード防止する。

URLを入力した時点で動画のタイトル・サムネイルを取得して表示させています。ユーザがこれからダウンロードする動画が合っているか相互確認するために実装しました。

苦労した箇所

細かいデザインで機能としては関係ないのですが、サムネイル下半分にグラデーションが掛かっています。それは背景のサムネイルの色に合わせて変更するようになっていて、color-thiefというライブラリで画像にある特徴的な色を抽出してそれをグラデーションに適用しています。サムネイルのDOMは最初2つとも同じだったので片方の色が変わったらもう片方も変更されてしまう状態になっていました。個別にclass名を追加して変更されるようにしました。中々、思うようにいかないと痛感しました。

resphonesive

  • レスポンシブ対応・ハンバーガーメニュー

最近では当たり前だけど、しっかり作りきった事がなかったので今回作成する事が出来てよかった。

バックエンド

  • 待機処理を並列・非同期処理にする事で動作速度向上

待機処理を行う動作は主に2種類あります。

  1. 動画・音声のダウンロード時
  2. サムネイル・タイトルのダウンロード・音声情報を取得するために歌詞サイト・iTunesAPIへのアクセス時

前者はThreadを用いてマルチスレッドで並列に処理を行うようにしました。
理由は動画・音声のダウンロードはプログレッシブ形式(データを保存しながら再生可能)なので保存とダウンロードが交互に行われ、全く処理が行われない状況は少ないと思いました。並列処理を行わせ速度改善を図りました。マルチスレッドなのでこちらの方が多くリソースを消費すると予想されます。

後者はasyncioを用いて非同期処理で待機する時間がある際に途中で処理を切り替えて行うようにしました。
理由は requests.get() でリクエストを投げて返ってくるまでの間はそこで全く処理が行われない状況が発生して、その間に別のリクエストを投げるなどしてできる限り待機する処理を短くしました。
こうする事で2つリクエストを投げる処理がある際に1つの処理でレスポンスに1秒かかるとする。2つだと全体で2秒の待機処理が入る。これをノンブロッキングに変えリクエストを投げた直後に待機処理に入らず、直ぐに別のリクエストを投げる。するとほとんど同時にレスポンスが返ってくるので待機時間は1秒になる。これが8つなどのリクエストになってもだいたい1秒近くで返ってくるので、リクエストの数が増えるほど効果がある。そしてスレッドは一つしか使用していないのでマルチスレッドよりもリソースの消費が少ないと思われる。

※ただ今回のプログラムでは requests を用いておりライブラリ自体非同期に対応していないため構文上は非同期処理ですが、内部では並列処理が行われています。

苦労した箇所

asyncioの構文が難しくてどのような仕組みで動作しているの分からなかったので、asyncioがPythonのどんな機能から出来ているのか調べる所から入りました。調べた内容は下記の記事にまとめてあります。
Pythonのrequestsを非同期にしてiTunes APIに高速にリクエストを投げるには

progressNumber

  • 並列で行われている処理の進捗を計算する。
def show_progress_bar(self, stream, chunk, bytes_remaining):
    current = ((stream.filesize - bytes_remaining)/stream.filesize)
    with self.lock:
        self.percent[threading.currentThread().getName()] = current*100

stream.filesizeにはURL一つ当たりでダウンロードされる予定のファイルサイズの値が入る。
bytes_remainingにはまだダウンロードされていないファイルサイズが入る。
(全体のファイルサイズ - 残りのファイルサイズ)/全体のファイルサイズ でどれくらいの割合のデータがダウンロードされたか計算している。その値をself.percent に各々のスレッド進捗として格納する。

def get_progress(self, threads):
    while any(t.is_alive() for t in threads):
        with self.lock:
            percent = round(sum(self.percent.values()) / (self.count * self.number))
            eel.putProgress(percent)
        time.sleep(1.0)
    eel.putProgress(100)

最後にself.percentの値を全て足して、現在立ち上がっているスレッドの数で割って全体の進捗状況の値をeel.putProgress(percent)でフロントに渡している。

songInfo

  • 音声情報の取得

最初はShazamのように音声ファイルを解析して情報を取得出来るかと考えていましたが、そのようなAPIも技術もなかったので、無理かと考えていました。そこでYoutubeの動画についての説明欄に、動画内で使用されている音声情報を付与している事に気付きました。そこから音声情報を取得してiTunesAPIに投げる事でより詳細は情報(ジャンル、年代、アートワーク)を取得出来るようになりました。

担当者はポートフォリオを隅々とはみないので紹介したい機能は自分から名乗りましょう。出来ればその際に苦労した躓いた箇所も説明出来るといいかもしれません。

苦労した箇所

Pytubeに動画の詳細情報をリスト形式で取得出来るとドキュメントに書かれていたが、リスト形式で取得出来なかったのでissueを作成して直してもらった。自分でプルリクエスト出してマージして貰えれば良いのだが、難しいと考え助けを求めた。

使用技術・選定理由

バックエンドは一番慣れているPythonを使用して作成しました。Eelというライブラリを使用してフロントエンドはHTML・CSS(SCSS)・JavaScriptを用い作成しました。PythonでGUIアプリを作成する場合、tkinter, kivy, PyQt, wxPython等が挙げられるのですが、GUIを作成するのに独自の記法を学ぶ必要があり、それらは汎用性が低く今後Web開発もやってみたいと考えていたので、HTML・CSS・JavaScriptでGUIが作成出来る。Eelを選択しました。
作成後にexe, app形式にしてインストールを簡単に出来るようにcx_Freeze使用を試みました。その他にもpy2exe, py2app, PyInstallerがありましたが、2つは開発が終了しており、PyInstallerはexe化したファイルの起動が遅いと書かれた記事を見たのでcx_Freezeを選択しました。

上手くいかなかった事

結果的にbuildしてapp形式に出来ました。しかし新しくファイルを作成する open() 処理が走るとエラーが起こり上手く動作しませんでした。app形式にした場合、consoleが立ち上がらないのでエラーが起きても分かりません。そこでファイルにlogを残すコードを追記したのですが、logファイルを生成する過程でエラーが出るのでlogファイル自体を生成することが出来ませんでした。
別のパソコンでも簡単に動作する形にまで持っていきたかったのですが、上手くいきませんでした。実行環境を意識させずに実行ファイルとして作成する場合、Pythonでは難易度が高かったです。

考えられる対策

コンパイラ言語で書いていたらこの辺りの問題は解決出来ていたかと思います。もしくはElectronを使用してバックエンドをnodeJsで書く方法もあったと思います。それらを使って今回のアプリを作成する技術力が必要だと感じました。Pyinstallerの方が記事が豊富なので、起動時間は多少遅くてもbuildするのが良いかも知れないです。

出来ない事・出来る事

出来ない事

今回データベース等は使用しなかったので、SQL操作を行う練習が必要になると思います。少しづつではありますが勉強しています。
AWS、GCP(一度だけスクリプトをデプロイしました。)等にデプロイする事をしなかったので、その辺りのネットワーク・Linux等の知識がWeb開発をする場合、必要だと考えています。
Webサービス等を開発する際、先にGithub等でオープンソースの人気プロジェクトを参考に設計を進めると綺麗な設計手法を学びながら作成出来るという事を知りませんでした。思うままに設計してしまい読みやすく綺麗なコードと言い難い状態になっています。今後アプリを作成する際は似たようなプロジェクトを参考に開発をしたいと考えています。

出来る事

上記のポートフォリオから、Pythonを使用した簡単なAPIを使用したスクリプト、処理速度を配慮したスクリプトの作成、HTML・CSS(SCSS)・JavaScriptを使用したWeb制作は出来ます。JavaScriptのモダンなライブラリ等を使用してアプリを作成出来るほどではないですが、Reactのチュートリアルで五目並べを作成しました。Webアプリ等に必要な基本的機能CRUDは今回のポートフォリオを作成する過程で一通りを行いました。iTunesAPIからJsonデータを取り扱う際に音声についての情報生成(ジャンル、年代、アートワーク)、フロントエンドでそれらの読み込み、ユーザからの変更があればJsonデータの更新・削除が出来ます。

Discussion