mirror of
https://github.com/ihabunek/twitch-dl
synced 2024-08-30 18:32:25 +00:00
Add testing on github
This commit is contained in:
parent
ad0f0d2a41
commit
900914377b
27
.github/workflows/test.yml
vendored
Normal file
27
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
name: Run tests
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install -e ".[test]"
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
pytest
|
||||||
|
- name: Validate minimum required version
|
||||||
|
run: |
|
||||||
|
vermin --no-tips twitchdl
|
@ -43,6 +43,11 @@ dev = [
|
|||||||
"vermin",
|
"vermin",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
test = [
|
||||||
|
"pytest",
|
||||||
|
"vermin",
|
||||||
|
]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
"Homepage" = "https://twitch-dl.bezdomni.net/"
|
"Homepage" = "https://twitch-dl.bezdomni.net/"
|
||||||
"Source" = "https://github.com/ihabunek/twitch-dl"
|
"Source" = "https://github.com/ihabunek/twitch-dl"
|
||||||
|
@ -2,7 +2,7 @@ import logging
|
|||||||
import platform
|
import platform
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from typing import Optional
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
@ -255,7 +255,7 @@ def clips(
|
|||||||
default=5,
|
default=5,
|
||||||
)
|
)
|
||||||
def download(
|
def download(
|
||||||
ids: tuple[str, ...],
|
ids: Tuple[str, ...],
|
||||||
auth_token: Optional[str],
|
auth_token: Optional[str],
|
||||||
chapter: Optional[int],
|
chapter: Optional[int],
|
||||||
concat: bool,
|
concat: bool,
|
||||||
@ -374,7 +374,7 @@ def videos(
|
|||||||
channel_name: str,
|
channel_name: str,
|
||||||
all: bool,
|
all: bool,
|
||||||
compact: bool,
|
compact: bool,
|
||||||
games_tuple: tuple[str, ...],
|
games_tuple: Tuple[str, ...],
|
||||||
json: bool,
|
json: bool,
|
||||||
limit: Optional[int],
|
limit: Optional[int],
|
||||||
pager: Optional[int],
|
pager: Optional[int],
|
||||||
|
@ -7,6 +7,7 @@ from twitchdl import twitch, utils
|
|||||||
from twitchdl.commands.download import get_video_placeholders
|
from twitchdl.commands.download import get_video_placeholders
|
||||||
from twitchdl.exceptions import ConsoleError
|
from twitchdl.exceptions import ConsoleError
|
||||||
from twitchdl.output import bold, print_clip, print_json, print_log, print_table, print_video
|
from twitchdl.output import bold, print_clip, print_json, print_log, print_table, print_video
|
||||||
|
from twitchdl.playlists import parse_playlists
|
||||||
from twitchdl.twitch import Chapter, Clip, Video
|
from twitchdl.twitch import Chapter, Clip, Video
|
||||||
|
|
||||||
|
|
||||||
@ -50,13 +51,13 @@ def info(id: str, *, json: bool = False):
|
|||||||
raise ConsoleError(f"Invalid input: {id}")
|
raise ConsoleError(f"Invalid input: {id}")
|
||||||
|
|
||||||
|
|
||||||
def video_info(video: Video, playlists, chapters: List[Chapter]):
|
def video_info(video: Video, playlists: str, chapters: List[Chapter]):
|
||||||
click.echo()
|
click.echo()
|
||||||
print_video(video)
|
print_video(video)
|
||||||
|
|
||||||
click.echo("Playlists:")
|
click.echo("Playlists:")
|
||||||
for p in m3u8.loads(playlists).playlists:
|
for p in parse_playlists(playlists):
|
||||||
click.echo(f"{bold(p.stream_info.video)} {p.uri}")
|
click.echo(f"{bold(p.name)} {p.url}")
|
||||||
|
|
||||||
if chapters:
|
if chapters:
|
||||||
click.echo()
|
click.echo()
|
||||||
@ -72,7 +73,7 @@ def video_info(video: Video, playlists, chapters: List[Chapter]):
|
|||||||
print_table(["Placeholder", "Value"], placeholders)
|
print_table(["Placeholder", "Value"], placeholders)
|
||||||
|
|
||||||
|
|
||||||
def video_json(video, playlists, chapters):
|
def video_json(video: Video, playlists: str, chapters: List[Chapter]):
|
||||||
playlists = m3u8.loads(playlists).playlists
|
playlists = m3u8.loads(playlists).playlists
|
||||||
|
|
||||||
video["playlists"] = [
|
video["playlists"] = [
|
||||||
|
@ -29,11 +29,15 @@ class Vod:
|
|||||||
"""Segment duration in seconds"""
|
"""Segment duration in seconds"""
|
||||||
|
|
||||||
|
|
||||||
def parse_playlists(playlists_m3u8: str):
|
def parse_playlists(playlists_m3u8: str) -> List[Playlist]:
|
||||||
def _parse(source: str) -> Generator[Playlist, None, None]:
|
def _parse(source: str) -> Generator[Playlist, None, None]:
|
||||||
document = load_m3u8(source)
|
document = load_m3u8(source)
|
||||||
|
|
||||||
for p in document.playlists:
|
for p in document.playlists:
|
||||||
|
from pprint import pp
|
||||||
|
|
||||||
|
pp(p.__dict__)
|
||||||
|
pp(p.stream_info.__dict__)
|
||||||
if p.stream_info.resolution:
|
if p.stream_info.resolution:
|
||||||
name = p.media[0].name
|
name = p.media[0].name
|
||||||
resolution = "x".join(str(r) for r in p.stream_info.resolution)
|
resolution = "x".join(str(r) for r in p.stream_info.resolution)
|
||||||
|
@ -3,7 +3,7 @@ Twitch API access.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from typing import Dict, Generator, List, Literal, Mapping, Optional, TypedDict
|
from typing import Dict, Generator, List, Literal, Mapping, Optional, Tuple, TypedDict
|
||||||
|
|
||||||
import click
|
import click
|
||||||
import httpx
|
import httpx
|
||||||
@ -345,7 +345,7 @@ def channel_videos_generator(
|
|||||||
sort: VideosSort,
|
sort: VideosSort,
|
||||||
type: VideosType,
|
type: VideosType,
|
||||||
game_ids: Optional[List[str]] = None,
|
game_ids: Optional[List[str]] = None,
|
||||||
) -> tuple[int, Generator[Video, None, None]]:
|
) -> Tuple[int, Generator[Video, None, None]]:
|
||||||
game_ids = game_ids or []
|
game_ids = game_ids or []
|
||||||
|
|
||||||
def _generator(videos: Data, max_videos: int) -> Generator[Video, None, None]:
|
def _generator(videos: Data, max_videos: int) -> Generator[Video, None, None]:
|
||||||
|
203
video_before.json
Normal file
203
video_before.json
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
{
|
||||||
|
"id": "2115833882",
|
||||||
|
"title": "Games I Feel Like Speedrun Marathon !newyt !Slender !Merch",
|
||||||
|
"description": null,
|
||||||
|
"publishedAt": "2024-04-10T05:01:12Z",
|
||||||
|
"broadcastType": "ARCHIVE",
|
||||||
|
"lengthSeconds": 30163,
|
||||||
|
"game": {
|
||||||
|
"id": "21063",
|
||||||
|
"name": "Saw"
|
||||||
|
},
|
||||||
|
"creator": {
|
||||||
|
"login": "ecdycis",
|
||||||
|
"displayName": "Ecdycis"
|
||||||
|
},
|
||||||
|
"playlists": [
|
||||||
|
{
|
||||||
|
"bandwidth": 6395968,
|
||||||
|
"resolution": [
|
||||||
|
1920,
|
||||||
|
1080
|
||||||
|
],
|
||||||
|
"codecs": "avc1.64002A,mp4a.40.2",
|
||||||
|
"video": "chunked",
|
||||||
|
"uri": "https://d2nvs31859zcd8.cloudfront.net/3104d817048588f268fa_ecdycis_43996750059_1712725267/chunked/index-muted-GKGIUOE29B.m3u8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bandwidth": 3430341,
|
||||||
|
"resolution": [
|
||||||
|
1280,
|
||||||
|
720
|
||||||
|
],
|
||||||
|
"codecs": "avc1.4D0020,mp4a.40.2",
|
||||||
|
"video": "720p60",
|
||||||
|
"uri": "https://d2nvs31859zcd8.cloudfront.net/3104d817048588f268fa_ecdycis_43996750059_1712725267/720p60/index-muted-GKGIUOE29B.m3u8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bandwidth": 1448243,
|
||||||
|
"resolution": [
|
||||||
|
852,
|
||||||
|
480
|
||||||
|
],
|
||||||
|
"codecs": "avc1.4D001F,mp4a.40.2",
|
||||||
|
"video": "480p30",
|
||||||
|
"uri": "https://d2nvs31859zcd8.cloudfront.net/3104d817048588f268fa_ecdycis_43996750059_1712725267/480p30/index-muted-GKGIUOE29B.m3u8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bandwidth": 215544,
|
||||||
|
"resolution": null,
|
||||||
|
"codecs": "mp4a.40.2",
|
||||||
|
"video": "audio_only",
|
||||||
|
"uri": "https://d2nvs31859zcd8.cloudfront.net/3104d817048588f268fa_ecdycis_43996750059_1712725267/audio_only/index-muted-GKGIUOE29B.m3u8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bandwidth": 709051,
|
||||||
|
"resolution": [
|
||||||
|
640,
|
||||||
|
360
|
||||||
|
],
|
||||||
|
"codecs": "avc1.4D001E,mp4a.40.2",
|
||||||
|
"video": "360p30",
|
||||||
|
"uri": "https://d2nvs31859zcd8.cloudfront.net/3104d817048588f268fa_ecdycis_43996750059_1712725267/360p30/index-muted-GKGIUOE29B.m3u8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bandwidth": 288844,
|
||||||
|
"resolution": [
|
||||||
|
284,
|
||||||
|
160
|
||||||
|
],
|
||||||
|
"codecs": "avc1.4D000C,mp4a.40.2",
|
||||||
|
"video": "160p30",
|
||||||
|
"uri": "https://d2nvs31859zcd8.cloudfront.net/3104d817048588f268fa_ecdycis_43996750059_1712725267/160p30/index-muted-GKGIUOE29B.m3u8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"chapters": [
|
||||||
|
{
|
||||||
|
"id": "6049e803f47a0b9cf64ce95adcf3d96d",
|
||||||
|
"durationMilliseconds": 4891000,
|
||||||
|
"positionMilliseconds": 0,
|
||||||
|
"type": "GAME_CHANGE",
|
||||||
|
"description": "Saw",
|
||||||
|
"subDescription": "",
|
||||||
|
"thumbnailURL": "",
|
||||||
|
"video": {
|
||||||
|
"id": "2115833882",
|
||||||
|
"lengthSeconds": 30163,
|
||||||
|
"__typename": "Video"
|
||||||
|
},
|
||||||
|
"__typename": "VideoMoment",
|
||||||
|
"game": {
|
||||||
|
"id": "21063",
|
||||||
|
"displayName": "Saw",
|
||||||
|
"boxArtURL": "https://static-cdn.jtvnw.net/ttv-boxart/21063_IGDB-40x53.jpg",
|
||||||
|
"__typename": "Game"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c3be4ce5a7bae802d7a6df02baf62a85",
|
||||||
|
"durationMilliseconds": 6235000,
|
||||||
|
"positionMilliseconds": 4891000,
|
||||||
|
"type": "GAME_CHANGE",
|
||||||
|
"description": "Resident Evil 7: Biohazard",
|
||||||
|
"subDescription": "",
|
||||||
|
"thumbnailURL": "",
|
||||||
|
"video": {
|
||||||
|
"id": "2115833882",
|
||||||
|
"lengthSeconds": 30163,
|
||||||
|
"__typename": "Video"
|
||||||
|
},
|
||||||
|
"__typename": "VideoMoment",
|
||||||
|
"game": {
|
||||||
|
"id": "492934",
|
||||||
|
"displayName": "Resident Evil 7: Biohazard",
|
||||||
|
"boxArtURL": "https://static-cdn.jtvnw.net/ttv-boxart/492934_IGDB-40x53.jpg",
|
||||||
|
"__typename": "Game"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bd584f17bc1f91fc11ed14dcec5e4742",
|
||||||
|
"durationMilliseconds": 3401000,
|
||||||
|
"positionMilliseconds": 11126000,
|
||||||
|
"type": "GAME_CHANGE",
|
||||||
|
"description": "Silent Hill: Homecoming",
|
||||||
|
"subDescription": "",
|
||||||
|
"thumbnailURL": "",
|
||||||
|
"video": {
|
||||||
|
"id": "2115833882",
|
||||||
|
"lengthSeconds": 30163,
|
||||||
|
"__typename": "Video"
|
||||||
|
},
|
||||||
|
"__typename": "VideoMoment",
|
||||||
|
"game": {
|
||||||
|
"id": "18864",
|
||||||
|
"displayName": "Silent Hill: Homecoming",
|
||||||
|
"boxArtURL": "https://static-cdn.jtvnw.net/ttv-boxart/18864_IGDB-40x53.jpg",
|
||||||
|
"__typename": "Game"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9f4be8bc7b5bf398213bc602a4b39c4d",
|
||||||
|
"durationMilliseconds": 3490000,
|
||||||
|
"positionMilliseconds": 14527000,
|
||||||
|
"type": "GAME_CHANGE",
|
||||||
|
"description": "Silent Hill 2",
|
||||||
|
"subDescription": "",
|
||||||
|
"thumbnailURL": "",
|
||||||
|
"video": {
|
||||||
|
"id": "2115833882",
|
||||||
|
"lengthSeconds": 30163,
|
||||||
|
"__typename": "Video"
|
||||||
|
},
|
||||||
|
"__typename": "VideoMoment",
|
||||||
|
"game": {
|
||||||
|
"id": "9891",
|
||||||
|
"displayName": "Silent Hill 2",
|
||||||
|
"boxArtURL": "https://static-cdn.jtvnw.net/ttv-boxart/9891_IGDB-40x53.jpg",
|
||||||
|
"__typename": "Game"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "7a42d537681decc10660c33f9071a37f",
|
||||||
|
"durationMilliseconds": 7241000,
|
||||||
|
"positionMilliseconds": 18017000,
|
||||||
|
"type": "GAME_CHANGE",
|
||||||
|
"description": "The Darkness",
|
||||||
|
"subDescription": "",
|
||||||
|
"thumbnailURL": "",
|
||||||
|
"video": {
|
||||||
|
"id": "2115833882",
|
||||||
|
"lengthSeconds": 30163,
|
||||||
|
"__typename": "Video"
|
||||||
|
},
|
||||||
|
"__typename": "VideoMoment",
|
||||||
|
"game": {
|
||||||
|
"id": "8448",
|
||||||
|
"displayName": "The Darkness",
|
||||||
|
"boxArtURL": "https://static-cdn.jtvnw.net/ttv-boxart/8448_IGDB-40x53.jpg",
|
||||||
|
"__typename": "Game"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "64418186f436d80b1359d2e6686222ef",
|
||||||
|
"durationMilliseconds": 4905000,
|
||||||
|
"positionMilliseconds": 25258000,
|
||||||
|
"type": "GAME_CHANGE",
|
||||||
|
"description": "Silent Hill 4: The Room",
|
||||||
|
"subDescription": "",
|
||||||
|
"thumbnailURL": "",
|
||||||
|
"video": {
|
||||||
|
"id": "2115833882",
|
||||||
|
"lengthSeconds": 30163,
|
||||||
|
"__typename": "Video"
|
||||||
|
},
|
||||||
|
"__typename": "VideoMoment",
|
||||||
|
"game": {
|
||||||
|
"id": "5804",
|
||||||
|
"displayName": "Silent Hill 4: The Room",
|
||||||
|
"boxArtURL": "https://static-cdn.jtvnw.net/ttv-boxart/5804_IGDB-40x53.jpg",
|
||||||
|
"__typename": "Game"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user