Bump version to v0.7.0: Enhanced GDM integration, status sync, and notification system

This commit is contained in:
2026-01-11 14:00:27 +09:00
parent 1175acd16e
commit 02d26a104d
12 changed files with 1708 additions and 305 deletions

View File

@@ -1,12 +1,3 @@
/**
* Video Modal Component JavaScript
* Reusable video player modal for Anime Downloader
*
* Usage:
* VideoModal.init({ package_name: 'anime_downloader', sub: 'ohli24' });
* VideoModal.openWithPath('/path/to/video.mp4');
*/
var VideoModal = (function() {
'use strict';
@@ -15,28 +6,45 @@ var VideoModal = (function() {
sub: 'ohli24'
};
var videoPlayer = null;
var videoPlayer = null; // Video.js instance
var artPlayer = null; // Artplayer instance
var plyrPlayer = null; // Plyr instance
var currentPlayer = 'videojs'; // 'videojs', 'artplayer', 'plyr'
var playlist = [];
var currentPlaylistIndex = 0;
var currentPlayingPath = '';
var currentStreamUrl = '';
var isVideoZoomed = false;
/**
* Initialize the video modal
* @param {Object} options - Configuration options
* @param {string} options.package_name - Package name (default: 'anime_downloader')
* @param {string} options.sub - Sub-module name (e.g., 'ohli24', 'linkkf')
*/
function init(options) {
config = Object.assign(config, options || {});
// Load saved player preference
var savedPlayer = localStorage.getItem('anime_downloader_preferred_player');
if (savedPlayer && ['videojs', 'artplayer', 'plyr'].indexOf(savedPlayer) >= 0) {
currentPlayer = savedPlayer;
$('#player-select').val(currentPlayer);
}
bindEvents();
console.log('[VideoModal] Initialized with config:', config);
console.log('[VideoModal] Initialized with player:', currentPlayer);
}
/**
* Bind all event handlers
*/
function bindEvents() {
// Player selector change
$('#player-select').off('change').on('change', function() {
var newPlayer = $(this).val();
if (newPlayer !== currentPlayer) {
switchPlayer(newPlayer);
}
});
// Dropdown episode selection
$('#episode-dropdown').off('change').on('change', function() {
var index = parseInt($(this).val());
@@ -50,10 +58,12 @@ var VideoModal = (function() {
$('#btn-video-zoom').off('click').on('click', function() {
isVideoZoomed = !isVideoZoomed;
if (isVideoZoomed) {
$('#video-player').addClass('vjs-zoomed');
$('#video-player, #plyr-player').addClass('vjs-zoomed');
$('#artplayer-container').addClass('art-zoomed');
$(this).addClass('active').find('i').removeClass('fa-expand').addClass('fa-compress');
} else {
$('#video-player').removeClass('vjs-zoomed');
$('#video-player, #plyr-player').removeClass('vjs-zoomed');
$('#artplayer-container').removeClass('art-zoomed');
$(this).removeClass('active').find('i').removeClass('fa-compress').addClass('fa-expand');
}
});
@@ -64,87 +74,81 @@ var VideoModal = (function() {
});
$('#videoModal').off('hide.bs.modal').on('hide.bs.modal', function() {
if (videoPlayer) {
videoPlayer.pause();
}
pauseAllPlayers();
});
$('#videoModal').off('hidden.bs.modal').on('hidden.bs.modal', function() {
$('body').removeClass('modal-video-open');
if (isVideoZoomed) {
isVideoZoomed = false;
$('#video-player').removeClass('vjs-zoomed');
$('#video-player, #plyr-player').removeClass('vjs-zoomed');
$('#artplayer-container').removeClass('art-zoomed');
$('#btn-video-zoom').removeClass('active').find('i').removeClass('fa-compress').addClass('fa-expand');
}
});
}
/**
* Open modal with a file path (fetches playlist from server)
* @param {string} filePath - Path to the video file
* Switch between players
*/
function openWithPath(filePath) {
$.ajax({
url: '/' + config.package_name + '/ajax/' + config.sub + '/get_playlist?path=' + encodeURIComponent(filePath),
type: 'GET',
dataType: 'json',
success: function(data) {
playlist = data.playlist || [];
currentPlaylistIndex = data.current_index || 0;
currentPlayingPath = filePath;
var streamUrl = '/' + config.package_name + '/ajax/' + config.sub + '/stream_video?path=' + encodeURIComponent(filePath);
initPlayer(streamUrl);
updatePlaylistUI();
$('#videoModal').modal('show');
},
error: function() {
// Fallback: single file
playlist = [{ name: filePath.split('/').pop(), path: filePath }];
currentPlaylistIndex = 0;
var streamUrl = '/' + config.package_name + '/ajax/' + config.sub + '/stream_video?path=' + encodeURIComponent(filePath);
initPlayer(streamUrl);
updatePlaylistUI();
$('#videoModal').modal('show');
}
});
function switchPlayer(newPlayer) {
pauseAllPlayers();
currentPlayer = newPlayer;
localStorage.setItem('anime_downloader_preferred_player', newPlayer);
// Hide all player containers
$('#videojs-container').hide();
$('#artplayer-container').hide();
$('#plyr-container').hide();
// Show selected player and reinitialize with current URL
if (currentStreamUrl) {
initPlayerWithUrl(currentStreamUrl);
}
console.log('[VideoModal] Switched to:', newPlayer);
}
/**
* Open modal with a direct stream URL
* @param {string} streamUrl - Direct URL to stream
* @param {string} title - Optional title
* Pause all players
*/
function openWithUrl(streamUrl, title) {
playlist = [{ name: title || 'Video', path: streamUrl }];
currentPlaylistIndex = 0;
initPlayer(streamUrl);
updatePlaylistUI();
$('#videoModal').modal('show');
function pauseAllPlayers() {
try {
if (videoPlayer) videoPlayer.pause();
} catch(e) {}
try {
if (artPlayer) artPlayer.pause();
} catch(e) {}
try {
if (plyrPlayer) plyrPlayer.pause();
} catch(e) {}
}
/**
* Open modal with a playlist array
* @param {Array} playlistData - Array of {name, path} objects
* @param {number} startIndex - Index to start playing from
* Initialize player with URL based on current player selection
*/
function openWithPlaylist(playlistData, startIndex) {
playlist = playlistData || [];
currentPlaylistIndex = startIndex || 0;
if (playlist.length > 0) {
var filePath = playlist[currentPlaylistIndex].path;
var streamUrl = '/' + config.package_name + '/ajax/' + config.sub + '/stream_video?path=' + encodeURIComponent(filePath);
initPlayer(streamUrl);
updatePlaylistUI();
$('#videoModal').modal('show');
function initPlayerWithUrl(streamUrl) {
currentStreamUrl = streamUrl;
if (currentPlayer === 'videojs') {
initVideoJS(streamUrl);
} else if (currentPlayer === 'artplayer') {
initArtplayer(streamUrl);
} else if (currentPlayer === 'plyr') {
initPlyr(streamUrl);
}
}
/**
* Initialize or update Video.js player
* @param {string} streamUrl - URL to play
* Initialize Video.js player
*/
function initPlayer(streamUrl) {
function initVideoJS(streamUrl) {
// Hide other containers
$('#artplayer-container').hide();
$('#plyr-container').hide();
$('#videojs-container').show();
if (!videoPlayer) {
videoPlayer = videojs('video-player', {
controls: true,
@@ -157,22 +161,84 @@ var VideoModal = (function() {
}
});
// Auto-next on video end
videoPlayer.on('ended', function() {
var autoNextEnabled = $('#auto-next-checkbox').is(':checked');
if (autoNextEnabled && currentPlaylistIndex < playlist.length - 1) {
currentPlaylistIndex++;
playVideoAtIndex(currentPlaylistIndex);
}
});
videoPlayer.on('ended', handleVideoEnded);
}
videoPlayer.src({ type: 'video/mp4', src: streamUrl });
}
/**
* Initialize Artplayer
*/
function initArtplayer(streamUrl) {
// Hide other containers
$('#videojs-container').hide();
$('#plyr-container').hide();
$('#artplayer-container').show().empty();
if (artPlayer) {
artPlayer.destroy();
artPlayer = null;
}
artPlayer = new Artplayer({
container: '#artplayer-container',
url: streamUrl,
autoplay: false,
pip: true,
screenshot: true,
setting: true,
playbackRate: true,
aspectRatio: true,
fullscreen: true,
fullscreenWeb: true,
theme: '#3b82f6'
});
artPlayer.on('video:ended', handleVideoEnded);
}
/**
* Initialize Plyr player
*/
function initPlyr(streamUrl) {
// Hide other containers
$('#videojs-container').hide();
$('#artplayer-container').hide();
$('#plyr-container').show();
// Set source
$('#plyr-player').attr('src', streamUrl);
if (!plyrPlayer) {
plyrPlayer = new Plyr('#plyr-player', {
controls: ['play-large', 'play', 'progress', 'current-time', 'mute', 'volume', 'settings', 'pip', 'fullscreen'],
settings: ['quality', 'speed'],
speed: { selected: 1, options: [0.5, 0.75, 1, 1.25, 1.5, 2] }
});
plyrPlayer.on('ended', handleVideoEnded);
} else {
plyrPlayer.source = {
type: 'video',
sources: [{ src: streamUrl, type: 'video/mp4' }]
};
}
}
/**
* Handle video ended event (auto-next)
*/
function handleVideoEnded() {
var autoNextEnabled = $('#auto-next-checkbox').is(':checked');
if (autoNextEnabled && currentPlaylistIndex < playlist.length - 1) {
currentPlaylistIndex++;
playVideoAtIndex(currentPlaylistIndex);
}
}
/**
* Play video at specific playlist index
* @param {number} index - Playlist index
*/
function playVideoAtIndex(index) {
if (index < 0 || index >= playlist.length) return;
@@ -180,14 +246,73 @@ var VideoModal = (function() {
var item = playlist[index];
var streamUrl = '/' + config.package_name + '/ajax/' + config.sub + '/stream_video?path=' + encodeURIComponent(item.path);
if (videoPlayer) {
videoPlayer.src({ type: 'video/mp4', src: streamUrl });
videoPlayer.play();
}
initPlayerWithUrl(streamUrl);
// Try to auto-play
setTimeout(function() {
if (currentPlayer === 'videojs' && videoPlayer) videoPlayer.play();
else if (currentPlayer === 'artplayer' && artPlayer) artPlayer.play = true;
else if (currentPlayer === 'plyr' && plyrPlayer) plyrPlayer.play();
}, 100);
updatePlaylistUI();
}
/**
* Open modal with a file path (fetches playlist from server)
*/
function openWithPath(filePath) {
$.ajax({
url: '/' + config.package_name + '/ajax/' + config.sub + '/get_playlist?path=' + encodeURIComponent(filePath),
type: 'GET',
dataType: 'json',
success: function(data) {
playlist = data.playlist || [];
currentPlaylistIndex = data.current_index || 0;
currentPlayingPath = filePath;
var streamUrl = '/' + config.package_name + '/ajax/' + config.sub + '/stream_video?path=' + encodeURIComponent(filePath);
initPlayerWithUrl(streamUrl);
updatePlaylistUI();
$('#videoModal').modal('show');
},
error: function() {
playlist = [{ name: filePath.split('/').pop(), path: filePath }];
currentPlaylistIndex = 0;
var streamUrl = '/' + config.package_name + '/ajax/' + config.sub + '/stream_video?path=' + encodeURIComponent(filePath);
initPlayerWithUrl(streamUrl);
updatePlaylistUI();
$('#videoModal').modal('show');
}
});
}
/**
* Open modal with a direct stream URL
*/
function openWithUrl(streamUrl, title) {
playlist = [{ name: title || 'Video', path: streamUrl }];
currentPlaylistIndex = 0;
initPlayerWithUrl(streamUrl);
updatePlaylistUI();
$('#videoModal').modal('show');
}
/**
* Open modal with a playlist array
*/
function openWithPlaylist(playlistData, startIndex) {
playlist = playlistData || [];
currentPlaylistIndex = startIndex || 0;
if (playlist.length > 0) {
var filePath = playlist[currentPlaylistIndex].path;
var streamUrl = '/' + config.package_name + '/ajax/' + config.sub + '/stream_video?path=' + encodeURIComponent(filePath);
initPlayerWithUrl(streamUrl);
updatePlaylistUI();
$('#videoModal').modal('show');
}
}
/**
* Update playlist UI (dropdown, external player buttons)
*/