用 Python

帶你輕鬆玩 KKBOX Open API

John Liu 劉炘武

- Partner Engineer @

  • 協助Partner介接KKBOX服務
  • 撰寫文件及Sample Code
  • Linux and Vim lover
  • <3 Python

@johnliu55tw

在開始之前…

  • 不要客氣,請隨時發問

  • 已安裝 Python  3 

  • 已安裝 pip

  • 安裝 pipenv

    • $ pip install pipenv
  • git clone https://github.com/johnliu55tw/InnovationChat-20
  • 投影片連結:https://goo.gl/GtAc8o

  • Wifi: 'K_Square_guest', '12345678'

Agenda

  • 為什麼是 Python?
  • 探索KKBOX Open API
  • KKBOX Open API 認證機制
  • Let's code!
    1. 使用Search API,使用 Python
    2. 串接KKBOX Widget
    3. Playlist Search web app
  • Summary

為什麼是 Python?

  • 因為我喜歡。
  • 語法乾淨,像在寫英文
  • IPython rocks!
  • 多種編程範型 (Programming Paradigms)
    • OOP、Functional
    • 內建 Asynchronous IO (Since 3.4)
  • 資料科學及大數據模組

探索KKBOX Open API

  • 取得 KKBOX 平台的高品質音樂資料
    • 歌手、專輯、歌曲
    • 排行榜、電台、主題歌單
  • HTTP RESTful API
  • JSON 編碼

What is KKBOX Open API?

探索KKBOX Open API

KKBOX Open API 認證機制

  • 所有 Endpoints 都需要認證
  • OAuth 2.0 - Client Credentials Flow
  • Client 使用 ID 及 Secret 向認證伺服器請求認證
  • 認證伺服器核發 Access Token 給Client
  • Client 使用 Access Token 向 KKBOX Open API 請求資源
  • 一旦 Access Token 過期,必須重新要求 Access Token

Client Credentials

  • Client ID

  • Client Secret

KKBOX Open API 認證機制

KKBOX Open API 認證機制

from kkbox_developer_sdk.auth_flow import KKBOXOAuth
# Replace CLIENT_ID and CLIENT_SECRET with yours
auth = KKBOXOAuth(CLIENT_ID, CLIENT_SECRET)
token = auth.fetch_access_token_by_client_credentials()
print(token.access_token)
BQfUdNXcFWiTliaSiTovbQ==

​​使用 SDK 取得Access Token

$ pip install kkbox_developer_sdk

KKBOX Open API 認證機制

Token access helper function

import pickle

def get_token(token_file, client_id, client_secret):
    """Helper function for getting the token.

    If the specified file exists, try to load it with pickle.
    Else use the given client ID and Secret to request a token.
    """
    if path.exists(token_file):
        with open(token_file, 'rb') as f:
            return pickle.load(f)
    else:
        auth = KKBOXOAuth(client_id, client_secret)
        token = auth.fetch_access_token_by_client_credentials()
        with open(token_file, 'wb') as f:
            pickle.dump(token, f)
        return token

Let's code 1: 使用Search API

目標:搜尋與「運動」有關的歌單

  • 請求方法及 URL
  • 資源 ID
  • Query 參數
  • 資料回傳格式

Let's code 1: 使用Search API

目標:搜尋與「運動」有關的歌單

from kkbox_developer_sdk.api import KKBOXAPI
kkboxapi = KKBOXAPI(token)
search_results = kkboxapi.search_fetcher.search(
        '運動',
        types=['playlist'],
        terr='TW')
playlists = search_results['playlists']['data']
first = playlists[0]
from pprint import pprint
pprint(first, depth=2)

目標:搜尋與「運動」有關的歌單

{'description': '在做有氧運動時...'
 'id': 'KnqLLVliEedzFEen54',
 'images': [{...}, {...}, {...}],
 'owner': {'description': 'House是現今流行樂壇中最重要的樂種...',
           'id': 'DZHpWKlqBsC81LL8oy',
           'images': [...],
           'name': 'DJ Rainbowchild',
           'url': 'https://www.kkbox.com/tw/profile/DZHpWKlqBsC81LL8oy'},
 'title': '世大運動一動:有氧運動專用勸世舞曲(8.11更新)',
 'updated_at': '2017-08-11T12:51:22+00:00',
 'url': 'https://event.kkbox.com/content/playlist/KnqLLVliEedzFEen54'}

