mirror of
https://github.com/steredbits/pelagia.git
synced 2026-10-24 14:02:15 +02:00
152 lines
6.9 KiB
C++
152 lines
6.9 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// Copyright (C) 2026 steredbits and Pelagia contributors
|
|
// Tests of Jellyfin URL construction (pure functions, no network).
|
|
|
|
#include "api/jellyfin_urls.h"
|
|
|
|
#include <string>
|
|
|
|
#include "test_framework.h"
|
|
|
|
namespace {
|
|
|
|
bool contains(const std::string& haystack, const std::string& needle) {
|
|
return haystack.find(needle) != std::string::npos;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
// Encodage URL.
|
|
CHECK_EQ(api::url_encode("abc-123_~.ok"), "abc-123_~.ok");
|
|
CHECK_EQ(api::url_encode("a b&c/é"), "a%20b%26c%2F%C3%A9");
|
|
|
|
// Normalization of the server URL.
|
|
CHECK_EQ(api::normalize_server_url("http://10.0.0.2:8096/"), "http://10.0.0.2:8096");
|
|
CHECK_EQ(api::normalize_server_url("http://10.0.0.2:8096"), "http://10.0.0.2:8096");
|
|
|
|
// Auth and views.
|
|
CHECK_EQ(api::build_auth_url("http://srv:8096/"), "http://srv:8096/Users/AuthenticateByName");
|
|
CHECK_EQ(api::build_views_url("http://srv:8096", "u1"), "http://srv:8096/Users/u1/Views");
|
|
|
|
// Items: optional parameters.
|
|
api::ItemsQuery query;
|
|
query.parent_id = "lib42";
|
|
query.include_item_types = "Movie";
|
|
query.recursive = true;
|
|
std::string items = api::build_items_url("http://srv:8096", "u1", query);
|
|
CHECK(contains(items, "http://srv:8096/Users/u1/Items?"));
|
|
CHECK(contains(items, "parentId=lib42"));
|
|
CHECK(contains(items, "includeItemTypes=Movie"));
|
|
CHECK(contains(items, "recursive=true"));
|
|
|
|
api::ItemsQuery empty_query;
|
|
std::string items_plain = api::build_items_url("http://srv:8096", "u1", empty_query);
|
|
CHECK(!contains(items_plain, "parentId"));
|
|
CHECK(!contains(items_plain, "includeItemTypes"));
|
|
CHECK(!contains(items_plain, "recursive"));
|
|
// collapseBoxSetItems: absent by default, sent only if set.
|
|
CHECK(!contains(items_plain, "collapseBoxSetItems"));
|
|
api::ItemsQuery no_boxsets;
|
|
no_boxsets.collapse_box_set_items = false;
|
|
CHECK(contains(api::build_items_url("http://srv:8096", "u1", no_boxsets),
|
|
"collapseBoxSetItems=false"));
|
|
api::ItemsQuery with_boxsets;
|
|
with_boxsets.collapse_box_set_items = true;
|
|
CHECK(contains(api::build_items_url("http://srv:8096", "u1", with_boxsets),
|
|
"collapseBoxSetItems=true"));
|
|
|
|
// Seasons / episodes.
|
|
CHECK_EQ(api::build_seasons_url("http://srv:8096", "serie1", "u1"),
|
|
"http://srv:8096/Shows/serie1/Seasons?userId=u1");
|
|
std::string episodes = api::build_episodes_url("http://srv:8096", "serie1", "s2", "u1");
|
|
CHECK(contains(episodes, "/Shows/serie1/Episodes?userId=u1"));
|
|
CHECK(contains(episodes, "seasonId=s2"));
|
|
|
|
// Image : tag optionnel.
|
|
std::string image = api::build_image_url("http://srv:8096", "item9", "tag123", 400);
|
|
CHECK(contains(image, "/Items/item9/Images/Primary?maxWidth=400"));
|
|
CHECK(contains(image, "tag=tag123"));
|
|
std::string image_no_tag = api::build_image_url("http://srv:8096", "item9", "", 400);
|
|
CHECK(!contains(image_no_tag, "tag="));
|
|
|
|
// Stream: H.264 1080p + AAC stereo transcoding required by v1.
|
|
std::string stream = api::build_stream_url("http://srv:8096/", "item9", "tok", "dev1");
|
|
CHECK(contains(stream, "http://srv:8096/Videos/item9/stream.ts?"));
|
|
CHECK(contains(stream, "static=false"));
|
|
CHECK(contains(stream, "videoCodec=h264"));
|
|
CHECK(contains(stream, "audioCodec=aac"));
|
|
CHECK(contains(stream, "maxWidth=1920"));
|
|
CHECK(contains(stream, "maxHeight=1080"));
|
|
CHECK(contains(stream, "audioChannels=2"));
|
|
CHECK(contains(stream, "deviceId=dev1"));
|
|
CHECK(contains(stream, "api_key=tok"));
|
|
// No start position or playback session by default.
|
|
CHECK(!contains(stream, "startTimeTicks"));
|
|
CHECK(!contains(stream, "playSessionId"));
|
|
|
|
// Server-side seek: optional startTimeTicks + playSessionId.
|
|
std::string stream_seek =
|
|
api::build_stream_url("http://srv:8096", "item9", "tok", "dev1",
|
|
6000000000LL, "play-42");
|
|
CHECK(contains(stream_seek, "startTimeTicks=6000000000"));
|
|
CHECK(contains(stream_seek, "playSessionId=play-42"));
|
|
|
|
// Tracks: nothing by default (compatible with the base URL).
|
|
CHECK(!contains(stream, "mediaSourceId"));
|
|
CHECK(!contains(stream, "audioStreamIndex"));
|
|
CHECK(!contains(stream, "subtitleStreamIndex"));
|
|
CHECK(!contains(stream, "subtitleMethod"));
|
|
|
|
// Source and audio chosen, without subtitles.
|
|
api::TrackSelection tracks;
|
|
tracks.media_source_id = "src 1";
|
|
tracks.audio_index = 2;
|
|
std::string with_audio = api::build_stream_url("http://srv:8096", "item9", "tok", "dev1", 0, "",
|
|
true, tracks);
|
|
CHECK(contains(with_audio, "&mediaSourceId=src%201"));
|
|
CHECK(contains(with_audio, "&audioStreamIndex=2"));
|
|
CHECK(!contains(with_audio, "subtitle"));
|
|
|
|
// Subtitles rendered by the client: nothing in the URL (the stream stays unchanged,
|
|
// so direct streaming is possible).
|
|
tracks.subtitle_index = 4;
|
|
tracks.subtitle_delivery = api::SubtitleDelivery::Client;
|
|
std::string client_subs = api::build_stream_url("http://srv:8096", "item9", "tok", "dev1", 0,
|
|
"", true, tracks);
|
|
CHECK(!contains(client_subs, "subtitle"));
|
|
CHECK_EQ(client_subs, with_audio);
|
|
|
|
// Subtitles burned in by the server.
|
|
tracks.subtitle_delivery = api::SubtitleDelivery::Encode;
|
|
std::string burned = api::build_stream_url("http://srv:8096", "item9", "tok", "dev1",
|
|
6000000000LL, "play-42", false, tracks);
|
|
CHECK(contains(burned, "&subtitleStreamIndex=4&subtitleMethod=Encode"));
|
|
CHECK(contains(burned, "&audioStreamIndex=2"));
|
|
CHECK(contains(burned, "startTimeTicks=6000000000"));
|
|
CHECK(!contains(burned, "api_key"));
|
|
// Index at -1: no burn-in, even if the mode is Encode.
|
|
tracks.subtitle_index = -1;
|
|
CHECK(!contains(api::build_stream_url("http://srv:8096", "item9", "tok", "dev1", 0, "", true,
|
|
tracks), "subtitle"));
|
|
|
|
// SRT subtitles: item's source by default, encoded identifiers.
|
|
CHECK_EQ(api::build_subtitle_url("http://srv:8096/", "item9", "src 1", 6),
|
|
"http://srv:8096/Videos/item9/src%201/Subtitles/6/Stream.srt");
|
|
CHECK_EQ(api::build_subtitle_url("http://srv:8096", "item9", "", 0),
|
|
"http://srv:8096/Videos/item9/item9/Subtitles/0/Stream.srt");
|
|
|
|
// Token masking for display/logs.
|
|
CHECK_EQ(api::mask_api_key("http://s/v?api_key=secret123"),
|
|
"http://s/v?api_key=********");
|
|
CHECK_EQ(api::mask_api_key("http://s/v?api_key=secret123&x=1"),
|
|
"http://s/v?api_key=********&x=1");
|
|
CHECK_EQ(api::mask_api_key("http://s/v?x=1"), "http://s/v?x=1");
|
|
std::string masked = api::mask_api_key(stream_seek);
|
|
CHECK(!contains(masked, "tok"));
|
|
CHECK(contains(masked, "api_key=********"));
|
|
CHECK(contains(masked, "playSessionId=play-42"));
|
|
|
|
return testfw::test_failures();
|
|
}
|