Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#101: As an engineer, the BunnyHop library can take advantage of the new Bunny.net presigned url uploads for Bunny Stream. #16

Open
wants to merge 37 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
b039f1a
Merge pull request #10 from capless/develop
bjinwright Nov 8, 2020
b3f9d5f
Use capless-docker jupyter
bjinwright Aug 25, 2021
aa69001
Added barebone classes and methods for Stream API
Zuiluj Aug 31, 2021
492aa6e
Completed code flow for StreamCollection class. Also made the StreamC…
Zuiluj Sep 1, 2021
4be6d95
Added streamcollection and stream class in core. Added barebones unit…
Zuiluj Sep 2, 2021
aa070a6
fixed the docker-compose and pyproject files
bjinwright Sep 3, 2021
0e470f8
Added barebone classes and methods for Stream API
Zuiluj Aug 31, 2021
ec3c924
Completed code flow for StreamCollection class. Also made the StreamC…
Zuiluj Sep 1, 2021
9ebf6c4
Added streamcollection and stream class in core. Added barebones unit…
Zuiluj Sep 2, 2021
b8e4548
Merge branch 'feature/bunny_stream' of personal:capless/bunnyhop into…
Zuiluj Sep 3, 2021
55a5927
Refactored StreamCollection to make it work. Commented out __repr__()…
Zuiluj Sep 3, 2021
daf8481
refactored unittests
Zuiluj Sep 3, 2021
7a81fe4
Used mock unit tests instead of tests that calls external api. Commen…
Zuiluj Sep 9, 2021
af57371
Added code for video class in stream.py
Zuiluj Sep 13, 2021
1d2f1d5
Added some docstrings. Added properties in Video class. Fixed import …
Zuiluj Sep 15, 2021
55594a0
Tested Video class and made refactor as necessary.
Zuiluj Sep 16, 2021
ca3dec5
Refactored some docstrings. Finish unit tests for Video class
Zuiluj Sep 17, 2021
99c3a34
Added github actions and pdoc lib
Zuiluj Sep 23, 2021
9defc01
Updated github actions
Zuiluj Sep 23, 2021
1586e64
Updated git actions
Zuiluj Sep 23, 2021
a2bf455
updated git actions
Zuiluj Sep 23, 2021
45d74bd
Updated git actions
Zuiluj Sep 24, 2021
eb60dc5
Updated docs
Zuiluj Sep 27, 2021
9154acd
Update prod.yml
bjinwright Sep 29, 2021
520ad9f
Create dev.yml
bjinwright Sep 29, 2021
6a4250b
Update dev.yml
bjinwright Sep 29, 2021
2eef162
Update dev.yml
bjinwright Sep 29, 2021
ca90b56
Renamed tests
Zuiluj Sep 29, 2021
5ecf6c7
Merge branch 'feature/bunny_stream' of personal:capless/bunnyhop into…
Zuiluj Sep 29, 2021
c261ebc
Configured bunny tests to use mock instead of requesting against the …
Zuiluj Oct 1, 2021
646bce5
Added TUSUpload class in stream API. Added tuspy as package
Zuiluj May 19, 2022
198e27c
refactored docstrings
Zuiluj May 23, 2022
489869a
moved tus upload to video class to be able to work with videos directly
Zuiluj May 24, 2022
e7e07fe
fixed opening of the file and let TUS client use native file opener
Zuiluj May 25, 2022
c6b8941
fixed opening of the file and let TUS client use native file opener
Zuiluj May 25, 2022
08c260c
Fixed presigned signature not using the correct timestamp
Zuiluj May 26, 2022
ba62c60
Fixed generate_presigned_req_sig to also accept api_key params. Added…
Zuiluj May 27, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Python application

