Files
jellyfin-plugin-floatplane/doc/research/reference-sources/kodi_addon.py
claude 9e33a343ab
Some checks failed
release / build-release (push) Has been cancelled
Floatplane channel plugin for Jellyfin 10.10.x: browse, HLS proxy, cookie session, CI release
- 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>
2026-10-25 09:42:27 +01:00

95 lines
2.5 KiB
Python

#!/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()