Add testing on github

This commit is contained in:
Ivan Habunek 2024-04-24 07:55:43 +02:00
parent ad0f0d2a41
commit 900914377b
No known key found for this signature in database
GPG Key ID: F5F0623FF5EBCB3D
7 changed files with 250 additions and 10 deletions

27
.github/workflows/test.yml vendored Normal file
View 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

View File

@ -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"

View File

@ -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],

View File

@ -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"] = [

View File

@ -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)

View File

@ -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
View 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"
}
}
]
}