common.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. /*
  2. Movie App - Common Configuration
  3. If the app folder is renamed, update APP_NAME below and all paths
  4. will automatically adjust everywhere this file is included via requirepkg().
  5. */
  6. // ── App identity ─────────────────────────────────────────────────────────────
  7. var APP_NAME = "Movie";
  8. var BACKEND_PATH = APP_NAME + "/backend/";
  9. // ── Server API endpoints (relative from any page in this app) ────────────────
  10. var MEDIA_API = "../media"; // ?file=<vpath> streams a file
  11. var TRANSCODE_API = "../media/transcode"; // ?file
  12. var HLS_API = "../media/hls"; // ?file same transcode, as an HLS playlist
  13. var STORYBOARD_API = "../media/storyboard/"; // ?file[&image=1] scrub previews
  14. var SUBTITLE_API = "../media/subtitles/"; // ?file[&track=n|&font=n] embedded tracks
  15. var AGI_INTERFACE = "../system/ajgi/interface?script=";
  16. // ── Script paths (used when calling ao_module_agirun from the frontend) ──────
  17. var SCRIPT_GET_LIBRARY = BACKEND_PATH + "getLibrary.js";
  18. var SCRIPT_GET_LIBRARY_CACHE = BACKEND_PATH + "getLibraryCache.js";
  19. var SCRIPT_GET_EPISODES = BACKEND_PATH + "getEpisodes.js";
  20. var SCRIPT_GET_THUMBNAIL = BACKEND_PATH + "getThumbnail.js";
  21. var SCRIPT_LIST_FOLDER = BACKEND_PATH + "listFolder.js";
  22. var SCRIPT_GET_MOVIE_INFO = BACKEND_PATH + "getMovieInfo.js";
  23. var SCRIPT_DISABLE_MOVIE_INFO = BACKEND_PATH + "disableMovieInfo.js";
  24. var SCRIPT_GET_WATCHTIME = BACKEND_PATH + "getWatchTime.js";
  25. var SCRIPT_SET_WATCHTIME = BACKEND_PATH + "setWatchTime.js";
  26. var SCRIPT_GET_INDEX_STATS = BACKEND_PATH + "getIndexStats.js";
  27. var SCRIPT_CLEAR_INDEX = BACKEND_PATH + "clearIndex.js";
  28. // ── Streaming mode ───────────────────────────────────────────────────────────
  29. // Formats the browser cannot decode are transcoded on the fly, and there are
  30. // two ways to deliver that transcode:
  31. //
  32. // mp4 a single fragmented MP4 streamed down one response. The long-standing
  33. // default: it starts fastest and costs the server nothing but the ffmpeg
  34. // process. It cannot answer byte-range requests, which WebKit clients
  35. // (Safari, and every browser on iOS) require before they will play
  36. // anything, so on those it fails outright.
  37. // hls the same transcode cut into segments behind a playlist. Every segment
  38. // is a finite, seekable file, so WebKit plays it and seeking inside the
  39. // transcoded window no longer restarts the stream.
  40. //
  41. // "auto" picks hls on WebKit and mp4 everywhere else, so nothing changes for
  42. // browsers that were already working.
  43. var STREAM_MODE_KEY = "movie_stream_mode";
  44. function isWebKitClient() {
  45. var ua = navigator.userAgent;
  46. // On iOS every browser is WebKit underneath, whatever it calls itself.
  47. if (/CriOS|FxiOS/.test(ua)) { return true; }
  48. // Chrome, Edge and Opera all carry "Safari" in their desktop UA.
  49. if (/Chrome\/|Chromium|Edg\/|OPR\/|Firefox\//.test(ua)) { return false; }
  50. return /Safari/.test(ua);
  51. }
  52. function getStreamMode() {
  53. var mode = localStorage.getItem(STREAM_MODE_KEY);
  54. return (mode === "mp4" || mode === "hls") ? mode : "auto";
  55. }
  56. function setStreamMode(mode) {
  57. if (mode !== "mp4" && mode !== "hls") { mode = "auto"; }
  58. localStorage.setItem(STREAM_MODE_KEY, mode);
  59. }
  60. // Whether the current preference resolves to HLS for this browser.
  61. function usingHLS() {
  62. var mode = getStreamMode();
  63. if (mode === "hls") { return true; }
  64. if (mode === "mp4") { return false; }
  65. return isWebKitClient();
  66. }
  67. // WebKit plays HLS natively; everyone else needs hls.js, which is only present
  68. // if it has been vendored into web/script/.
  69. function nativeHLSSupported(videoEl) {
  70. if (!videoEl || !videoEl.canPlayType) { return false; }
  71. return videoEl.canPlayType("application/vnd.apple.mpegurl") !== "";
  72. }
  73. function hlsPlaybackSupported(videoEl) {
  74. if (nativeHLSSupported(videoEl)) { return true; }
  75. return !!(window.Hls && window.Hls.isSupported());
  76. }
  77. // Build the streaming URL for a transcoded file, honouring the current mode.
  78. // startSeconds restarts the transcode at an offset; the resulting stream always
  79. // begins at zero, so callers track the offset separately.
  80. function transcodeStreamURL(filepath, startSeconds) {
  81. var base = usingHLS() ? HLS_API : TRANSCODE_API;
  82. var url = base + "?file=" + encodeURIComponent(filepath);
  83. if (startSeconds && startSeconds > 0.001) {
  84. url += "&start=" + startSeconds.toFixed(3);
  85. }
  86. return url;
  87. }
  88. // Point a <video> at a stream URL. HLS on a browser without native support is
  89. // routed through hls.js when it is available. Returns false when the stream
  90. // cannot be played at all, so the caller can say so rather than hang.
  91. function attachTranscodeStream(videoEl, url) {
  92. if (videoEl._hlsInstance) {
  93. // Tear down the previous hls.js attachment before rebinding
  94. try { videoEl._hlsInstance.destroy(); } catch (e) {}
  95. videoEl._hlsInstance = null;
  96. }
  97. var isPlaylist = url.indexOf(HLS_API) === 0;
  98. if (isPlaylist && !nativeHLSSupported(videoEl)) {
  99. if (!(window.Hls && window.Hls.isSupported())) { return false; }
  100. var hls = new window.Hls({ enableWorker: true });
  101. videoEl._hlsInstance = hls;
  102. hls.loadSource(url);
  103. hls.attachMedia(videoEl);
  104. return true;
  105. }
  106. videoEl.src = url;
  107. videoEl.load();
  108. return true;
  109. }
  110. // ── Scanner settings ─────────────────────────────────────────────────────────
  111. var VALID_VIDEO_FORMATS = ["mp4", "webm", "ogg", "mkv", "avi", "mov", "m4v", "wmv", "flv", "rmvb", "ts"];
  112. var SKIP_ROOT_PREFIXES = ["tmp:/", "trash:/"]; // roots to skip entirely
  113. var VIDEO_FOLDER_NAME = "Video"; // expected folder inside each root
  114. var MOVIE_FOLDER_NAMES = ["movie", "movies"]; // folder names (case-insensitive) treated as movie containers
  115. var ANIME_FOLDER_NAMES = ["anime"]; // folder names (case-insensitive) treated as anime containers