Let's code 1: 使用Search API

Playlist search helper function

def search_playlists(token, keyword):
    """Search playlists using the given keyword.

    This function returns a list of playlist object directly.
    """
    kkboxapi = KKBOXAPI(token)
    data = kkboxapi.search_fetcher.search(
            keyword,
            types=['playlist'],
            terr='TW')
    return data['playlists']['data']

Let's code 1: 使用Search API

Let's code 2: KKBOX Widget

  • id
  • type
  • terr
  • lang
  • autoplay
  • loop
https://widget.kkbox.com/v1/?param1=value&param2=value
<iframe width="320" height="470"
    src="https://widget.kkbox.com/v1/?id=KnqLLVliEedzFEen54&type=playlist"
</iframe>

Embed with <iframe>

Let's code 2: KKBOX Widget

Let's code 3: Playlist Search

  • 播放清單搜尋器

  • Python + Flask + Jinja2 HTML template engine

  • No JavaScript!

Jinja2 HTML Template - Form

<div class="w-100 mb-3">
  <form action="/" method="get">
    <div class="input-group">
      <input type="text" name="question" class="form-control"
             placeholder="Ask me something about music...">
      <div class="input-group-append">
        <button class="btn btn-outline-secondary" type="submit">
          Search
        </button>
      </div>
    </div>
  </form>
</div>

Let's code 3: Playlist Search

Jinja2 HTML Template - Search History

<div class="w-100 mb-3 border border-info rounded"
     style="height: 80px; overflow: scroll;">
  <ul>
  {% for record in search_history|reverse %}
    <li><b>{{ record.q }}</b>:
    {% if record.id %}
      <a target="_blank"
         href="https://www.kkbox.com/tw/tc/playlist/{{ record.id }}">
        {{ record.title }}
      </a>
    {% else %}
      <span>Found nothing...</span>
    {% endif %}
    </li>
  {% endfor %}
  </ul>
</div>

Let's code 3: Playlist Search

Jinja2 HTML Template - Widget

<div class="d-flex justify-content-center">
  {% if playlist_id %}
    <iframe width="320" height="470" frameborder="0" scrolling="no"
            src="https://widget.kkbox.com/v1/?id={{playlist_id}}&type=playlist&terr=tw&lang=tc&autoplay=true&loop=true">
    </iframe>
  {% endif %}
</div>

Let's code 3: Playlist Search

Flask Application

  • Configurations

    • SECRET_KEY
    • TOKEN_FILE
    • KKBOX_CLIENT_ID
    • KKBOX_CLIENT_SECRET
  • Endpoints
    • GET /
    • GET /?question=<value>

Let's code 3: Playlist Search

Flask Application - Configurations

Let's code 3: Playlist Search

app.config.update(SECRET_KEY=urandom(24),
                  KKBOX_CLIENT_ID='The Client ID',
                  KKBOX_CLIENT_SECRET='The Client Secret',
                  TOKEN_FILE='./token.pkl')

Flask Application - Endpoints

@app.route('/', methods=['GET'])
def index():
    history = session.setdefault('history', list())
    question = request.args.get('question')
    if question:
        token = get_token(app.config['TOKEN_FILE'],
                          app.config['KKBOX_CLIENT_ID'],
                          app.config['KKBOX_CLIENT_SECRET'])
        results = search_playlists(token, question)
        record = {'q': question,
                  'title': results[0]['title'] if results else None,
                  'id': results[0]['id'] if results else None}
        history.append(record)
        # Manually set to True since list.append won't be a update.
        session.modified = True
        return render_template('index.html',
                               search_history=history,
                               playlist_id=record['id'])
    else:
        return render_template('index.html',
                               search_history=history)

Let's code 3: Playlist Search

Summary

  • 探索KKBOX Open API - KKBOX 開發者網站
  • KKBOX Open API 認證機制
  • 使用KKBOX Python SDK
  • 使用KKBOX HTML Widget
  • 在Web app中使用KKBOX Open API

Thank you!

Made with Slides.com