Floatplane channel plugin for Jellyfin 10.10.x: browse, HLS proxy, cookie session, CI release
Some checks failed
release / build-release (push) Has been cancelled
Some checks failed
release / build-release (push) Has been cancelled
- IChannel: creators -> sub-channels -> video posts (public listing API, search mapped) - IRequiresMediaInfoCallback -> signed proxy URLs; stream controller rewrites HLS so the AES-128 key is fetched server-side with the session cookie, segments stay on the CDN - delivery/info cached per token lifetime and throttled (~2/min) - config page: paste sails.sid + validate, extra creators, quality, search test - scripts/package.sh -> plugin folder + zip + md5 + manifest.json; Gitea release workflow - research docs, HAR analysis (HAR itself gitignored), install-pi5.sh Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
94
doc/research/reference-sources/kodi_addon.py
Normal file
94
doc/research/reference-sources/kodi_addon.py
Normal file
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/python
|
||||
'''
|
||||
plugin.video.floatplaneclub
|
||||
Copyright (C) 2017 MrWolvetech
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
'''
|
||||
|
||||
from xbmcswift2 import Plugin
|
||||
import resources.lib.scraper as scraper
|
||||
|
||||
plugin = Plugin()
|
||||
|
||||
|
||||
@plugin.route('/')
|
||||
def show_page1():
|
||||
videos, has_next_page = scraper.get_videos()
|
||||
items = [{
|
||||
'label': video['title'],
|
||||
'thumbnail': video['thumbnail'],
|
||||
'info': {
|
||||
'originaltitle': video['title']
|
||||
},
|
||||
'path': plugin.url_for(
|
||||
endpoint='watch_video',
|
||||
video_id=video['guid']
|
||||
),
|
||||
'is_playable': True,
|
||||
} for video in videos]
|
||||
if has_next_page:
|
||||
next_page = str(2)
|
||||
items.append({
|
||||
'label': '>> Page %s >>' % (
|
||||
next_page
|
||||
),
|
||||
'path': plugin.url_for(
|
||||
endpoint='show_page',
|
||||
page=next_page
|
||||
),
|
||||
})
|
||||
return plugin.finish(items)
|
||||
|
||||
|
||||
@plugin.route('/<page>/')
|
||||
def show_page(page):
|
||||
if page is 1:
|
||||
return show_page1()
|
||||
|
||||
videos, has_next_page = scraper.get_videos(page)
|
||||
items = [{
|
||||
'label': video['title'],
|
||||
'thumbnail': video['thumbnail'],
|
||||
'info': {
|
||||
'originaltitle': video['title']
|
||||
},
|
||||
'path': plugin.url_for(
|
||||
endpoint='watch_video',
|
||||
video_id=video['url']
|
||||
),
|
||||
'is_playable': True,
|
||||
} for video in videos]
|
||||
if has_next_page:
|
||||
next_page = str(int(page) + 1)
|
||||
items.append({
|
||||
'label': '>> page %s >>' % (
|
||||
next_page
|
||||
),
|
||||
'path': plugin.url_for(
|
||||
endpoint='show_page',
|
||||
page=next_page
|
||||
),
|
||||
})
|
||||
return plugin.finish(items)
|
||||
|
||||
|
||||
@plugin.route('/watch/<video_id>/')
|
||||
def watch_video(video_id):
|
||||
video_url = scraper.get_video_url(video_id)
|
||||
return plugin.set_resolved_url(video_url)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
plugin.run()
|
||||
Reference in New Issue
Block a user