Skip to content

Commit

Permalink
Modified x2
Browse files Browse the repository at this point in the history
  • Loading branch information
noaione committed Mar 16, 2019
0 parents commit d026690
Show file tree
Hide file tree
Showing 11 changed files with 638 additions and 0 deletions.
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.vscode
*.m3u8
*.egg-info
build
dist
__pycache__
.pyc
*.ts
*.mkv
venv
.idea
43 changes: 43 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# yuu - Changelog

[![koito yuu](https://raw.githubusercontent.com/noaione/cdn/gh-pages/i/fzpt7mt.jpg)](https://anilist.co/character/123528/Yuu-Koito)

[![pypi version](https://img.shields.io/pypi/v/yuu.svg?style=for-the-badge)](https://pypi.org/project/yuu/) [![python version](https://img.shields.io/pypi/pyversions/yuu.svg?style=for-the-badge)](#) [![License](https://img.shields.io/github/license/noaione/yuu.svg?style=for-the-badge)](https://github.com/noaione/yuu/blob/master/LICENSE)

#### Version 0.1
- First release

#### Version 0.1.1
- Add proxy mode
- Using session
- Fix some problem

#### Version 0.1.2
- Added more proxy test
- Add verbose
- Fix more problem

#### Version 0.1.2.1
- Proxy test fix

#### Version 0.1.2.2
- Variable change
- More verbose

#### Version 0.1.2.3
- Fixes decryptData() problem (Issue #1)

#### Version 0.1.3
- Fix output override problem

#### Version 0.1.4
- Change webparse() from using beautifulsoup4 to json API
- Rearrange code in `command.py`

#### Version 0.1.4.1
- Added some support for `channel` or `slots` url things
- Make some auto-output-parser thingy for direct m3u8 link
- Cleaning some code

#### Version 0.1.4.2
- Add Illegal/Forbidden character replacer
12 changes: 12 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Copyright (c) 2019, "Aiman Maharana" or "NoAiOne"
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
74 changes: 74 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# (koito) yuu
A simple AbemaTV video downloader in python

[![koito yuu](https://raw.githubusercontent.com/noaione/cdn/gh-pages/i/fzpt7mt.jpg)](https://anilist.co/character/123528/Yuu-Koito)

[![pypi version](https://img.shields.io/pypi/v/yuu.svg?style=for-the-badge)](https://pypi.org/project/yuu/) [![python version](https://img.shields.io/pypi/pyversions/yuu.svg?style=for-the-badge)](#) [![License](https://img.shields.io/github/license/noaione/yuu.svg?style=for-the-badge)](https://github.com/noaione/yuu/blob/master/LICENSE)

## Requirements
- pycryptodome
- Python 3.5+
- m3u8
- tqdm
- Japan connection/proxy/vpn

## Installation
`pip install yuu`

or clone this project and type `pip install .`

## Usage
```
usage: yuu [-h] [--proxies PROXY]
[--resolution {180p,240p,360p,480p,720p,1080p}] [--output OUTPUT]
[--version] [--verbose]
input
A simple AbemaTV video downloader
positional arguments:
input AbemaTV url site or m3u8
optional arguments:
-h, --help show this help message and exit
--proxies PROXY, -p PROXY
Use http(s)/socks5 proxies (please add `socks5://` if
you use socks5)
--resolution {180p,240p,360p,480p,720p,1080p}, -r {180p,240p,360p,480p,720p,1080p}
Resolution (Default: 1080p)
--output OUTPUT, -o OUTPUT
Output filename
--version, -V show program's version number and exit
--verbose, -v Enable verbose
Created by NoAiOne - Version 0.1.4.2
```

- **`--proxies/-p`**: Download using proxy for people outside Japan
- Example: `127.0.0.1:1080`, `http://127.0.0.1:1080`, `http://user:pass@127.0.0.1:1080`, `socks5://127.0.0.1:1080`
- **`--resolution/-r`**: Target resolution
- **`--output/-o`**: Output filename (Automated if there's nothing omitted)
- **`--version/-V`**: Show version number
- **`--verbose/-v`**: Enable verbose/debug mode

**Information: Please use HTTPS proxy for now, it tested and works. SOCKS5 are not tested yet and HTTP doesn't work**

Example command:
- >`yuu https://abema.tv/video/episode/54-25_s1_p1`
Download 1080p video of `Yagate Kimi ni Naru` episode 01
- >`yuu https://abema.tv/video/episode/54-25_s1_p1 -r 480p`
Download 480p video of `Yagate Kimi ni Naru` episode 01
- >`yuu https://ds-vod-abematv.akamaized.net/program/54-25_s1_p1/1080/playlist.m3u8 -o '5toubun01.ts'`
Download 1080p video from m3u8 link
- >`yuu https://abema.tv/video/episode/54-25_s1_p1 -p '127.0.0.1:3128`
Download 480p video of `Yagate Kimi ni Naru` episode 01 using 127.0.0.1:3128 proxy

## Credits
- jackyzy823 (Decryption key fetching method)
- Last-Order ([Minyami](https://github.com/Last-Order/Minyami) author)

*This project are protected by BSD 3-Clause License*
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
requests[socks]
m3u8
tqdm
pycryptodome
40 changes: 40 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import setuptools
from yuu.common import __version__

with open('README.md', 'r') as f:
desc = f.read()

setuptools.setup(
name = 'yuu',
version = __version__,
description = 'Yuu - A simple AbemaTV video downloader',
long_description = desc,
long_description_content_type = "text/markdown",
author = 'noaione',
author_email = 'noaione0809@gmail.com',
keywords = [
'ripping',
'downloader',
'parser'
],
license = 'BSD-3-Clause',
url = 'https://github.com/noaione/yuu',
packages = setuptools.find_packages(),
install_requires = [
'requests[socks]',
'm3u8',
'tqdm',
'pycryptodome'
],
classifiers = [
'Development Status :: 5 - Production/Stable',
'License :: OSI Approved :: BSD License'
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7'
],
entry_points = {
'console_scripts': ['yuu=yuu.command:main']
}
)
2 changes: 2 additions & 0 deletions yuu/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .parser import *
from .downloader import *
115 changes: 115 additions & 0 deletions yuu/command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import argparse
import shutil
import requests

from .downloader import get_video, merge_video
from .parser import webparse, webparse_m3u8, parsem3u8, fetch_video_key, get_auth_token
from .common import __version__

def main():
parser = argparse.ArgumentParser(prog='yuu', description='A simple AbemaTV video downloader', epilog='Created by NoAiOne - Version {v}'.format(v=__version__))
parser.add_argument('--proxies', '-p', required=False, default=None, dest='proxy', help='Use http(s)/socks5 proxies (please add `socks5://` if you use socks5)')
parser.add_argument('--resolution', '-r', required=False, default='1080p', dest='res', choices=['180p', '240p', '360p', '480p', '720p', '1080p'], help='Resolution (Default: 1080p)')
parser.add_argument('--output', '-o', required=False, default=None, dest='output', help='Output filename')
parser.add_argument('--version', '-V', action='version', version='%(prog)s {v} - Created by NoAiOne'.format(v=__version__))
parser.add_argument('--verbose', '-v', action='store_true', help="Enable verbose")
parser.add_argument('input', help='AbemaTV url site or m3u8')

args = parser.parse_args()
print('[INFO] Starting yuu v{ver}...'.format(ver=__version__))

sesi = requests.Session()
if args.proxy:
print('[INFO] Testing proxy')
sesi.proxies = {'http': args.proxy, 'https': args.proxy}
# Somebody tell me how to do recursive test properly
try:
if args.verbose:
print('[DEBUG] Testing http+https mode proxy')
sesi.get('http://httpbin.org/get') # Some test website to check if proxy works or not
pmode = "HTTP+HTTPS/SOCKS5"
except requests.exceptions.RequestException:
if args.verbose:
print('[DEBUG] Failed')
sesi = requests.Session()
sesi.proxies = {'http': args.proxy}
try:
if args.verbose:
print('[DEBUG] Testing http mode proxy')
sesi.get('http://httpbin.org/get') # This too but in https mode
pmode = "HTTP/SOCKS5"
except requests.exceptions.RequestException:
if args.verbose:
print('[DEBUG] Failed')
sesi = requests.Session()
sesi.proxies = {'https': args.proxy} # Final test if it's failed then it will return error
try:
if args.verbose:
print('[DEBUG] Testing https mode proxy')
sesi.get('http://httpbin.org/get')
pmode = "HTTPS/SOCKS5"
except requests.exceptions.RequestException:
if args.verbose:
print('[DEBUG] Failed')
print('[ERROR] Cannot connect to proxy (Request timeout)')
exit(1)
try:
sesi.get('http://httpbin.org/get')
pmode = "No proxy"
except:
print('[ERROR] No connection available to make requests')
exit(1)
if args.verbose:
print('[DEBUG] Using proxy mode: {}'.format(pmode))

print('[INFO] Fetching user token')
authtoken = get_auth_token(sesi, args.verbose)
sesi.headers.update({'Authorization': authtoken[0]})

if args.input[-5:] != '.m3u8':
print('[INFO] Parsing website')
outputtitle, m3u8link = webparse(args.input, args.res, sesi, args.verbose)
print('[INFO] Parsing m3u8')
files, iv, ticket = parsem3u8(m3u8link, sesi, args.verbose)
if args.output:
if args.output[-3:] == '.ts':
output = args.output
else:
output = args.output + '.ts'
else:
output = '{x} (AbemaTV {r}).ts'.format(x=outputtitle, r=args.res)
if args.verbose:
print('[DEBUG] Output file: {}'.format(output))
elif args.input[-5:] == '.m3u8':
print('[INFO] Parsing m3u8')
outputtitle, res = webparse_m3u8(args.input, sesi, args.verbose)
files, iv, ticket = parsem3u8(args.input, sesi, args.verbose)
if args.output:
if args.output[-3:] == '.ts':
output = args.output
else:
output = args.output + '.ts'
else:
output = '{x} (AbemaTV {r}).ts'.format(x=outputtitle, r=res)

# Don't use forbidden/illegal character (replace it with underscore)
illegalchar = ['/', '<', '>', ':', '"', '\\', '|', '?', '*'] # https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file
for char in illegalchar:
output = output.replace(char, '_')

print('[INFO] Fetching video key')
getkey = fetch_video_key(ticket, authtoken, sesi, args.verbose)

print('[INFO][DOWN] Starting downloader...')
dllist, tempdir = get_video(files, getkey, iv, sesi, args.verbose)
print('[INFO][DOWN] Finished downloading')
print('[INFO] Merging video')
merge_video(dllist, output)
print('[INFO] Finished merging')

print('[INFO] Cleaning up')
shutil.rmtree(tempdir)
exit(0)

if __name__=='__main__':
main()
27 changes: 27 additions & 0 deletions yuu/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import re

__version__ = '0.1.4.2'

_STRTABLE = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
_HKEY = b"3AF0298C219469522A313570E8583005A642E73EDD58E3EA2FB7339D3DF1597E"

_KEYPARAMS = {
"osName": "android",
"osVersion": "6.0.1",
"osLand": "ja_JP",
"osTimezone": "Asia/Tokyo",
"appId": "tv.abema",
"appVersion": "3.27.1"
}

_MEDIATOKEN_API = "https://api.abema.io/v1/media/token"
_LICENSE_API = "https://license.abema.io/abematv-hls"
_USERAPI = "https://api.abema.io/v1/users"
_PROGRAMAPI = 'https://api.abema.io/v1/video/programs/'
_CHANNELAPI = 'https://api.abema.io/v1/media/slots/'

def is_channel(url):
url = re.findall('(slots)', url)
if not url:
return True
return False
Loading

0 comments on commit d026690

Please sign in to comment.