Skip to content

Commit

Permalink
add "View All YouTube Videos (Experimental)" context-menu action to U…
Browse files Browse the repository at this point in the history
…nread category

Right click on Unread and choose "View All YouTube Videos" and it will open up
a custom/local html file that plays all of the videos back to back.
  • Loading branch information
d3fault committed Oct 2, 2024
1 parent a672609 commit 45ac397
Show file tree
Hide file tree
Showing 8 changed files with 362 additions and 0 deletions.
1 change: 1 addition & 0 deletions QuiteRSS.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
<file alias="newspaper_description">html/newspaper_description.html</file>
<file alias="newspaper_head">html/newspaper_head.html</file>
<file alias="newspaper_description_rtl">html/newspaper_description_rtl.html</file>
<file alias="sequential_youtube_player">html/sequential_youtube_player.html</file>
</qresource>
<qresource prefix="/flags">
<file alias="flag_AR">images/flags/flag_arab.png</file>
Expand Down
292 changes: 292 additions & 0 deletions html/sequential_youtube_player.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>YouTube Sequential Player</title>
<style>
body {
background-color: black;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
height: auto;
margin: 0;
color: white;
text-align: center;
overflow: auto;
}
#player-container {
margin-bottom: 20px;
}
#current-title {
font-size: 20px;
margin-bottom: 10px;
color: white;
}
#queue {
list-style-type: none;
padding: 0;
margin: 0;
width: auto;
}
#queue li {
background-color: #333;
padding: 10px;
margin-bottom: 5px;
cursor: pointer;
color: white;
border-radius: 5px;
text-align: left;
user-select: none;
display: flex;
align-items: center;
position: relative;
}
#queue li.dragging {
opacity: 0.5;
}
#queue li.current {
background-color: #f0f0f0;
color: black;
font-weight: bold;
}
#queue li.current .drag-icon {
border-color: black;
}
.drag-icon {
width: 20px;
height: 20px;
margin-right: 10px;
display: inline-block;
border: 2px dotted white;
box-sizing: border-box;
border-radius: 4px;
cursor: grab;
}
#queue li:hover {
cursor: pointer;
}
#controls {
margin-top: 20px;
}
a {
color: white;
margin: 0 15px;
cursor: pointer;
text-decoration: none;
padding: 8px 12px;
border: 2px solid white;
border-radius: 5px;
transition: background-color 0.3s;
}
a:hover {
background-color: white;
color: black;
}
.insert-line {
position: absolute;
left: 0;
right: 0;
height: 2px;
background-color: red;
display: none;
z-index: 1;
}
</style>
<script>
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

var videos = [%1];

var currentIndex = 0;
var player;

function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
height: '390',
width: '640',
videoId: videos[currentIndex].id,
playerVars: {
'autoplay': 1,
'controls': 1,
'rel': 0,
'modestbranding': 1
},
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});

updateQueueUI();
}

function onPlayerReady() {
updateControls();
refocusPlayer();
}

function onPlayerStateChange(event) {
if (event.data === YT.PlayerState.ENDED) {
loadNextVideo();
}
}

function loadVideoAtIndex(index) {
currentIndex = index;
player.loadVideoById(videos[currentIndex].id);
updateControls();
refocusPlayer();
updateQueueUI();
}

function loadPreviousVideo() {
if (currentIndex > 0) {
currentIndex--;
player.loadVideoById(videos[currentIndex].id);
updateControls();
refocusPlayer();
updateQueueUI();
}
}

function loadNextVideo() {
if (currentIndex < videos.length - 1) {
currentIndex++;
player.loadVideoById(videos[currentIndex].id);
updateControls();
refocusPlayer();
updateQueueUI();
}
}

function refocusPlayer() {
var iframe = document.getElementById('player');
if (iframe) {
iframe.focus();
}
}

function updateControls() {
var prevLink = document.getElementById('prev');
var nextLink = document.getElementById('next');

prevLink.classList.toggle('disabled', currentIndex === 0);
nextLink.classList.toggle('disabled', currentIndex === videos.length - 1);
}

function updateQueueUI() {
var queue = document.getElementById('queue');
var currentTitle = document.getElementById('current-title');
queue.innerHTML = '';
currentTitle.textContent = videos[currentIndex].title;

videos.forEach((video, index) => {
var listItem = document.createElement('li');
listItem.innerHTML = `<span class="drag-icon"></span>${index + 1}. ${video.title}`;
listItem.setAttribute('draggable', 'true');
listItem.dataset.index = index;

if (index === currentIndex) {
listItem.classList.add('current');
}

listItem.addEventListener('click', function() {
loadVideoAtIndex(index);
});

listItem.addEventListener('dragstart', handleDragStart);
listItem.addEventListener('dragover', handleDragOver);
listItem.addEventListener('drop', handleDrop);
listItem.addEventListener('dragend', handleDragEnd);

queue.appendChild(listItem);
});
}

