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

[plugin.video.piped] 1.0.1 #4516

Merged
merged 1 commit into from
May 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions plugin.video.piped/addon.xml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.piped" name="Piped" version="1.0.0" provider-name="Syhlx">
<addon id="plugin.video.piped" name="Piped" version="1.0.1" provider-name="Syhlx">
<requires>
<import addon="xbmc.python" version="3.0.0" />
<import addon="inputstream.adaptive" version="19.0.0" />
<import addon="script.module.requests" version="2.27.0" />
</requires>
<extension point="xbmc.service" library="service.py" />
<extension point="xbmc.python.pluginsource" library="default.py">
<provides>video</provides>
</extension>
<extension point="xbmc.service" library="service.py" />
<extension point="xbmc.addon.metadata">
<summary lang="en_GB">Piped Addon for Kodi</summary>
<description lang="en_GB">An addon which allows you to access any Piped instance, login and manage your playlists and watch history</description>
Expand Down
29 changes: 26 additions & 3 deletions plugin.video.piped/lib/authentication.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from xbmcaddon import Addon
from xbmcgui import Dialog
import requests

def get_auth_token() -> str:
def get_auth_token(force_reauth: bool=False) -> str:
addon = Addon()
if not addon.getSettingBool('use_login'): return ''

if len(addon.getSettingString('auth_token')) > 0:
if not force_reauth and len(addon.getSettingString('auth_token')) > 0:
return addon.getSettingString('auth_token')
else:
error_msg: str = ''
Expand All @@ -23,4 +24,26 @@ def get_auth_token() -> str:
return auth_token
except:
Dialog().ok(addon.getLocalizedString(30016), error_msg)
return ''
return ''

def authenticated_request(path: str, append_token: bool=False) -> dict:
addon = Addon()
instance: str = addon.getSettingString('instance')
force_reauth: bool = False

result: dict = dict()

for _ in range(2):
auth_token: str = get_auth_token(force_reauth)

url: str = f'{instance}{path}'
if append_token: url = f'{url}{auth_token}'

result = requests.get(url, headers={'Authorization': auth_token}).json()

if 'error' in result: force_reauth = True
else: return result

Dialog().ok(addon.getLocalizedString(30016), str(result))

return result
26 changes: 21 additions & 5 deletions plugin.video.piped/lib/dash.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,20 @@
import xbmc
from xbmcaddon import Addon

class AutoIncrement():
def __init__(self):
self.i: int = 0

def gen(self):
self.i += 1
return self.i

def generate_dash(video_id: str) -> str:
addon = Addon()
resp = get(f'{addon.getSettingString("instance")}/streams/{video_id}').json()

autoincrement = AutoIncrement()