on:
push:
branches:
- feature/*

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- run: touch .env
- run: docker-compose pull
# In this step, this action saves a list of existing images,
# the cache is created without them in the post run.
# It also restores the cache if it exists.
- uses: satackey/[email protected]
# Ignore the failure of a step and avoid terminating the job.
continue-on-error: true
- run: docker-compose build

- name: Run Unit Tests
run: docker-compose run web poetry run python -m unittest


55 changes: 55 additions & 0 deletions .github/workflows/prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Python application

on:
push:
branches:
- master

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- run: docker-compose pull
# In this step, this action saves a list of existing images,
# the cache is created without them in the post run.
# It also restores the cache if it exists.
- uses: satackey/[email protected]
# Ignore the failure of a step and avoid terminating the job.
continue-on-error: true
- run: docker-compose build

- name: Run Unit Tests
run: docker-compose run web poetry run python -m unittest

- name: Publish to PyPi
if: github.ref == 'refs/heads/master'
run: docker-compose run web poetry build && docker-compose run web poetry publish --username=${{ secrets.PYPI_USERNAME }} --password=${{ secrets.PYPI_PASSWORD }}

- name: Create pdoc
run: docker-compose run web poetry run python -m pdoc -o ./docs bunnyhop

- uses: shallwefootball/s3-upload-action@master
name: Upload S3
id: S3
with:
aws_key_id: ${{ secrets.AWS_KEY_ID }}
aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY}}
aws_bucket: ${{ secrets.AWS_BUCKET }}
source_dir: 'docs'

- name: Update deployment status (success)
if: success()
uses: chrnorm/deployment-status@releases/v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
target_url: https://${{ secrets.AWS_BUCKET }}.s3-website-us-east-1.amazonaws.com/${{steps.S3.outputs.object_key}}/index.html
state: 'success'
deployment_id: ${{ steps.test.outputs.deployment_id }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ target/

# Jupyter Notebook
.ipynb_checkpoints
*.ipynb

# IPython
profile_default/
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
FROM capless/capless-docker:2
ADD . /code
FROM capless/capless-docker:jupyter
COPY . /code
RUN poetry install
193 changes: 193 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ b.Zone.create_edge_rule(
Triggers = []
)
```
<hr/>
## Storage

### Storage Zones
Expand Down Expand Up @@ -238,6 +239,7 @@ mj['first_name']
# Returns: 'Michael'
```

<hr/>
# Purge

## Create a Purge
Expand All @@ -246,11 +248,13 @@ mj['first_name']
b.Purge.create(url='https://myzone.b-cdn.net/style.css')
```

<hr/>
# Stats

```python
b.Stats.get(dateFrom='2018-12-01', dateTo='2020-01-01', pullZone='example-zone', serverZoneId='serverZoneID')
```
<hr/>
# Billing

#### Get Billing Summary
Expand All @@ -263,3 +267,192 @@ b.Billing.get()
```python
b.Billing.applycode(couponCode='somecode123')
```

<hr/>

# Bunny Stream

1. Acquire first your BunnyNet Stream API key. This Stream API key is different from your own API key.
2. Create a Video Library in your Bunny console and acquire its Library ID by `navigating to it > API > Video Library ID`
3. Initialize BunnyStream yung the two keys you have.


```python
from bunnyhop import BunnyStream
from envs import env
import os


BUNNYCDN_STREAM_API_KEY = '<STREAM_API_KEY>'
BUNNYCDN_STREAM_LIBRARY_KEY = '<LIBRARY_KEY>'


b = BunnyStream(
api_key=BUNNYCDN_STREAM_API_KEY,
library_id=BUNNYCDN_STREAM_LIBRARY_KEY
)
```


# StreamCollection
A `Collection` that can be created in a Bunny's video library.


## List Collection
Lists all of collections available in the given library ID


```python
b.StreamCollection.all(items_per_page=9)
```



## Create Collection
Create a collection. Returns a `StreamCollection` object which you can perform the same operations if you want to not specify the ID


```python
stream_col = b.StreamCollection.create('test')
stream_col.guid
```



## Get a Collection
Acquires a collection based on the GUID you gave. This returns a `StreamCollection` object.


```python
stream_col = b.StreamCollection.get('6d1089a9-0764-4580-a5c9-e26a98fa7812')
```


## Update a Collection
Updates a `StreamCollection` object if it has properties but if not, GUID is required.


```python
stream_col.update('updated')
```


## Delete a Collection
Deletes a collection based on a GUID you provided. If the object is a `StreamCollection` property, it will delete itself if you didn't specified a GUID.


```python
stream_col.delete()
```



# Video
A `Video` that can be created and then uploaded to a collection or just a library.

## Create a Video
Create a `Video` object in Bunny API.

**This is required before running `upload()`**


```python
vid = b.Video.create(title='new_video', collection_id='6d1089a9-0764-4580-a5c9-e26a98fa7812')
```


## Upload a Video
Uploads the Video to Bunny. **`create()` is required before uploading**. If the object is a `Video`-class it will upload itself. Otherwise, provide the GUID of the created video.


```python
import requests

mp4_file = requests.get('https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_1MB.mp4')
vid.upload(mp4_file.content)
# with open(mp4_file, 'rb') as file:
# vid.upload(file)
```


## Get a Video
Acquires a `Video` based on the given GUID.


```python
vid = b.Video.get('fde3acc9-7139-403a-a514-781151e57841')
```


## Fetch a Video
Uploads a video to the specified `video_id` from the URL that was given. Requires `create()` to be called first and use its provided `video_id`


```python
b.Video.fetch(
url='https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_1MB.mp4',
headers={},
video_id='5a32233f-b1fd-41f7-ae41-c49ed714fc67')
```


## List videos
Acquires the list of the videos that are in the current library.

**Notes**

- The minimum `itemsPerPage` is 10, anything below that will be ignored.


```python
b.Video.all(items_per_page=1)
```

## Update a Video
Update a `Video`'s title and collection it belongs. If the object is a `Video` class, it will update itself without needing to provide GUID.


```python
vid.update('updatedTitle', '6d1089a9-0764-4580-a5c9-e26a98fa7812')
```


## Delete a Video
Deletes a `Video` based on its GUID. If the object is an `Video` class, it will delete itself.


```python
vid.delete()
```


## Set Video's thumbnail
Set a `Video`'s thumbnail with the specified image's URL


```python
vid.set_thumbnail(thumbnail_url='https://dummyimage.com/600x400/000/fff.jpg')
```


## Add Captions


```python
import base64
import requests

cc_file = requests.get('https://raw.githubusercontent.com/andreyvit/subtitle-tools/master/sample.srt')
cc_to_upload = base64.b64encode(cc_file.content)

print(cc_to_upload)
vid.add_caption(srclang='en', label='test_label', captions_file=cc_to_upload)
```


## Delete Captions


```python
vid.delete_caption('en')
```
6 changes: 5 additions & 1 deletion bunnyhop/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
from .core import Bunny
"""
.. include:: ../README.md
"""
__docformat__ = "google"
from .core import Bunny, BunnyStream
20 changes: 17 additions & 3 deletions bunnyhop/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ def __init__(self,
self.endpoint_url = endpoint_url
super().__init__(**kwargs)

def __repr__(self):
return '<{class_name}: {uni} >'.format(
class_name=self.__class__.__name__, uni=self.__str__())
# def __repr__(self):
# return '<{class_name}: {uni} >'.format(
# class_name=self.__class__.__name__, uni=self.__str__())

def get_header(self):
header = {
Expand Down Expand Up @@ -79,3 +79,17 @@ def call_storage_api(self, api_url, api_method, header=None, params={}, data={},
return requests.put(self.get_url(api_url, endpoint_url), headers=header, files=files)
return self.call_api(api_url, api_method, header=header, params={}, data=data, json_data=json_data,
endpoint_url=endpoint_url)


class BaseStreamBunny(BaseBunny):
"""
NOTE: The API key for Stream API is different from the bunny.net account API key
Docs:
https://docs.bunny.net/reference/api-overview
"""

endpoint_url = env('BUNNYCDN_STREAM_API_ENDPOINT', 'https://video.bunnycdn.com')

def __init__(self, api_key, library_id=None, endpoint_url=None, **kwargs):
self.library_id = library_id
super().__init__(api_key=api_key, endpoint_url=endpoint_url, **kwargs)
9 changes: 9 additions & 0 deletions bunnyhop/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from bunnyhop.stats import Stats
from bunnyhop.storage import Storage, StorageZone
from bunnyhop.zone import Zone
from bunnyhop.stream import StreamCollection, Video


class Bunny(object):
Expand All @@ -14,3 +15,11 @@ def __init__(self, api_key):
self.StorageZone = StorageZone(api_key)
self.Stats = Stats(api_key)
self.Billing = Billing(api_key)


class BunnyStream(object):
""" """

def __init__(self, api_key, library_id):
self.StreamCollection = StreamCollection(api_key, library_id)
self.Video = Video(api_key, library_id)
Loading