function handleDragStart(event) {
event.target.classList.add('dragging');
event.dataTransfer.setData('text/plain', event.target.dataset.index);
}

function handleDragOver(event) {
event.preventDefault();
const draggingIndex = event.dataTransfer.getData('text/plain');
let targetIndex = event.target.dataset.index;

if (!targetIndex && event.target.previousElementSibling) {
targetIndex = event.target.previousElementSibling.dataset.index;
}

if (draggingIndex !== targetIndex) {
const insertLine = document.createElement('div');
insertLine.classList.add('insert-line');
event.target.parentNode.insertBefore(insertLine, event.target);

const rect = event.target.getBoundingClientRect();
const scrollTop = window.scrollY || document.documentElement.scrollTop;
insertLine.style.top = rect.top + scrollTop + 'px';
insertLine.style.left = rect.left + 'px';
insertLine.style.width = rect.width + 'px';
insertLine.style.display = 'block';

event.target.addEventListener('dragleave', function() {
insertLine.remove();
});
}
}

function handleDrop(event) {
event.preventDefault();
const draggedIndex = event.dataTransfer.getData('text/plain');
let targetIndex = event.target.dataset.index;

if (!targetIndex && event.target.previousElementSibling) {
targetIndex = event.target.previousElementSibling.dataset.index;
}

if (draggedIndex !== targetIndex) {
const draggedVideo = videos[draggedIndex];
videos.splice(draggedIndex, 1);
videos.splice(targetIndex, 0, draggedVideo);

if (currentIndex >= Math.min(draggedIndex, targetIndex) && currentIndex <= Math.max(draggedIndex, targetIndex)) {
currentIndex += (targetIndex < draggedIndex) ? 1 : -1;
}

updateQueueUI();
}

const insertLine = document.querySelector('.insert-line');
if (insertLine) {
insertLine.remove();
}
}

function handleDragEnd(event) {
event.target.classList.remove('dragging');
const insertLine = document.querySelector('.insert-line');
if (insertLine) {
insertLine.remove();
}
}
</script>
</head>
<body>
<div id="player-container">
<div id="current-title"></div>
<div id="player"></div>
<div id="controls">
<a id="prev" class="disabled" onclick="loadPreviousVideo()">Previous</a>
<a id="next" onclick="loadNextVideo()">Next</a>
</div>
</div>

<ul id="queue"></ul>
</body>
</html>
7 changes: 7 additions & 0 deletions src/application/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,8 @@ void MainWindow::createFeedsWidget()
this, SLOT(clearDeleted()));
connect(categoriesTree_, SIGNAL(signalMarkRead(QTreeWidgetItem*)),
this, SLOT(slotMarkReadCategory(QTreeWidgetItem*)));
connect(categoriesTree_, SIGNAL(signalViewAllYoutubeVideos()),
this, SLOT(viewAllYoutubeVideos()));
connect(showCategoriesButton_, SIGNAL(clicked()),
this, SLOT(showNewsCategoriesTree()));
connect(feedsSplitter_, SIGNAL(splitterMoved(int,int)),
Expand Down Expand Up @@ -7585,6 +7587,11 @@ void MainWindow::slotMarkReadCategory(QTreeWidgetItem *item)
}
}

void MainWindow::viewAllYoutubeVideos()
{
currentNewsTab->viewAllYoutubeVideos();
}

/** @brief Show/Hide categories tree
*---------------------------------------------------------------------------*/
void MainWindow::showNewsCategoriesTree()
Expand Down
1 change: 1 addition & 0 deletions src/application/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ private slots:
void slotCategoriesClicked(QTreeWidgetItem *item, int, bool createTab = false);
void clearDeleted();
void slotMarkReadCategory(QTreeWidgetItem *item);
void viewAllYoutubeVideos();
void showNewsCategoriesTree();
void feedsSplitterMoved(int pos, int);

Expand Down
4 changes: 4 additions & 0 deletions src/categoriestreewidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ void CategoriesTreeWidget::showContextMenuCategory(const QPoint &pos)
menu.addSeparator();
menu.addAction(tr("Mark Read"), this, SLOT(slotMarkRead()));
}
if (itemClicked_ == topLevelItem(UnreadItem)) {
menu.addSeparator();
menu.addAction(tr("View All Youtube Videos (Experimental)"), this, SIGNAL(signalViewAllYoutubeVideos()));
}
menu.exec(viewport()->mapToGlobal(pos));
}
}
Expand Down
1 change: 1 addition & 0 deletions src/categoriestreewidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class CategoriesTreeWidget : public QTreeWidget
void signalMiddleClicked();
void signalClearDeleted();
void signalMarkRead(QTreeWidgetItem *item);
void signalViewAllYoutubeVideos();
// void pressKeyUp();
// void pressKeyDown();

Expand Down
Loading

0 comments on commit 45ac397

Please sign in to comment.