streams: dict = dict(
audio = dict(),
video = dict(),
Expand All @@ -15,9 +25,15 @@ def generate_dash(video_id: str) -> str:
prefer_original_lang: bool = addon.getSettingBool("audio_prefer_original_lang")
preferred_lang: str = addon.getSettingString("audio_custom_lang") if addon.getSettingString("audio_custom_lang") and not addon.getSettingBool("audio_prefer_kodi_lang") else xbmc.getLanguage(xbmc.ISO_639_1)

streams['video']['null']: dict = dict() # Video stream do not have a language
if len(addon.getSettingString("video_codec_priority")) > 1:
for codec in addon.getSettingString("video_codec_priority").split(','): streams['video']['null'][codec]: list = list()

for stream in resp["audioStreams"] + resp["videoStreams"]:
media_lang = stream["audioTrackLocale"] if stream["audioTrackLocale"] is not None else 'null'
media_type, media_format = stream["mimeType"].split("/")
media_type, _ = stream["mimeType"].split("/")
if 'codec' not in stream or not isinstance(stream["codec"], str): continue
media_format = stream["codec"][:4] if len(stream["codec"]) > 4 else stream["codec"]
if media_type in ['audio', 'video'] and "googlevideo" in stream["url"]:
if media_type == 'audio' and ((prefer_original_lang and stream["audioTrackType"] == "ORIGINAL") or (not prefer_original_lang and media_lang[:2] == preferred_lang)):
if not default_audio.__contains__(media_lang): default_audio[media_lang]: dict = dict()
Expand All @@ -39,7 +55,7 @@ def generate_dash(video_id: str) -> str:
for subtitle in resp["subtitles"]:
mpd += f'<AdaptationSet contentType="text" mimeType="{subtitle["mimeType"]}" segmentAlignment="true" lang="{subtitle["code"]}">'
mpd += '<Role schemeIdUri="urn:mpeg:dash:role:2011" value="subtitle"/>'
mpd += f'<Representation id="caption_{subtitle["code"]}{"_auto" if subtitle["autoGenerated"] else ""}" bandwidth="256">'
mpd += f'<Representation id="caption_{subtitle["code"]}{"_auto" if subtitle["autoGenerated"] else ""}_{autoincrement.gen()}" bandwidth="256">'
mpd += f'<BaseURL>{subtitle["url"].replace("&", "&amp;")}</BaseURL>'
mpd += '</Representation></AdaptationSet>'

Expand All @@ -49,7 +65,7 @@ def generate_dash(video_id: str) -> str:
stream_xml: str = ''
for stream in streams[stream_type][stream_lang][stream_format]:
if stream["initEnd"] > 0:
stream_xml += f'<Representation id="{stream["itag"]}" codecs="{stream["codec"]}" bandwidth="{stream["bitrate"]}"'
stream_xml += f'<Representation id="{stream["itag"]}_{autoincrement.gen()}" codecs="{stream["codec"]}" bandwidth="{stream["bitrate"]}"'
if stream_type == 'audio':
stream_xml += '><AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>'
elif stream_type == 'video':
Expand All @@ -60,11 +76,11 @@ def generate_dash(video_id: str) -> str:
stream_xml += '</SegmentBase></Representation>'

if len(stream_xml) > 0:
mpd += f'<AdaptationSet mimeType="{stream_type}/{stream_format}" startWithSAP="1" subsegmentAlignment="true"'
mpd += f'<AdaptationSet mimeType="{stream["mimeType"]}" startWithSAP="1" subsegmentAlignment="true"'
mpd += ' scanType="progressive">' if stream_type == 'video' else f' lang="{stream_lang}">'
mpd += stream_xml
mpd += f'</AdaptationSet>'

mpd += '</Period></MPD>'

return mpd
return mpd
21 changes: 5 additions & 16 deletions plugin.video.piped/lib/sections.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import xbmcgui
import xbmcplugin

from lib.authentication import get_auth_token
from lib.authentication import authenticated_request
from lib.history import set_watch_history, mark_as_watched, mark_as_unwatched
from lib.utils import get_component, human_format

Expand Down Expand Up @@ -94,12 +94,7 @@ def list_videos(videos: list, hide_watched: bool=False, nextpage: str='') -> Non
xbmcplugin.endOfDirectory(addon_handle)

def feed() -> None:
instance: str = addon.getSettingString('instance')
auth_token = get_auth_token()

videos = get(f'{instance}/feed?authToken={auth_token}').json()

list_videos(videos, addon.getSettingBool('watch_history_hide_watched_feed'))
list_videos(authenticated_request('/feed?authToken=', True), addon.getSettingBool('watch_history_hide_watched_feed'))

def list_channels(channels: list, nextpage: str='') -> None:
for channel in channels:
Expand Down Expand Up @@ -130,17 +125,14 @@ def list_channels(channels: list, nextpage: str='') -> None:
xbmcplugin.endOfDirectory(addon_handle)

def subscriptions() -> None:
instance: str = addon.getSettingString('instance')
auth_token = get_auth_token()

list_channels(get(f'{instance}/subscriptions', headers={'Authorization': auth_token}).json())
list_channels(authenticated_request('/subscriptions'))

def list_playlists(playlists: list, nextpage: str='') -> None:
for playlist in playlists:
info: str = ''
if 'shortDescription' in playlist and playlist['shortDescription'] is not None: info += playlist['shortDescription'] + "\n\n"
elif 'description' in playlist and playlist['description'] is not None: info += playlist['description'] + "\n\n"
info += f"{addon.getLocalizedString(30018)}: {playlist['videos']}"
if 'videos' in playlist and playlist['videos'] is not None: info += f"{addon.getLocalizedString(30018)}: {playlist['videos']}"

if 'id' not in playlist:
playlist['id'] = get_component(playlist['url'])['params']['list']
Expand All @@ -163,10 +155,7 @@ def list_playlists(playlists: list, nextpage: str='') -> None:
xbmcplugin.endOfDirectory(addon_handle)

def playlists() -> None:
instance: str = addon.getSettingString('instance')
auth_token = get_auth_token()

list_playlists(get(f'{instance}/user/playlists', headers={'Authorization': auth_token}).json())
list_playlists(authenticated_request('/user/playlists'))

def playlist(playlist_id: str, hide_watched=None) -> None:
instance: str = addon.getSettingString('instance')
Expand Down
25 changes: 12 additions & 13 deletions plugin.video.piped/lib/services/httpserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,23 @@
from lib.dash import generate_dash

class HttpRequestHandler(SimpleHTTPRequestHandler):
def do_GET(self):
def gen_headers(self, content_length: int = 0):
if self.path.startswith('/watch?v='):
self.send_response(200)
self.send_header('Content type', 'application/dash+xml')
self.send_response(200, 'OK')
if content_length > 0: self.send_header('Content-Length', str(content_length))
self.send_header('Content-Type', 'application/dash+xml; charset=utf-8')
self.send_header("Connection", "close")
self.end_headers()
self.wfile.write(generate_dash(parse_qsl(self.path)[0][1]).encode('utf-8'))
else:
self.send_error(404, "File not Found")

def do_GET(self):
content = generate_dash(parse_qsl(self.path)[0][1]).encode('utf-8')
self.gen_headers(len(content))
self.wfile.write(content)

def do_HEAD(self):
if self.path.startswith('/watch?v='):
self.send_response(200)
self.send_header('Content type', 'application/dash+xml')
self.end_headers()
else:
self.send_error(404, "File not Found")

return
self.gen_headers()

class HttpService(Thread):
def __init__(self):
Expand All @@ -39,4 +38,4 @@ def run(self):

def stop(self, timeout=1):
if self.httpd is not None: self.httpd.shutdown()
self.join(timeout)
self.join(timeout)
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,6 @@ msgctxt "#30019"
msgid "Channels"
msgstr ""

msgctxt "#3001"
msgid ""
msgstr ""

msgctxt "#30100"
msgid "Instance"
msgstr ""
Expand Down Expand Up @@ -185,3 +181,7 @@ msgstr ""
msgctxt "#30404"
msgid "Load subtitles"
msgstr ""

msgctxt "#30405"
msgid "Video codec priority"
msgstr ""
Loading
Loading