source: smplayer/trunk/src/playlist.cpp

Last change on this file was 189, checked in by Silvan Scherrer, 8 years ago

SMPlayer: adjust version info in readme and some small glitches

  • Property svn:eol-style set to LF
File size: 65.7 KB
Line 
1/* smplayer, GUI front-end for mplayer.
2 Copyright (C) 2006-2017 Ricardo Villalba <rvm@users.sourceforge.net>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17*/
18
19#include "playlist.h"
20
21#include <QTableView>
22#include <QStandardItemModel>
23#include <QSortFilterProxyModel>
24#include <QStyledItemDelegate>
25#include <QToolBar>
26#include <QFile>
27#include <QTextStream>
28#include <QDir>
29#include <QFileInfo>
30#include <QMessageBox>
31#include <QPushButton>
32#include <QMenu>
33#include <QDateTime>
34#include <QSettings>
35#include <QInputDialog>
36#include <QToolButton>
37#include <QTimer>
38#include <QVBoxLayout>
39#include <QUrl>
40#include <QDragEnterEvent>
41#include <QDropEvent>
42#include <QHeaderView>
43#include <QTextCodec>
44#include <QApplication>
45#include <QMimeData>
46#include <QDomDocument>
47#include <QDesktopServices>
48#include <QDebug>
49
50#if QT_VERSION >= 0x050000
51#include "myscroller.h"
52#endif
53
54#include "myaction.h"
55#include "mylineedit.h"
56#include "filedialog.h"
57#include "helper.h"
58#include "images.h"
59#include "preferences.h"
60#include "multilineinputdialog.h"
61#include "version.h"
62#include "extensions.h"
63#include "guiconfig.h"
64
65#ifdef CHROMECAST_SUPPORT
66#include "chromecast.h"
67#endif
68
69#ifdef PLAYLIST_DOWNLOAD
70#include "inputurl.h"
71#include "youtube/loadpage.h"
72#include "urlhistory.h"
73#include <QNetworkAccessManager>
74#include <QTemporaryFile>
75#include <QClipboard>
76#include <QMovie>
77#endif
78
79#if USE_INFOPROVIDER
80#include "infoprovider.h"
81#endif
82
83#define DRAG_ITEMS 0
84#define PL_ALLOW_DUPLICATES 1
85#define SIMULATE_FILE_DELETION 0
86#define USE_ITEM_DELEGATE 0
87
88#define COL_NUM 0
89#define COL_NAME 1
90#define COL_TIME 2
91#define COL_FILENAME 3
92
93#if USE_ITEM_DELEGATE
94class PlaylistDelegate : public QStyledItemDelegate {
95public:
96 PlaylistDelegate(QObject * parent = 0) : QStyledItemDelegate(parent) {};
97 virtual void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const {
98 QStyleOptionViewItem opt = option;
99 initStyleOption(&opt, index);
100 if (index.column() == COL_NAME) {
101 bool played = index.data(PLItem::Role_Played).toBool();
102 bool current = index.data(PLItem::Role_Current).toBool();
103 if (current) opt.font.setBold(true);
104 else
105 if (played) opt.font.setItalic(true);
106 }
107 else
108 if (index.column() == COL_FILENAME) {
109 opt.textElideMode = Qt::ElideMiddle;
110 }
111 QStyledItemDelegate::paint(painter, opt, index);
112 }
113};
114#endif
115
116/* ----------------------------------------------------------- */
117
118
119PLItem::PLItem() : QStandardItem() {
120 col_num = new QStandardItem();
121 col_duration = new QStandardItem();
122 col_filename = new QStandardItem();
123
124 setDuration(0);
125 setPlayed(false);
126 setCurrent(false);
127
128 col_num->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
129}
130
131PLItem::PLItem(const QString filename, const QString name, double duration) : QStandardItem() {
132 col_num = new QStandardItem();
133 col_duration = new QStandardItem();
134 col_filename = new QStandardItem();
135
136 setFilename(filename);
137 setName(name);
138 setDuration(duration);
139 setPlayed(false);
140 setCurrent(false);
141
142 col_num->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
143}
144
145PLItem::~PLItem() {
146}
147
148void PLItem::setFilename(const QString filename) {
149 col_filename->setText(filename);
150 col_filename->setToolTip(filename);
151 col_filename->setData(filename);
152
153 if (!filename.contains("://") && filename.count() > 50) {
154 QStringList parts = filename.split(QDir::separator());
155 //if (!parts.isEmpty() && parts[0].isEmpty()) parts[0] = "/";
156 //qDebug() << "PLItem::setFilename: parts count:" << parts.count() << "parts:" << parts;
157 if (parts.count() >= 2) {
158 QString s = parts[parts.count()-2] + QDir::separator() + parts[parts.count()-1];
159 if (parts.count() > 2) s = QString("...") + QDir::separator() + s;
160 col_filename->setText(s);
161 }
162 }
163}
164
165void PLItem::setName(const QString name) {
166 setText(name);
167 setData(name);
168 setToolTip(name);
169}
170
171void PLItem::setDuration(double duration) {
172 col_duration->setData(duration);
173 col_duration->setText(Helper::formatTime(duration));
174}
175
176void PLItem::setPlayed(bool played) {
177 setData(played, Role_Played);
178#if !USE_ITEM_DELEGATE
179 QFont f = font();
180 f.setItalic(played);
181 setFont(f);
182#endif
183}
184
185void PLItem::setPosition(int position) {
186 //col_num->setText(QString("%1").arg(position, 4, 10, QChar('0')));
187 col_num->setText(QString::number(position));
188 col_num->setData(position);
189}
190
191void PLItem::setCurrent(bool b) {
192 setData(b, Role_Current);
193#if !USE_ITEM_DELEGATE
194 QFont f = font();
195 f.setBold(b);
196 f.setItalic(b ? false : played());
197 setFont(f);
198#endif
199}
200
201QString PLItem::filename() {
202 return col_filename->data().toString();
203}
204
205QString PLItem::name() {
206 return text();
207}
208
209double PLItem::duration() {
210 return col_duration->data().toDouble();
211}
212
213bool PLItem::played() {
214 return data(Role_Played).toBool();
215}
216
217int PLItem::position() {
218 return col_num->data().toInt();
219}
220
221bool PLItem::isCurrent() {
222 return data(Role_Current).toBool();
223}
224
225QList<QStandardItem *> PLItem::items() {
226 QList<QStandardItem *> l;
227 l << col_num << this << col_duration << col_filename;
228 return l;
229}
230
231void PLItem::setExtraParams(const QStringList & pars) {
232 setData(pars, Role_Params);
233}
234
235QStringList PLItem::extraParams() {
236 return data(Role_Params).toStringList();
237}
238
239void PLItem::setVideoURL(const QString & url) {
240 setData(url, Role_Video_URL);
241}
242
243QString PLItem::videoURL() {
244 return data(Role_Video_URL).toString();
245}
246
247/* ----------------------------------------------------------- */
248
249
250Playlist::Playlist(QWidget * parent, Qt::WindowFlags f)
251 : QWidget(parent,f)
252 , set(0)
253 , modified(false)
254 , recursive_add_directory(false)
255 , automatically_get_info(false)
256 , save_playlist_in_config(true)
257 , play_files_from_start(true)
258 , row_spacing(-1) // Default height
259 , start_play_on_load(true)
260 , automatically_play_next(true)
261 , ignore_player_errors(false)
262 , change_name(true)
263 , save_dirs(true)
264{
265 playlist_path = "";
266 latest_dir = "";
267
268 filter_edit = new MyLineEdit(this);
269 connect(filter_edit, SIGNAL(textChanged(const QString &)), this, SLOT(filterEditChanged(const QString &)));
270
271 createTable();
272 createActions();
273 createToolbar();
274
275 QVBoxLayout *layout = new QVBoxLayout;
276#ifdef PLAYLIST_DOUBLE_TOOLBAR
277 layout->addWidget(toolbar);
278 layout->addWidget(listView);
279 layout->addWidget(toolbar2);
280#else
281 layout->addWidget(listView);
282 layout->addWidget(toolbar);
283 layout->addWidget(filter_edit);
284 filter_edit->hide();
285#endif
286 setLayout(layout);
287
288 clear();
289
290 retranslateStrings();
291
292#if !DOCK_PLAYLIST
293 setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Expanding );
294 adjustSize();
295#else
296 //setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Expanding );
297 //setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
298 setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
299#endif
300
301 setAcceptDrops(true);
302 setAttribute(Qt::WA_NoMousePropagation);
303
304 // Random seed
305 QTime now = QTime::currentTime();
306 qsrand(now.msec());
307
308 //loadSettings();
309
310 // Save config every 5 minutes.
311 save_timer = new QTimer(this);
312 connect( save_timer, SIGNAL(timeout()), this, SLOT(maybeSaveSettings()) );
313 save_timer->start( 5 * 60000 );
314
315#ifdef PLAYLIST_DOWNLOAD
316 downloader = new LoadPage(new QNetworkAccessManager(this), this);
317 downloader->setUserAgent("SMPlayer");
318 connect(downloader, SIGNAL(pageLoaded(QByteArray)), this, SLOT(playlistDownloaded(QByteArray)));
319 connect(downloader, SIGNAL(errorOcurred(int, QString)), this, SLOT(errorOcurred(int, QString)));
320
321 history_urls = new URLHistory;
322 history_urls->addUrl("http://smplayer.info/sample.m3u8");
323#endif
324}
325
326Playlist::~Playlist() {
327 saveSettings();
328 if (set) delete set;
329
330#ifdef PLAYLIST_DOWNLOAD
331 delete history_urls;
332#endif
333}
334
335void Playlist::setConfigPath(const QString & config_path) {
336 qDebug() << "Playlist::setConfigPath:" << config_path;
337
338 if (set) {
339 delete set;
340 set = 0;
341 }
342
343 if (!config_path.isEmpty()) {
344 QString inifile = config_path + "/playlist.ini";
345 qDebug() << "Playlist::setConfigPath: ini file:" << inifile;
346 set = new QSettings(inifile, QSettings::IniFormat);
347 loadSettings();
348 }
349}
350
351void Playlist::updateWindowTitle() {
352 QString title;
353
354 title = playlist_filename;
355 if (title.isEmpty()) title = tr("Untitled playlist");
356 if (modified) title += " (*)";
357
358 qDebug() << "Playlist::updateWindowTitle:" << title;
359
360 setWindowTitle(title);
361 emit windowTitleChanged(title);
362}
363
364void Playlist::setPlaylistFilename(const QString & f) {
365 playlist_filename = f;
366 updateWindowTitle();
367}
368
369void Playlist::setModified(bool mod) {
370 qDebug("Playlist::setModified: %d", mod);
371
372 modified = mod;
373 emit modifiedChanged(modified);
374 updateWindowTitle();
375}
376
377void Playlist::createTable() {
378 table = new QStandardItemModel(this);
379 table->setColumnCount(COL_FILENAME + 1);
380 //table->setSortRole(Qt::UserRole + 1);
381
382 proxy = new QSortFilterProxyModel(this);
383 proxy->setSourceModel(table);
384 proxy->setSortRole(Qt::UserRole + 1);
385 proxy->setFilterRole(Qt::UserRole + 1);
386 proxy->setFilterKeyColumn(-1); // All columns
387
388#if USE_ITEM_DELEGATE
389 PlaylistDelegate * pl_delegate = new PlaylistDelegate(this);
390#endif
391
392 listView = new QTableView(this);
393 listView->setModel(proxy);
394
395#if USE_ITEM_DELEGATE
396 listView->setItemDelegateForColumn(COL_NAME, pl_delegate);
397 //listView->setItemDelegateForColumn(COL_FILENAME, pl_delegate);
398#endif
399
400 listView->setObjectName("playlist_table");
401 listView->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
402 listView->setSelectionBehavior(QAbstractItemView::SelectRows);
403 listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
404 listView->setContextMenuPolicy( Qt::CustomContextMenu );
405 listView->setShowGrid(false);
406 listView->setSortingEnabled(true);
407 listView->setWordWrap(false);
408#if !USE_ITEM_DELEGATE
409 listView->setAlternatingRowColors(true);
410#endif
411 listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
412
413 listView->verticalHeader()->hide();
414
415#if QT_VERSION >= 0x050000
416 MyScroller::setScroller(listView->viewport());
417
418 listView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
419// listView->horizontalHeader()->setSectionResizeMode(COL_NAME, QHeaderView::Stretch);
420#else
421 listView->horizontalHeader()->setResizeMode(QHeaderView::Interactive);
422// listView->horizontalHeader()->setResizeMode(COL_NAME, QHeaderView::Stretch);
423// listView->horizontalHeader()->setResizeMode(COL_FILENAME, QHeaderView::Interactive);
424#endif
425 listView->horizontalHeader()->setStretchLastSection(true);
426 listView->horizontalHeader()->setSortIndicator(0, Qt::AscendingOrder);
427
428 /*
429 listView->horizontalHeader()->setResizeMode(COL_TIME, QHeaderView::ResizeToContents);
430 listView->horizontalHeader()->setResizeMode(COL_PLAY, QHeaderView::ResizeToContents);
431 */
432 //listView->setIconSize( Images::icon("ok").size() );
433
434#if DRAG_ITEMS
435 listView->setSelectionMode(QAbstractItemView::SingleSelection);
436 listView->setDragEnabled(true);
437 listView->setAcceptDrops(true);
438 listView->setDropIndicatorShown(true);
439 listView->setDragDropMode(QAbstractItemView::InternalMove);
440#endif
441
442 connect(listView, SIGNAL(activated(const QModelIndex &)),
443 this, SLOT(itemActivated(const QModelIndex &)) );
444
445 setFilenameColumnVisible(false);
446}
447
448void Playlist::createActions() {
449 openAct = new MyAction(this, "pl_open", false);
450 connect( openAct, SIGNAL(triggered()), this, SLOT(load()) );
451
452#ifdef PLAYLIST_DOWNLOAD
453 openUrlAct = new MyAction(this, "pl_open_url", false);
454 connect( openUrlAct, SIGNAL(triggered()), this, SLOT(openUrl()) );
455#endif
456
457 saveAct = new MyAction(this, "pl_save", false);
458 connect( saveAct, SIGNAL(triggered()), this, SLOT(saveCurrentPlaylist()) );
459
460 saveAsAct = new MyAction(this, "pl_save_as", false);
461 connect( saveAsAct, SIGNAL(triggered()), this, SLOT(save()) );
462
463 playAct = new MyAction(this, "pl_play", false);
464 connect( playAct, SIGNAL(triggered()), this, SLOT(playCurrent()) );
465
466 nextAct = new MyAction(Qt::Key_N /*Qt::Key_Greater*/, this, "pl_next", false);
467 connect( nextAct, SIGNAL(triggered()), this, SLOT(playNext()) );
468
469 prevAct = new MyAction(Qt::Key_P /*Qt::Key_Less*/, this, "pl_prev", false);
470 connect( prevAct, SIGNAL(triggered()), this, SLOT(playPrev()) );
471
472 moveUpAct = new MyAction(this, "pl_move_up", false);
473 connect( moveUpAct, SIGNAL(triggered()), this, SLOT(upItem()) );
474
475 moveDownAct = new MyAction(this, "pl_move_down", false);
476 connect( moveDownAct, SIGNAL(triggered()), this, SLOT(downItem()) );
477
478 repeatAct = new MyAction(this, "pl_repeat", false);
479 repeatAct->setCheckable(true);
480
481 shuffleAct = new MyAction(this, "pl_shuffle", false);
482 shuffleAct->setCheckable(true);
483
484 // Add actions
485 addCurrentAct = new MyAction(this, "pl_add_current", false);
486 connect( addCurrentAct, SIGNAL(triggered()), this, SLOT(addCurrentFile()) );
487
488 addFilesAct = new MyAction(this, "pl_add_files", false);
489 connect( addFilesAct, SIGNAL(triggered()), this, SLOT(addFiles()) );
490
491 addDirectoryAct = new MyAction(this, "pl_add_directory", false);
492 connect( addDirectoryAct, SIGNAL(triggered()), this, SLOT(addDirectory()) );
493
494 addUrlsAct = new MyAction(this, "pl_add_urls", false);
495 connect( addUrlsAct, SIGNAL(triggered()), this, SLOT(addUrls()) );
496
497 // Remove actions
498 removeSelectedAct = new MyAction(this, "pl_remove_selected", false);
499 connect( removeSelectedAct, SIGNAL(triggered()), this, SLOT(removeSelected()) );
500
501 removeAllAct = new MyAction(this, "pl_remove_all", false);
502 connect( removeAllAct, SIGNAL(triggered()), this, SLOT(removeAll()) );
503
504 // Edit
505 editAct = new MyAction(this, "pl_edit", false);
506 connect( editAct, SIGNAL(triggered()), this, SLOT(editCurrentItem()) );
507
508#ifdef PLAYLIST_DELETE_FROM_DISK
509 deleteSelectedFileFromDiskAct = new MyAction(this, "pl_delete_from_disk");
510 connect( deleteSelectedFileFromDiskAct, SIGNAL(triggered()), this, SLOT(deleteSelectedFileFromDisk()));
511#endif
512
513 copyURLAct = new MyAction(this, "pl_copy_url");
514 connect( copyURLAct, SIGNAL(triggered()), this, SLOT(copyURL()));
515
516 openFolderAct = new MyAction(this, "pl_open_folder");
517 connect( openFolderAct, SIGNAL(triggered()), this, SLOT(openFolder()));
518
519#ifdef CHROMECAST_SUPPORT
520 playOnChromecastAct = new MyAction(this, "pl_chromecast");
521 connect( playOnChromecastAct, SIGNAL(triggered()), this, SLOT(playOnChromecast()));
522#else
523 openURLInWebAct = new MyAction(this, "pl_url_in_web");
524 connect( openURLInWebAct, SIGNAL(triggered()), this, SLOT(openURLInWeb()));
525#endif
526
527 showSearchAct = new MyAction(this, "pl_show_search", false);
528 showSearchAct->setCheckable(true);
529 connect(showSearchAct, SIGNAL(toggled(bool)), filter_edit, SLOT(setVisible(bool)));
530
531 showPositionColumnAct = new MyAction(this, "pl_show_position_column");
532 showPositionColumnAct->setCheckable(true);
533 connect(showPositionColumnAct, SIGNAL(toggled(bool)), this, SLOT(setPositionColumnVisible(bool)));
534
535 showNameColumnAct = new MyAction(this, "pl_show_name_column");
536 showNameColumnAct->setCheckable(true);
537 connect(showNameColumnAct, SIGNAL(toggled(bool)), this, SLOT(setNameColumnVisible(bool)));
538
539 showDurationColumnAct = new MyAction(this, "pl_show_duration_column");
540 showDurationColumnAct->setCheckable(true);
541 connect(showDurationColumnAct, SIGNAL(toggled(bool)), this, SLOT(setDurationColumnVisible(bool)));
542
543 showFilenameColumnAct = new MyAction(this, "pl_show_filename_column");
544 showFilenameColumnAct->setCheckable(true);
545 connect(showFilenameColumnAct, SIGNAL(toggled(bool)), this, SLOT(setFilenameColumnVisible(bool)));
546}
547
548void Playlist::createToolbar() {
549 toolbar = new QToolBar(this);
550 toolbar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
551 //toolbar->setIconSize(QSize(48,48));
552
553#ifdef PLAYLIST_DOUBLE_TOOLBAR
554 toolbar2 = new QToolBar(this);
555 toolbar2->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
556#endif
557
558/*
559 toolbar->addAction(openAct);
560#ifdef PLAYLIST_DOWNLOAD
561 toolbar->addAction(openUrlAct);
562#endif
563 toolbar->addAction(saveAct);;
564 toolbar->addSeparator();
565*/
566
567 file_menu = new QMenu(this);
568 file_menu->addAction(openAct);
569 file_menu->addAction(saveAct);
570 file_menu->addAction(saveAsAct);
571#ifdef PLAYLIST_DOWNLOAD
572 file_menu->addAction(openUrlAct);
573#endif
574
575 file_button = new QToolButton(this);
576 file_button->setMenu(file_menu);
577 file_button->setPopupMode(QToolButton::InstantPopup);
578
579 add_menu = new QMenu( this );
580 add_menu->addAction(addCurrentAct);
581 add_menu->addAction(addFilesAct );
582 add_menu->addAction(addDirectoryAct);
583 add_menu->addAction(addUrlsAct);
584
585 add_button = new QToolButton( this );
586 add_button->setMenu( add_menu );
587 add_button->setPopupMode(QToolButton::InstantPopup);
588
589 remove_menu = new QMenu( this );
590 remove_menu->addAction(removeSelectedAct);
591 remove_menu->addAction(removeAllAct);
592
593 remove_button = new QToolButton( this );
594 remove_button->setMenu( remove_menu );
595 remove_button->setPopupMode(QToolButton::InstantPopup);
596
597
598#ifdef PLAYLIST_DOWNLOAD
599 QLabel * loading_label = new QLabel(this);
600 animation = new QMovie();
601 animation->setFileName(Images::file("pl_loading.gif"));
602 loading_label->setMovie(animation);
603#endif
604
605 toolbar->addWidget(file_button);
606 toolbar->addSeparator();
607 toolbar->addWidget(add_button);
608 toolbar->addWidget(remove_button);
609
610 toolbar->addSeparator();
611 toolbar->addAction(playAct);
612 toolbar->addAction(prevAct);
613 toolbar->addAction(nextAct);
614#ifdef PLAYLIST_DOUBLE_TOOLBAR
615 toolbar2->addAction(moveUpAct);
616 toolbar2->addAction(moveDownAct);
617 toolbar2->addAction(repeatAct);
618 toolbar2->addAction(shuffleAct);
619 toolbar2->addSeparator();
620 toolbar2->addWidget(filter_edit);
621 #ifdef PLAYLIST_DOWNLOAD
622 loading_label_action = toolbar2->addWidget(loading_label);
623 #endif
624#else
625 toolbar->addSeparator();
626 toolbar->addAction(repeatAct);
627 toolbar->addAction(shuffleAct);
628 toolbar->addSeparator();
629 toolbar->addAction(moveUpAct);
630 toolbar->addAction(moveDownAct);
631 toolbar->addSeparator();
632 toolbar->addAction(showSearchAct);
633 // toolbar->addWidget(filter_edit);
634 #ifdef PLAYLIST_DOWNLOAD
635 loading_label_action = toolbar->addWidget(loading_label);
636 #endif
637#endif
638
639#ifdef PLAYLIST_DOWNLOAD
640 loading_label_action->setVisible(false);
641#endif
642
643 // Popup menu
644 popup = new QMenu(this);
645 popup->addAction(playAct);
646 popup->addAction(removeSelectedAct);
647 popup->addAction(editAct);
648#ifdef PLAYLIST_DELETE_FROM_DISK
649 popup->addAction(deleteSelectedFileFromDiskAct);
650#endif
651 popup->addAction(copyURLAct);
652 popup->addAction(openFolderAct);
653#ifdef CHROMECAST_SUPPORT
654 popup->addAction(playOnChromecastAct);
655#else
656 popup->addAction(openURLInWebAct);
657#endif
658 popup->addSeparator();
659 popup->addAction(showPositionColumnAct);
660 popup->addAction(showNameColumnAct);
661 popup->addAction(showDurationColumnAct);
662 popup->addAction(showFilenameColumnAct);
663
664 connect( listView, SIGNAL(customContextMenuRequested(const QPoint &)),
665 this, SLOT(showPopup(const QPoint &)) );
666}
667
668void Playlist::retranslateStrings() {
669 table->setHorizontalHeaderLabels(QStringList() << " " << tr("Name") << tr("Length") << tr("Filename / URL") );
670
671 openAct->change( Images::icon("open"), tr("&Load...") );
672#ifdef PLAYLIST_DOWNLOAD
673 openUrlAct->change( Images::icon("url"), tr("Load playlist from &URL...") );
674 openUrlAct->setToolTip(tr("Download playlist from URL"));
675#endif
676 saveAct->change( Images::icon("save"), tr("&Save") );
677 saveAsAct->change( Images::icon("save"), tr("Save &as...") );
678
679 playAct->change( tr("&Play") );
680
681 nextAct->change( tr("&Next") );
682 prevAct->change( tr("Pre&vious") );
683
684 playAct->setIcon( Images::icon("play") );
685 nextAct->setIcon( Images::icon("next") );
686 prevAct->setIcon( Images::icon("previous") );
687
688 moveUpAct->change( Images::icon("up"), tr("Move &up") );
689 moveDownAct->change( Images::icon("down"), tr("Move &down") );
690
691 repeatAct->change( Images::icon("repeat"), tr("&Repeat") );
692 shuffleAct->change( Images::icon("shuffle"), tr("S&huffle") );
693
694 // Add actions
695 addCurrentAct->change( tr("Add &current file") );
696 addFilesAct->change( tr("Add &file(s)") );
697 addDirectoryAct->change( tr("Add &directory") );
698 addUrlsAct->change( tr("Add &URL(s)") );
699
700 // Remove actions
701 removeSelectedAct->change( Images::icon("delete"), tr("Remove &selected") );
702 removeAllAct->change( tr("Remove &all") );
703
704#ifdef PLAYLIST_DELETE_FROM_DISK
705 deleteSelectedFileFromDiskAct->change( tr("&Delete file from disk") );
706#endif
707
708 copyURLAct->change( Images::icon("copy"), tr("&Copy file path to clipboard") );
709 openFolderAct->change( Images::icon("openfolder"), tr("&Open source folder") );
710
711#ifdef CHROMECAST_SUPPORT
712 playOnChromecastAct->change( Images::icon("chromecast"), tr("Play on Chromec&ast") );
713#else
714 openURLInWebAct->change( tr("Open stream in &a web browser") );
715#endif
716
717 showSearchAct->change(Images::icon("find"), tr("Search"));
718
719 showPositionColumnAct->change(tr("Show position column"));
720 showNameColumnAct->change(tr("Show name column"));
721 showDurationColumnAct->change(tr("Show length column"));
722 showFilenameColumnAct->change(tr("Show filename column"));
723
724 // Edit
725 editAct->change( tr("&Edit") );
726
727 // Tool buttons
728 file_button->setIcon(Images::icon("open")); // FIXME: change icon
729 file_button->setToolTip(tr("Load/Save"));
730 add_button->setIcon( Images::icon("plus") );
731 add_button->setToolTip( tr("Add...") );
732 remove_button->setIcon( Images::icon("minus") );
733 remove_button->setToolTip( tr("Remove...") );
734
735 // Filter edit
736#if QT_VERSION >= 0x040700
737 filter_edit->setPlaceholderText(tr("Search"));
738#endif
739
740 // Icon
741 setWindowIcon( Images::icon("logo", 64) );
742 //setWindowTitle( tr( "SMPlayer - Playlist" ) );
743}
744
745void Playlist::list() {
746 qDebug("Playlist::list");
747
748 for (int n = 0; n < count(); n++) {
749 PLItem * i = itemData(n);
750 qDebug() << "Playlist::list: filename:" << i->filename() << "name:" << i->name() << "duration:" << i->duration();
751 }
752}
753
754void Playlist::setFilter(const QString & filter) {
755 proxy->setFilterWildcard(filter);
756}
757
758void Playlist::filterEditChanged(const QString & text) {
759 qDebug() << "Playlist::filterEditChanged:" << text;
760 setFilter(text);
761
762 if (text.isEmpty()) {
763 qApp->processEvents();
764 listView->scrollTo(listView->currentIndex(), QAbstractItemView::PositionAtCenter);
765 }
766}
767
768void Playlist::setCurrentItem(int current) {
769 QModelIndex index = proxy->index(current, 0);
770 QModelIndex s_index = proxy->mapToSource(index);
771
772 //qDebug() << "Playlist::setCurrentItem: index:" << index.row() << "s_index:" << s_index.row();
773
774 int s_current = s_index.row();
775
776 PLItem * item = 0;
777 for (int n = 0; n < count(); n++) {
778 item = itemData(n);
779 if (n == s_current) {
780 item->setPlayed(true);
781 }
782 item->setCurrent( (n == s_current) );
783 }
784
785 listView->clearSelection();
786 listView->selectionModel()->setCurrentIndex(listView->model()->index(current, 0), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
787}
788
789int Playlist::findCurrentItem() {
790 //qDebug("Playlist::findCurrentItem");
791
792 static int last_current = -1;
793
794 // Check if the last found current is still the current item to save time
795 PLItem * i = itemFromProxy(last_current);
796 if (i && i->isCurrent()) {
797 //qDebug() << "Playlist::findCurrentItem: return last_current:" << last_current;
798 return last_current;
799 }
800
801 for (int n = 0; n < proxy->rowCount(); n++) {
802 if (itemFromProxy(n)->isCurrent()) {
803 last_current = n;
804 return n;
805 }
806 }
807
808 return -1;
809}
810
811void Playlist::clear() {
812 table->setRowCount(0);
813 setCurrentItem(0);
814 setModified(false);
815}
816
817int Playlist::count() {
818 return table->rowCount();
819}
820
821bool Playlist::isEmpty() {
822 return (table->rowCount() == 0);
823}
824
825bool Playlist::existsItem(int row) {
826 return (row > -1 && row < table->rowCount());
827}
828
829PLItem * Playlist::itemData(int row) {
830 QStandardItem * i = table->item(row, COL_NAME);
831 return static_cast<PLItem*>(i);
832}
833
834PLItem * Playlist::itemFromProxy(int row) {
835 QModelIndex index = proxy->index(row, 0);
836 QModelIndex s_index = proxy->mapToSource(index);
837 //qDebug() << "Playlist::itemFromProxy: index is valid:" << index.isValid() << "s_index is valid:" << s_index.isValid();
838 if (index.isValid() && s_index.isValid()) {
839 return itemData(s_index.row());
840 } else {
841 return 0;
842 }
843}
844
845/*
846void Playlist::changeItem(int row, const QString & filename, const QString name, double duration, bool played, int pos) {
847 PLItem * i = itemData(row);
848
849 int position = row + 1;
850 if (pos != -1) position = pos;
851 i->setPosition(position);
852
853 i->setFilename(filename);
854 i->setName(name);
855 i->setDuration(duration);
856 i->setPlayed(played);
857}
858*/
859
860void Playlist::addItem(QString filename, QString name, double duration, QStringList params, QString video_url) {
861 //qDebug() << "Playlist::addItem:" << filename;
862
863 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
864 filename = Helper::changeSlashes(filename);
865 #endif
866
867 if (name.isEmpty()) {
868 QFileInfo fi(filename);
869 // Let's see if it looks like a file (no dvd://1 or something)
870 if (filename.indexOf(QRegExp("^.*://.*")) == -1) {
871 // Local file
872 name = fi.fileName();
873 } else {
874 // Stream
875 name = filename;
876 }
877 }
878
879 PLItem * i = new PLItem(filename, name, duration);
880 i->setExtraParams(params);
881 i->setVideoURL(video_url);
882 i->setPosition(count()+1);
883 table->appendRow(i->items());
884
885 if (findCurrentItem() == -1) setCurrentItem(0);
886
887/*
888#if !PL_ALLOW_DUPLICATES
889 // Test if already is in the list
890 bool exists = false;
891 for ( int n = 0; n < pl.count(); n++) {
892 if ( pl[n].filename() == filename ) {
893 exists = true;
894 int last_item = pl.count()-1;
895 pl.move(n, last_item);
896 qDebug("Playlist::addItem: item already in list (%d), moved to %d", n, last_item);
897 if (current_item > -1) {
898 if (current_item > n) current_item--;
899 else
900 if (current_item == n) current_item = last_item;
901 }
902 break;
903 }
904 }
905
906 if (!exists) {
907#endif
908 if (name.isEmpty()) {
909 QFileInfo fi(filename);
910 // Let's see if it looks like a file (no dvd://1 or something)
911 if (filename.indexOf(QRegExp("^.*://.*")) == -1) {
912 // Local file
913 name = fi.fileName(); //fi.baseName(true);
914 } else {
915 // Stream
916 name = filename;
917 }
918 }
919 pl.append( PlaylistItem(filename, name, duration) );
920 //setModified( true ); // Better set the modified on a higher level
921#if !PL_ALLOW_DUPLICATES
922 } else {
923 qDebug("Playlist::addItem: item not added, already in the list");
924 }
925#endif
926*/
927}
928
929
930void Playlist::load_m3u(QString file, M3UFormat format) {
931 bool utf8 = false;
932 if (format == DetectFormat) {
933 utf8 = (QFileInfo(file).suffix().toLower() == "m3u8");
934 } else {
935 utf8 = (format == M3U8);
936 }
937
938 qDebug() << "Playlist::load_m3u: utf8:" << utf8;
939
940 QRegExp m3u_id("^#EXTM3U|^#M3U");
941 QRegExp info("^#EXTINF:(.*),(.*)");
942
943 QFile f( file );
944 if ( f.open( QIODevice::ReadOnly ) ) {
945 playlist_path = QFileInfo(file).path();
946
947 clear();
948 QString filename="";
949 QString name="";
950 double duration=0;
951 QStringList extra_params;
952
953 QTextStream stream( &f );
954
955 if (utf8)
956 stream.setCodec("UTF-8");
957 else
958 stream.setCodec(QTextCodec::codecForLocale());
959
960 QString line;
961 while ( !stream.atEnd() ) {
962 line = stream.readLine().trimmed();
963 if (line.isEmpty()) continue; // Ignore empty lines
964
965 qDebug( "Playlist::load_m3u: line: '%s'", line.toUtf8().data() );
966 if (m3u_id.indexIn(line)!=-1) {
967 //#EXTM3U
968 // Ignore line
969 }
970 else
971 /*
972 if (info.indexIn(line)!=-1) {
973 duration = info.cap(1).toDouble();
974 name = info.cap(2);
975 qDebug("Playlist::load_m3u: name: '%s', duration: %f", name.toUtf8().data(), duration );
976 }
977 */
978 if (line.startsWith("#EXTINF:")) {
979 QStringList fields = line.mid(8).split(",");
980 //qDebug() << "Playlist::load_m3u: fields:" << fields;
981 if (fields.count() >= 1) duration = fields[0].toDouble();
982 if (fields.count() >= 2) name = fields[1];
983 }
984 else
985 if (line.startsWith("#EXTVLCOPT:")) {
986 QString par = line.mid(11);
987 qDebug() << "Playlist::load_m3u: EXTVLCOPT:" << par;
988 extra_params << par;
989 }
990 else
991 if (line.startsWith("#")) {
992 // Comment
993 // Ignore
994 } else {
995 filename = line;
996 QFileInfo fi(filename);
997 if (fi.exists()) {
998 filename = fi.absoluteFilePath();
999 }
1000 if (!fi.exists()) {
1001 if (QFileInfo( playlist_path + "/" + filename).exists() ) {
1002 filename = playlist_path + "/" + filename;
1003 }
1004 }
1005 name.replace("&#44;", ",");
1006 //qDebug() << "Playlist::load_m3u: extra_params:" << extra_params;
1007 addItem( filename, name, duration, extra_params );
1008 name="";
1009 duration = 0;
1010 extra_params.clear();
1011 }
1012 }
1013 f.close();
1014 //list();
1015
1016 setPlaylistFilename(file);
1017 setModified( false );
1018
1019 if (start_play_on_load) startPlay();
1020 }
1021}
1022
1023void Playlist::load_pls(QString file) {
1024 qDebug("Playlist::load_pls");
1025
1026 if (!QFile::exists(file)) {
1027 qDebug("Playlist::load_pls: '%s' doesn't exist, doing nothing", file.toUtf8().constData());
1028 return;
1029 }
1030
1031 playlist_path = QFileInfo(file).path();
1032
1033 QSettings set(file, QSettings::IniFormat);
1034 set.beginGroup("playlist");
1035
1036 if (set.status() == QSettings::NoError) {
1037 clear();
1038 QString filename;
1039 QString name;
1040 double duration;
1041
1042 int num_items = set.value("NumberOfEntries", 0).toInt();
1043
1044 for (int n=0; n < num_items; n++) {
1045 filename = set.value("File"+QString::number(n+1), "").toString();
1046 name = set.value("Title"+QString::number(n+1), "").toString();
1047 duration = (double) set.value("Length"+QString::number(n+1), 0).toInt();
1048
1049 QFileInfo fi(filename);
1050 if (fi.exists()) {
1051 filename = fi.absoluteFilePath();
1052 }
1053 if (!fi.exists()) {
1054 if (QFileInfo( playlist_path + "/" + filename).exists() ) {
1055 filename = playlist_path + "/" + filename;
1056 }
1057 }
1058 addItem( filename, name, duration );
1059 }
1060 }
1061
1062 set.endGroup();
1063
1064 //list();
1065
1066 setPlaylistFilename(file);
1067 setModified( false );
1068
1069 if (set.status() == QSettings::NoError && start_play_on_load) startPlay();
1070}
1071
1072void Playlist::loadXSPF(const QString & filename) {
1073 qDebug() << "Playlist::loadXSPF:" << filename;
1074
1075 QFile f(filename);
1076 if (!f.open(QIODevice::ReadOnly)) {
1077 return;
1078 }
1079
1080 QDomDocument dom_document;
1081 bool ok = dom_document.setContent(f.readAll());
1082 qDebug() << "Playlist::loadXSPF: success:" << ok;
1083 if (!ok) return;
1084
1085 QDomNode root = dom_document.documentElement();
1086 qDebug() << "Playlist::loadXSPF: tagname:" << root.toElement().tagName();
1087
1088 QDomNode child = root.firstChildElement("trackList");
1089 if (!child.isNull()) {
1090 clear();
1091
1092 qDebug() << "Playlist::loadXSPF: child:" << child.nodeName();
1093 QDomNode track = child.firstChildElement("track");
1094 while (!track.isNull()) {
1095 QString location = QUrl::fromPercentEncoding(track.firstChildElement("location").text().toLatin1());
1096 QString title = track.firstChildElement("title").text();
1097 int duration = track.firstChildElement("duration").text().toInt();
1098
1099 qDebug() << "Playlist::loadXSPF: location:" << location;
1100 qDebug() << "Playlist::loadXSPF: title:" << title;
1101 qDebug() << "Playlist::loadXSPF: duration:" << duration;
1102
1103 addItem( location, title, (double) duration / 1000 );
1104
1105 track = track.nextSiblingElement("track");
1106 }
1107
1108 //list();
1109 setPlaylistFilename(filename);
1110 setModified( false );
1111 if (start_play_on_load) startPlay();
1112 }
1113}
1114
1115bool Playlist::save_m3u(QString file) {
1116 qDebug() << "Playlist::save_m3u:" << file;
1117
1118 QString dir_path = QFileInfo(file).path();
1119 if (!dir_path.endsWith("/")) dir_path += "/";
1120
1121 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
1122 dir_path = Helper::changeSlashes(dir_path);
1123 #endif
1124
1125 qDebug() << "Playlist::save_m3u: dir_path:" << dir_path;
1126
1127 bool utf8 = (QFileInfo(file).suffix().toLower() == "m3u8");
1128
1129 QFile f( file );
1130 if ( f.open( QIODevice::WriteOnly ) ) {
1131 QTextStream stream( &f );
1132
1133 if (utf8)
1134 stream.setCodec("UTF-8");
1135 else
1136 stream.setCodec(QTextCodec::codecForLocale());
1137
1138 QString filename;
1139 QString name;
1140
1141 stream << "#EXTM3U" << "\n";
1142 stream << "# Playlist created by SMPlayer " << Version::printable() << " \n";
1143
1144 for (int n = 0; n < count(); n++) {
1145 PLItem * i = itemData(n);
1146 filename = i->filename();
1147 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
1148 filename = Helper::changeSlashes(filename);
1149 #endif
1150 name = i->name();
1151 name.replace(",", "&#44;");
1152 stream << "#EXTINF:";
1153 stream << i->duration() << ",";
1154 stream << name << "\n";
1155
1156 // Save extra params
1157 QStringList params = i->extraParams();
1158 foreach(QString par, params) {
1159 stream << "#EXTVLCOPT:" << par << "\n";
1160 }
1161
1162 // Try to save the filename as relative instead of absolute
1163 if (filename.startsWith( dir_path )) {
1164 filename = filename.mid( dir_path.length() );
1165 }
1166 stream << filename << "\n";
1167 }
1168 f.close();
1169
1170 setPlaylistFilename(file);
1171 setModified( false );
1172 return true;
1173 } else {
1174 return false;
1175 }
1176}
1177
1178
1179bool Playlist::save_pls(QString file) {
1180 qDebug() << "Playlist::save_pls:" << file;
1181
1182 QString dir_path = QFileInfo(file).path();
1183 if (!dir_path.endsWith("/")) dir_path += "/";
1184
1185 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
1186 dir_path = Helper::changeSlashes(dir_path);
1187 #endif
1188
1189 qDebug() << "Playlist::save_pls: dir_path:" << dir_path;
1190
1191 QSettings set(file, QSettings::IniFormat);
1192 set.beginGroup( "playlist");
1193
1194 QString filename;
1195
1196 for (int n = 0; n < count(); n++) {
1197 PLItem * i = itemData(n);
1198 filename = i->filename();
1199 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
1200 filename = Helper::changeSlashes(filename);
1201 #endif
1202
1203 // Try to save the filename as relative instead of absolute
1204 if (filename.startsWith( dir_path )) {
1205 filename = filename.mid( dir_path.length() );
1206 }
1207
1208 set.setValue("File"+QString::number(n+1), filename);
1209 set.setValue("Title"+QString::number(n+1), i->name());
1210 set.setValue("Length"+QString::number(n+1), (int) i->duration());
1211 }
1212
1213 set.setValue("NumberOfEntries", count());
1214 set.setValue("Version", 2);
1215
1216 set.endGroup();
1217
1218 set.sync();
1219
1220 bool ok = (set.status() == QSettings::NoError);
1221 if (ok) {
1222 setPlaylistFilename(file);
1223 setModified( false );
1224 }
1225
1226 return ok;
1227}
1228
1229bool Playlist::saveXSPF(const QString & filename) {
1230 qDebug() << "Playlist::saveXSPF:" << filename;
1231
1232 QFile f(filename);
1233 if (f.open( QIODevice::WriteOnly)) {
1234 QTextStream stream(&f);
1235 stream.setCodec("UTF-8");
1236
1237 stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1238 stream << "<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\">\n";
1239 stream << "\t<trackList>\n";
1240
1241 for (int n = 0; n < count(); n++) {
1242 PLItem * i = itemData(n);
1243 QString location = i->filename();
1244 qDebug() << "Playlist::saveXSPF:" << location;
1245
1246 bool is_local = QFile::exists(location);
1247
1248 #ifdef Q_OS_WIN
1249 if (is_local) {
1250 location.replace("\\", "/");
1251 }
1252 #endif
1253 //qDebug() << "Playlist::saveXSPF:" << location;
1254
1255 QUrl url(location);
1256 location = url.toEncoded();
1257 //qDebug() << "Playlist::saveXSPF:" << location;
1258
1259 if (!location.startsWith("file:") && is_local) {
1260 #ifdef Q_OS_WIN
1261 location = "file:///" + location;
1262 #else
1263 location = "file://" + location;
1264 #endif
1265 }
1266
1267 QString title = i->name();
1268 int duration = i->duration() * 1000;
1269
1270 #if QT_VERSION >= 0x050000
1271 location = location.toHtmlEscaped();
1272 title = title.toHtmlEscaped();
1273 #else
1274 location = Qt::escape(location);
1275 title = Qt::escape(title);
1276 #endif
1277
1278 stream << "\t\t<track>\n";
1279 stream << "\t\t\t<location>" << location << "</location>\n";
1280 stream << "\t\t\t<title>" << title << "</title>\n";
1281 stream << "\t\t\t<duration>" << duration << "</duration>\n";
1282 stream << "\t\t</track>\n";
1283 }
1284
1285 stream << "\t</trackList>\n";
1286 stream << "</playlist>\n";
1287
1288 setPlaylistFilename(filename);
1289 setModified(false);
1290 return true;
1291 } else {
1292 return false;
1293 }
1294}
1295
1296
1297void Playlist::load() {
1298 if (maybeSave()) {
1299 Extensions e;
1300 QString s = MyFileDialog::getOpenFileName(
1301 this, tr("Choose a file"),
1302 lastDir(),
1303 tr("Playlists") + e.playlist().forFilter() + ";;" + tr("All files") +" (*)");
1304
1305 if (!s.isEmpty()) {
1306 latest_dir = QFileInfo(s).absolutePath();
1307
1308 QString suffix = QFileInfo(s).suffix().toLower();
1309 if (suffix == "pls") {
1310 load_pls(s);
1311 }
1312 else
1313 if (suffix == "xspf") {
1314 loadXSPF(s);
1315 }
1316 else {
1317 load_m3u(s);
1318 }
1319 //listView->resizeColumnsToContents();
1320 }
1321 }
1322}
1323
1324bool Playlist::saveCurrentPlaylist() {
1325 qDebug("Playlist::saveCurrentPlaylist");
1326 return save(playlistFilename());
1327}
1328
1329bool Playlist::save(const QString & filename) {
1330 qDebug() << "Playlist::save:" << filename;
1331
1332 QString s = filename;
1333
1334 if (s.isEmpty()) {
1335 Extensions e;
1336 s = MyFileDialog::getSaveFileName(
1337 this, tr("Choose a filename"),
1338 lastDir(),
1339 tr("Playlists") + e.playlist().forFilter() + ";;" + tr("All files") +" (*)");
1340 }
1341
1342 if (!s.isEmpty()) {
1343 // If filename has no extension, add it
1344 if (QFileInfo(s).suffix().isEmpty()) {
1345 s = s + ".m3u";
1346 }
1347 if (QFileInfo(s).exists()) {
1348 int res = QMessageBox::question( this,
1349 tr("Confirm overwrite?"),
1350 tr("The file %1 already exists.\n"
1351 "Do you want to overwrite?").arg(s),
1352 QMessageBox::Yes,
1353 QMessageBox::No,
1354 QMessageBox::NoButton);
1355 if (res == QMessageBox::No ) {
1356 return false;
1357 }
1358 }
1359 latest_dir = QFileInfo(s).absolutePath();
1360
1361 QString suffix = QFileInfo(s).suffix().toLower();
1362 if (suffix == "pls") {
1363 return save_pls(s);
1364 }
1365 else
1366 if (suffix == "xspf") {
1367 return saveXSPF(s);
1368 }
1369 else {
1370 return save_m3u(s);
1371 }
1372
1373 } else {
1374 return false;
1375 }
1376}
1377
1378bool Playlist::maybeSave() {
1379 if (!isModified()) return true;
1380
1381 int res = QMessageBox::question( this,
1382 tr("Playlist modified"),
1383 tr("There are unsaved changes, do you want to save the playlist?"),
1384 QMessageBox::Yes,
1385 QMessageBox::No,
1386 QMessageBox::Cancel);
1387
1388 switch (res) {
1389 case QMessageBox::No : return true; // Discard changes
1390 case QMessageBox::Cancel : return false; // Cancel operation
1391 default : return save();
1392 }
1393}
1394
1395void Playlist::playCurrent() {
1396 int current = listView->currentIndex().row();
1397 if (current > -1) {
1398 playItem(current);
1399 }
1400}
1401
1402void Playlist::itemActivated(const QModelIndex & index ) {
1403 qDebug() << "Playlist::itemActivated: row:" << index.row();
1404 playItem(index.row());
1405}
1406
1407void Playlist::showPopup(const QPoint & pos) {
1408 qDebug("Playlist::showPopup: x: %d y: %d", pos.x(), pos.y() );
1409
1410 QModelIndex index = listView->currentIndex();
1411 if (!index.isValid()) {
1412 playAct->setEnabled(false);
1413 removeSelectedAct->setEnabled(false);
1414 editAct->setEnabled(false);
1415 #ifdef PLAYLIST_DELETE_FROM_DISK
1416 deleteSelectedFileFromDiskAct->setEnabled(false);
1417 #endif
1418 copyURLAct->setEnabled(false);
1419 openFolderAct->setEnabled(false);
1420 #ifdef CHROMECAST_SUPPORT
1421 playOnChromecastAct->setEnabled(false);
1422 #else
1423 openURLInWebAct->setEnabled(false);
1424 #endif
1425 } else {
1426 playAct->setEnabled(true);
1427 removeSelectedAct->setEnabled(true);
1428 editAct->setEnabled(true);
1429 #ifdef PLAYLIST_DELETE_FROM_DISK
1430 deleteSelectedFileFromDiskAct->setEnabled(true);
1431 #endif
1432 copyURLAct->setEnabled(true);
1433 openFolderAct->setEnabled(true);
1434 #ifdef CHROMECAST_SUPPORT
1435 playOnChromecastAct->setEnabled(true);
1436 #else
1437 openURLInWebAct->setEnabled(true);
1438 #endif
1439
1440 QModelIndex s_index = proxy->mapToSource(index);
1441 int current = s_index.row();
1442 PLItem * i = itemData(current);
1443 QString filename = i->filename();
1444 QFileInfo fi(filename);
1445
1446 if (fi.exists()) {
1447 copyURLAct->setText( tr("&Copy file path to clipboard") );
1448 #ifndef CHROMECAST_SUPPORT
1449 openURLInWebAct->setEnabled(false);
1450 #endif
1451 } else {
1452 copyURLAct->setText( tr("&Copy URL to clipboard") );
1453 openFolderAct->setEnabled(false);
1454 }
1455 }
1456
1457 if (!popup->isVisible()) {
1458 popup->move( listView->viewport()->mapToGlobal(pos) );
1459 popup->show();
1460 }
1461}
1462
1463void Playlist::startPlay() {
1464 // Start to play
1465 if ( shuffleAct->isChecked() )
1466 playItem( chooseRandomItem() );
1467 else
1468 playItem(0);
1469}
1470
1471void Playlist::playItem( int n ) {
1472 qDebug("Playlist::playItem: %d (count: %d)", n, proxy->rowCount());
1473
1474 if ( (n >= proxy->rowCount()) || (n < 0) ) {
1475 qDebug("Playlist::playItem: out of range");
1476 emit playlistEnded();
1477 return;
1478 }
1479
1480 PLItem * i = itemFromProxy(n);
1481 QString filename = i->filename();
1482 QStringList params = i->extraParams();
1483
1484 if (!filename.isEmpty()) {
1485 setCurrentItem(n);
1486
1487 if (!params.isEmpty()) {
1488 emit requestToPlayStream(filename, params);
1489 } else {
1490 if (play_files_from_start) {
1491 emit requestToPlayFile(filename, 0);
1492 } else {
1493 emit requestToPlayFile(filename);
1494 }
1495 }
1496 }
1497}
1498
1499void Playlist::playNext() {
1500 qDebug("Playlist::playNext");
1501
1502 if (shuffleAct->isChecked()) {
1503 // Shuffle
1504 int chosen_item = chooseRandomItem();
1505 qDebug("Playlist::playNext: chosen_item: %d", chosen_item);
1506 if (chosen_item == -1) {
1507 clearPlayedTag();
1508 if (repeatAct->isChecked()) {
1509 chosen_item = chooseRandomItem();
1510 if (chosen_item == -1) chosen_item = 0;
1511 }
1512 }
1513 playItem(chosen_item);
1514 } else {
1515 int current = findCurrentItem();
1516 bool finished_list = (current + 1 >= proxy->rowCount());
1517 if (finished_list) clearPlayedTag();
1518
1519 if (repeatAct->isChecked() && finished_list) {
1520 playItem(0);
1521 } else {
1522 playItem(current + 1);
1523 }
1524 }
1525}
1526
1527void Playlist::playPrev() {
1528 qDebug("Playlist::playPrev");
1529 int current = findCurrentItem() - 1;
1530 if (current >= 0) {
1531 playItem(current);
1532 } else {
1533 if (proxy->rowCount() > 1) playItem(proxy->rowCount() - 1);
1534 }
1535}
1536
1537void Playlist::playNextAuto() {
1538 qDebug("Playlist::playNextAuto");
1539 if (automatically_play_next) {
1540 playNext();
1541 } else {
1542 emit playlistEnded();
1543 }
1544}
1545
1546void Playlist::resumePlay() {
1547 qDebug("Playlist::resumePlay");
1548
1549 if (count() > 0) {
1550 int current = findCurrentItem();
1551 if (current < 0) current = 0;
1552 playItem(current);
1553 }
1554}
1555
1556void Playlist::getMediaInfo(const MediaData & mdat) {
1557 qDebug("Playlist::getMediaInfo");
1558
1559 QString filename = mdat.filename;
1560 double duration = mdat.duration;
1561 QString artist = mdat.clip_artist;
1562 QString video_url = mdat.stream_path;
1563
1564 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
1565 filename = Helper::changeSlashes(filename);
1566 #endif
1567
1568 QString name;
1569 if (change_name) {
1570 name = mdat.clip_name;
1571 if (name.isEmpty()) name = mdat.stream_title;
1572
1573 if (name.isEmpty()) {
1574 QFileInfo fi(filename);
1575 if (fi.exists()) {
1576 // Local file
1577 name = fi.fileName();
1578 } else {
1579 // Stream
1580 name = filename;
1581 }
1582 }
1583 if (!artist.isEmpty()) name = artist + " - " + name;
1584 }
1585
1586 for (int n = 0; n < count(); n++) {
1587 PLItem * i = itemData(n);
1588 if (i->filename() == filename) {
1589 // Found item
1590 bool modified_name = !(i->filename().endsWith(i->name()));
1591 if (i->duration() < 1) {
1592 if (!modified_name && !name.isEmpty()) {
1593 i->setName(name);
1594 }
1595 i->setDuration(duration);
1596 }
1597 else
1598 // Edited name (sets duration to 1)
1599 if (i->duration() == 1) {
1600 i->setDuration(duration);
1601 }
1602 i->setVideoURL(video_url);
1603 }
1604 }
1605}
1606
1607// Add current file to playlist
1608void Playlist::addCurrentFile() {
1609 qDebug("Playlist::addCurrentFile");
1610 emit requestToAddCurrentFile();
1611}
1612
1613void Playlist::addFiles() {
1614 Extensions e;
1615 QStringList files = MyFileDialog::getOpenFileNames(
1616 this, tr("Select one or more files to open"),
1617 lastDir(),
1618 tr("Multimedia") + e.multimedia().forFilter() + ";;" +
1619 tr("All files") +" (*.*)" );
1620
1621 if (files.count() != 0) {
1622 addFiles(files);
1623 setModified(true);
1624 }
1625}
1626
1627void Playlist::addFiles(QStringList files, AutoGetInfo auto_get_info) {
1628 qDebug("Playlist::addFiles");
1629
1630 #if USE_INFOPROVIDER
1631 bool get_info = (auto_get_info == GetInfo);
1632 if (auto_get_info == UserDefined) {
1633 get_info = automatically_get_info;
1634 }
1635
1636 MediaData data;
1637 setCursor(Qt::WaitCursor);
1638 #endif
1639
1640 QString initial_file;
1641 if (count() == 1) initial_file = itemData(0)->filename();
1642 int new_current_item = -1;
1643
1644 for (int n = 0; n < files.count(); n++) {
1645 QString name = "";
1646 double duration = 0;
1647 #if USE_INFOPROVIDER
1648 if ( (get_info) && (QFile::exists(files[n])) ) {
1649 data = InfoProvider::getInfo(files[n]);
1650 name = data.displayName();
1651 duration = data.duration;
1652 //qApp->processEvents();
1653 }
1654 #endif
1655
1656 //qDebug() << "Playlist::addFiles: comparing:" << initial_file << "with" << files[n];
1657
1658 if (!initial_file.isEmpty() && files[n] == initial_file) {
1659 PLItem * first_item = itemData(0);
1660 name = first_item->name();
1661 duration = first_item->duration();
1662 table->removeRow(0);
1663 new_current_item = n;
1664 }
1665 addItem(files[n], name, duration);
1666
1667 if (QFile::exists(files[n])) {
1668 latest_dir = QFileInfo(files[n]).absolutePath();
1669 }
1670 }
1671 #if USE_INFOPROVIDER
1672 unsetCursor();
1673 #endif
1674
1675 if (new_current_item != -1) setCurrentItem(new_current_item);
1676
1677 qDebug() << "Playlist::addFiles: latest_dir:" << latest_dir;
1678}
1679
1680void Playlist::addFile(QString file, AutoGetInfo auto_get_info) {
1681 addFiles( QStringList() << file, auto_get_info );
1682}
1683
1684void Playlist::addDirectory() {
1685 QString s = MyFileDialog::getExistingDirectory(
1686 this, tr("Choose a directory"),
1687 lastDir() );
1688
1689 if (!s.isEmpty()) {
1690 addDirectory(s);
1691 latest_dir = s;
1692 }
1693}
1694
1695void Playlist::addUrls() {
1696 MultilineInputDialog d(this);
1697 if (d.exec() == QDialog::Accepted) {
1698 QStringList urls = d.lines();
1699 foreach(QString u, urls) {
1700 if (!u.isEmpty()) addItem( u, "", 0 );
1701 }
1702 setModified(true);
1703 }
1704}
1705
1706void Playlist::addOneDirectory(QString dir) {
1707 QStringList filelist;
1708
1709 Extensions e;
1710 QRegExp rx_ext(e.multimedia().forRegExp());
1711 rx_ext.setCaseSensitivity(Qt::CaseInsensitive);
1712
1713 QStringList dir_list = QDir(dir).entryList();
1714
1715 QString filename;
1716 QStringList::Iterator it = dir_list.begin();
1717 while( it != dir_list.end() ) {
1718 filename = dir;
1719 if (filename.right(1)!="/") filename += "/";
1720 filename += (*it);
1721 QFileInfo fi(filename);
1722 if (!fi.isDir()) {
1723 if (rx_ext.indexIn(fi.suffix()) > -1) {
1724 filelist << filename;
1725 }
1726 }
1727 ++it;
1728 }
1729 addFiles(filelist);
1730}
1731
1732void Playlist::addDirectory(QString dir) {
1733 addOneDirectory(dir);
1734
1735 if (recursive_add_directory) {
1736 QFileInfoList dir_list = QDir(dir).entryInfoList(QStringList() << "*", QDir::AllDirs | QDir::NoDotAndDotDot);
1737 for (int n=0; n < dir_list.count(); n++) {
1738 if (dir_list[n].isDir()) {
1739 qDebug("Playlist::addDirectory: adding directory: %s", dir_list[n].filePath().toUtf8().data());
1740 addDirectory(dir_list[n].filePath());
1741 }
1742 }
1743 }
1744 setModified(true);
1745}
1746
1747// Remove selected items
1748void Playlist::removeSelected() {
1749 qDebug("Playlist::removeSelected");
1750
1751 QModelIndexList indexes = listView->selectionModel()->selectedRows();
1752 int count = indexes.count();
1753
1754 for (int n = count; n > 0; n--) {
1755 QModelIndex s_index = proxy->mapToSource(indexes.at(n-1));
1756 table->removeRow(s_index.row());
1757 setModified(true);
1758 }
1759
1760 if (isEmpty()) setModified(false);
1761
1762 if (findCurrentItem() == -1) {
1763 int current = indexes.at(0).row() - 1;
1764 if (current < 0) current = 0;
1765 setCurrentItem(current);
1766 }
1767}
1768
1769void Playlist::removeAll() {
1770 clear();
1771 setPlaylistFilename("");
1772}
1773
1774void Playlist::clearPlayedTag() {
1775 for (int n = 0; n < count(); n++) {
1776 itemData(n)->setPlayed(false);
1777 }
1778}
1779
1780int Playlist::chooseRandomItem() {
1781 qDebug( "Playlist::chooseRandomItem");
1782
1783 QList<int> fi; //List of not played items (free items)
1784 for (int n = 0; n < proxy->rowCount(); n++) {
1785 if (!itemFromProxy(n)->played()) fi.append(n);
1786 }
1787
1788 qDebug("Playlist::chooseRandomItem: free items: %d", fi.count() );
1789
1790 if (fi.count() == 0) return -1; // none free
1791
1792 qDebug("Playlist::chooseRandomItem: items: ");
1793 for (int i = 0; i < fi.count(); i++) {
1794 qDebug("Playlist::chooseRandomItem: * item: %d", fi[i]);
1795 }
1796
1797 int selected = (qrand() % fi.count());
1798 qDebug("Playlist::chooseRandomItem: selected item: %d (%d)", selected, fi[selected]);
1799 return fi[selected];
1800}
1801
1802void Playlist::upItem() {
1803 QModelIndex index = listView->currentIndex();
1804 QModelIndex s_index = proxy->mapToSource(index);
1805
1806 QModelIndex prev = listView->model()->index(index.row()-1, 0);
1807 QModelIndex s_prev = proxy->mapToSource(prev);
1808
1809 qDebug() << "Playlist::upItem: row:" << index.row() << "source row:" << s_index.row();
1810 qDebug() << "Playlist::upItem: previous row:" << prev.row() << "previous source row:" << s_prev.row();
1811
1812 if (s_index.isValid() && s_prev.isValid()) {
1813 int row = s_index.row();
1814 int prev_row = s_prev.row();
1815
1816 int pos_num_current = itemData(row)->position();
1817 int pos_num_prev = itemData(prev_row)->position();
1818
1819 qDebug() << "Playlist::upItem: pos_num_current:" << pos_num_current << "pos_num_prev:" << pos_num_prev;
1820
1821 itemData(row)->setPosition(pos_num_prev);
1822 itemData(prev_row)->setPosition(pos_num_current);
1823
1824 QList<QStandardItem*> cells = table->takeRow(row);
1825 table->insertRow(s_prev.row(), cells);
1826 listView->selectionModel()->setCurrentIndex(listView->model()->index(index.row()-1, 0), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
1827
1828 setModified(true);
1829 }
1830}
1831
1832void Playlist::downItem() {
1833 qDebug("Playlist::downItem");
1834
1835 QModelIndex index = listView->currentIndex();
1836 QModelIndex s_index = proxy->mapToSource(index);
1837
1838 QModelIndex next = listView->model()->index(index.row()+1, 0);
1839 QModelIndex s_next = proxy->mapToSource(next);
1840
1841 qDebug() << "Playlist::downItem: row:" << index.row() << "source row:" << s_index.row();
1842 qDebug() << "Playlist::downItem: next row:" << next.row() << "next source row:" << s_next.row();
1843
1844 if (s_index.isValid() && s_next.isValid()) {
1845 int row = s_index.row();
1846 int next_row = s_next.row();
1847
1848 int pos_num_current = itemData(row)->position();
1849 int pos_num_next = itemData(next_row)->position();
1850
1851 qDebug() << "Playlist::downItem: pos_num_current:" << pos_num_current << "pos_num_next:" << pos_num_next;
1852
1853 itemData(row)->setPosition(pos_num_next);
1854 itemData(next_row)->setPosition(pos_num_current);
1855
1856 QList<QStandardItem*> cells = table->takeRow(row);
1857 table->insertRow(s_next.row(), cells);
1858 listView->selectionModel()->setCurrentIndex(listView->model()->index(index.row()+1, 0), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
1859
1860 setModified(true);
1861 }
1862}
1863
1864void Playlist::editCurrentItem() {
1865 QModelIndex v_index = listView->currentIndex();
1866 QModelIndex s_index = proxy->mapToSource(v_index);
1867 qDebug() << "Playlist::editCurrentItem: row:" << v_index.row() << "source row:" << s_index.row();
1868 int current = s_index.row();
1869 if (current > -1) editItem(current);
1870}
1871
1872void Playlist::editItem(int row) {
1873 qDebug() << "Playlist::editItem:" << row;
1874
1875 PLItem * i = itemData(row);
1876 QString current_name = i->name();
1877 if (current_name.isEmpty()) current_name = i->filename();
1878
1879 bool ok;
1880 QString text = QInputDialog::getText( this,
1881 tr("Edit name"),
1882 tr("Type the name that will be displayed in the playlist for this file:"),
1883 QLineEdit::Normal,
1884 current_name, &ok );
1885 if ( ok && !text.isEmpty() ) {
1886 // user entered something and pressed OK
1887 i->setName(text);
1888
1889 // If duration == 0 the name will be overwritten!
1890 if (i->duration() < 1) i->setDuration(1);
1891
1892 setModified( true );
1893 }
1894}
1895
1896#ifdef PLAYLIST_DELETE_FROM_DISK
1897void Playlist::deleteSelectedFileFromDisk() {
1898 qDebug("Playlist::deleteSelectedFileFromDisk");
1899
1900 QModelIndex index = listView->currentIndex();
1901 if (!index.isValid()) return;
1902
1903 QModelIndex s_index = proxy->mapToSource(index);
1904
1905 qDebug() << "Playlist::deleteSelectedFileFromDisk: row:" << index.row() << "source row:" << s_index.row();
1906 int current = s_index.row();
1907
1908 // Select only the current row
1909 listView->selectionModel()->setCurrentIndex(listView->model()->index(index.row(), 0), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
1910
1911 QString filename = itemData(current)->filename();
1912 qDebug() << "Playlist::deleteSelectedFileFromDisk: current file:" << filename;
1913
1914 QFileInfo fi(filename);
1915 if (fi.exists() && fi.isFile() && fi.isWritable()) {
1916 // Ask the user for confirmation
1917 int res = QMessageBox::question(this, tr("Confirm deletion"),
1918 tr("You're about to DELETE the file '%1' from your drive.").arg(filename) + "<br>"+
1919 tr("This action cannot be undone. Are you sure you want to proceed?"),
1920 QMessageBox::Yes, QMessageBox::No);
1921
1922 if (res == QMessageBox::Yes) {
1923 // Delete file
1924 #if SIMULATE_FILE_DELETION
1925 bool success = true;
1926 #else
1927 bool success = QFile::remove(filename);
1928 #endif
1929
1930 if (success) {
1931 // Remove item from the playlist
1932 table->removeRow(current);
1933 if (findCurrentItem() == -1) {
1934 if (current > 0) setCurrentItem(current-1); else setCurrentItem(0);
1935 }
1936 } else {
1937 QMessageBox::warning(this, tr("Deletion failed"),
1938 tr("It wasn't possible to delete '%1'").arg(filename));
1939 }
1940 }
1941 } else {
1942 qDebug("Playlist::deleteSelectedFileFromDisk: file doesn't exists, it's not a file or it's not writable");
1943 QMessageBox::information(this, tr("Error deleting the file"),
1944 tr("It's not possible to delete '%1' from the filesystem.").arg(filename));
1945 }
1946}
1947#endif
1948
1949void Playlist::copyURL() {
1950 qDebug("Playlist::copyURL");
1951
1952 QModelIndexList indexes = listView->selectionModel()->selectedRows();
1953 int count = indexes.count();
1954
1955 QString text;
1956
1957 for (int n = 0; n < count; n++) {
1958 QModelIndex s_index = proxy->mapToSource(indexes.at(n));
1959 int current = s_index.row();
1960 text += itemData(current)->filename();
1961 if (n < count-1) {
1962 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
1963 text += "\r\n";
1964 #else
1965 text += "\n";
1966 #endif
1967 }
1968 }
1969
1970 if (!text.isEmpty()) QApplication::clipboard()->setText(text);
1971}
1972
1973void Playlist::openFolder() {
1974 qDebug("Playlist::openFolder");
1975
1976 QModelIndex index = listView->currentIndex();
1977 if (!index.isValid()) return;
1978 QModelIndex s_index = proxy->mapToSource(index);
1979 int current = s_index.row();
1980 PLItem * i = itemData(current);
1981 QString filename = i->filename();
1982
1983 qDebug() << "Playlist::openFolder: filename:" << filename;
1984
1985 QFileInfo fi(filename);
1986 if (fi.exists()) {
1987 QString src_folder = fi.absolutePath();
1988 QDesktopServices::openUrl(QUrl::fromLocalFile(src_folder));
1989 }
1990}
1991
1992#ifdef CHROMECAST_SUPPORT
1993void Playlist::playOnChromecast() {
1994 qDebug("Playlist::playOnChromecast");
1995
1996 QModelIndex index = listView->currentIndex();
1997 if (!index.isValid()) return;
1998 QModelIndex s_index = proxy->mapToSource(index);
1999 int current = s_index.row();
2000 PLItem * i = itemData(current);
2001 QString filename = i->filename();
2002 QString video_url = i->videoURL();
2003
2004 QString url = filename;
2005 if (!video_url.isEmpty()) url = video_url;
2006
2007 if (QFile::exists(filename)) {
2008 Chromecast::instance()->openLocal(url, i->name());
2009 } else {
2010 Chromecast::instance()->openStream(url, i->name());
2011 }
2012}
2013#else
2014void Playlist::openURLInWeb() {
2015 qDebug("Playlist::openURLInWeb");
2016
2017 QModelIndex index = listView->currentIndex();
2018 if (!index.isValid()) return;
2019 QModelIndex s_index = proxy->mapToSource(index);
2020 int current = s_index.row();
2021 PLItem * i = itemData(current);
2022 QString filename = i->filename();
2023 QString video_url = i->videoURL();
2024
2025 QString url = filename;
2026 if (!video_url.isEmpty()) url = video_url;
2027
2028 QDesktopServices::openUrl(QUrl(url));
2029}
2030#endif
2031
2032// Drag&drop
2033void Playlist::dragEnterEvent( QDragEnterEvent *e ) {
2034 qDebug("Playlist::dragEnterEvent");
2035
2036 if (e->mimeData()->hasUrls()) {
2037 e->acceptProposedAction();
2038 }
2039}
2040
2041void Playlist::dropEvent( QDropEvent *e ) {
2042 qDebug("Playlist::dropEvent");
2043
2044 QStringList files;
2045
2046 if (e->mimeData()->hasUrls()) {
2047 QList <QUrl> l = e->mimeData()->urls();
2048 QString s;
2049 for (int n=0; n < l.count(); n++) {
2050 if (l[n].isValid()) {
2051 qDebug("Playlist::dropEvent: scheme: '%s'", l[n].scheme().toUtf8().data());
2052 if (l[n].scheme() == "file")
2053 s = l[n].toLocalFile();
2054 else
2055 s = l[n].toString();
2056 /*
2057 qDebug(" * '%s'", l[n].toString().toUtf8().data());
2058 qDebug(" * '%s'", l[n].toLocalFile().toUtf8().data());
2059 */
2060 qDebug("Playlist::dropEvent: file: '%s'", s.toUtf8().data());
2061 files.append(s);
2062 }
2063 }
2064 }
2065
2066 #ifdef Q_OS_WIN
2067 files = Helper::resolveSymlinks(files); // Check for Windows shortcuts
2068 #endif
2069 files.sort();
2070
2071 QStringList only_files;
2072 for (int n = 0; n < files.count(); n++) {
2073 if ( QFileInfo( files[n] ).isDir() ) {
2074 addDirectory( files[n] );
2075 } else {
2076 only_files.append( files[n] );
2077 }
2078 }
2079
2080 if (only_files.count() == 1) {
2081 // Check if the file is a playlist
2082 QString filename = only_files[0];
2083 QFileInfo fi(filename);
2084 QString extension = fi.suffix().toLower();
2085 if (extension == "m3u8" || extension == "m3u") { load_m3u(filename); return; }
2086 else
2087 if (extension == "pls") { load_pls(filename); return; }
2088 else
2089 if (extension == "xspf") { loadXSPF(filename); return; }
2090 }
2091
2092 addFiles( only_files );
2093 setModified(true);
2094}
2095
2096
2097void Playlist::hideEvent( QHideEvent * ) {
2098 emit visibilityChanged(false);
2099}
2100
2101void Playlist::showEvent( QShowEvent * ) {
2102 emit visibilityChanged(true);
2103}
2104
2105void Playlist::closeEvent( QCloseEvent * e ) {
2106 saveSettings();
2107 e->accept();
2108}
2109
2110void Playlist::playerFailed(QProcess::ProcessError e) {
2111 qDebug("Playlist::playerFailed");
2112 if (ignore_player_errors) {
2113 if (e != QProcess::FailedToStart) {
2114 playNext();
2115 }
2116 }
2117}
2118
2119void Playlist::playerFinishedWithError(int e) {
2120 qDebug("Playlist::playerFinishedWithError: %d", e);
2121 if (ignore_player_errors) {
2122 playNext();
2123 }
2124}
2125
2126void Playlist::maybeSaveSettings() {
2127 qDebug("Playlist::maybeSaveSettings");
2128 if (isModified()) saveSettings();
2129}
2130
2131void Playlist::saveSettings() {
2132 qDebug("Playlist::saveSettings");
2133
2134 if (!set) return;
2135
2136 set->beginGroup( "playlist");
2137
2138 set->setValue( "repeat", repeatAct->isChecked() );
2139 set->setValue( "shuffle", shuffleAct->isChecked() );
2140
2141 set->setValue( "auto_get_info", automatically_get_info );
2142 set->setValue( "recursive_add_directory", recursive_add_directory );
2143 set->setValue( "save_playlist_in_config", save_playlist_in_config );
2144 set->setValue( "play_files_from_start", play_files_from_start );
2145 set->setValue( "start_play_on_load", start_play_on_load );
2146 set->setValue( "automatically_play_next", automatically_play_next );
2147 set->setValue( "ignore_player_errors", ignore_player_errors );
2148 set->setValue( "change_name", change_name );
2149
2150 set->setValue( "row_spacing", row_spacing );
2151
2152#if !DOCK_PLAYLIST
2153 set->setValue( "size", size() );
2154#endif
2155
2156 set->setValue(QString("header_state/2/%1").arg(Helper::qtVersion()), listView->horizontalHeader()->saveState());
2157
2158 set->setValue( "sort_column", proxy->sortColumn() );
2159 set->setValue( "sort_order", proxy->sortOrder() );
2160 set->setValue( "filter_case_sensitive", filterCaseSensitive() );
2161 set->setValue( "filter", filter_edit->text() );
2162 set->setValue( "sort_case_sensitive", sortCaseSensitive() );
2163 set->setValue( "auto_sort", autoSort() );
2164
2165 set->setValue( "show_search", showSearchAct->isChecked() );
2166
2167 set->endGroup();
2168
2169 set->beginGroup( "directories");
2170 set->setValue("save_dirs", save_dirs);
2171 set->setValue("latest_dir", save_dirs ? latest_dir : "" );
2172 set->endGroup();
2173
2174 if (save_playlist_in_config) {
2175 //Save current list
2176 set->beginGroup("playlist_contents");
2177 set->beginWriteArray("items");
2178 //set->setValue( "count", count() );
2179 for (int n = 0; n < count(); n++ ) {
2180 set->setArrayIndex(n);
2181 PLItem * i = itemData(n);
2182 set->setValue( QString("item_%1_filename").arg(n), i->filename() );
2183 set->setValue( QString("item_%1_duration").arg(n), i->duration() );
2184 set->setValue( QString("item_%1_name").arg(n), i->name() );
2185 set->setValue( QString("item_%1_params").arg(n), i->extraParams() );
2186 set->setValue( QString("item_%1_video_url").arg(n), i->videoURL() );
2187 }
2188 set->endArray();
2189 set->setValue( "current_item", findCurrentItem() );
2190 set->setValue("filename", playlistFilename());
2191 set->setValue( "modified", modified );
2192
2193 set->endGroup();
2194 }
2195
2196#ifdef PLAYLIST_DOWNLOAD
2197 set->beginGroup("history");
2198 set->setValue("max_items", history_urls->maxItems());
2199 set->setValue("urls", history_urls->toStringList());
2200 set->endGroup();
2201#endif
2202
2203 if (set->contains("playlist/change_title")) set->remove("playlist/change_title");
2204 if (set->contains("playlist/sort_case_sensivity")) set->remove("playlist/sort_case_sensivity");
2205 if (set->contains("playlist/filter_case_sensivity")) set->remove("playlist/filter_case_sensivity");
2206}
2207
2208void Playlist::loadSettings() {
2209 qDebug("Playlist::loadSettings");
2210
2211 if (!set) return;
2212
2213 set->beginGroup( "playlist");
2214
2215 repeatAct->setChecked( set->value( "repeat", repeatAct->isChecked() ).toBool() );
2216 shuffleAct->setChecked( set->value( "shuffle", shuffleAct->isChecked() ).toBool() );
2217
2218 automatically_get_info = set->value( "auto_get_info", automatically_get_info ).toBool();
2219 recursive_add_directory = set->value( "recursive_add_directory", recursive_add_directory ).toBool();
2220 save_playlist_in_config = set->value( "save_playlist_in_config", save_playlist_in_config ).toBool();
2221 play_files_from_start = set->value( "play_files_from_start", play_files_from_start ).toBool();
2222 start_play_on_load = set->value( "start_play_on_load", start_play_on_load ).toBool();
2223 automatically_play_next = set->value( "automatically_play_next", automatically_play_next ).toBool();
2224 ignore_player_errors = set->value( "ignore_player_errors", ignore_player_errors ).toBool();
2225 change_name = set->value( "change_name", change_name ).toBool();
2226
2227 row_spacing = set->value( "row_spacing", row_spacing ).toInt();
2228
2229#if !DOCK_PLAYLIST
2230 resize( set->value("size", size()).toSize() );
2231#endif
2232
2233 listView->horizontalHeader()->restoreState(set->value(QString("header_state/2/%1").arg(Helper::qtVersion()), QByteArray()).toByteArray());
2234
2235 int sort_column = set->value("sort_column", COL_NUM).toInt();
2236 int sort_order = set->value("sort_order", Qt::AscendingOrder).toInt();
2237 bool filter_case_sensitive = set->value("filter_case_sensitive", false).toBool();
2238 QString filter = set->value( "filter").toString();
2239 bool sort_case_sensitive = set->value("sort_case_sensitive", false).toBool();
2240 bool auto_sort = set->value("auto_sort", false).toBool();
2241
2242 showSearchAct->setChecked( set->value( "show_search", false).toBool() );
2243
2244 set->endGroup();
2245
2246 set->beginGroup( "directories");
2247 save_dirs = set->value("save_dirs", save_dirs).toBool();
2248 if (save_dirs) {
2249 latest_dir = set->value("latest_dir", latest_dir).toString();
2250 }
2251 set->endGroup();
2252
2253 if (save_playlist_in_config) {
2254 //Load latest list
2255 set->beginGroup("playlist_contents");
2256 int count = set->beginReadArray("items");
2257
2258 QString filename, name;
2259 double duration;
2260 for (int n = 0; n < count; n++) {
2261 set->setArrayIndex(n);
2262 filename = set->value( QString("item_%1_filename").arg(n), "" ).toString();
2263 duration = set->value( QString("item_%1_duration").arg(n), -1 ).toDouble();
2264 name = set->value( QString("item_%1_name").arg(n), "" ).toString();
2265 QStringList params = set->value( QString("item_%1_params").arg(n), QStringList()).toStringList();
2266 QString video_url = set->value( QString("item_%1_video_url").arg(n), "").toString();
2267 addItem( filename, name, duration, params, video_url );
2268 }
2269 set->endArray();
2270 setCurrentItem( set->value( "current_item", -1 ).toInt() );
2271 setPlaylistFilename( set->value("filename", "").toString() );
2272 setModified( set->value( "modified", false ).toBool() );
2273
2274 set->endGroup();
2275 //listView->resizeColumnsToContents();
2276 }
2277
2278#ifdef PLAYLIST_DOWNLOAD
2279 set->beginGroup("history");
2280 history_urls->setMaxItems(set->value("max_items", 50).toInt());
2281 history_urls->fromStringList( set->value("urls", history_urls->toStringList()).toStringList() );
2282 set->endGroup();
2283#endif
2284
2285 setFilterCaseSensitive(filter_case_sensitive);
2286 setSortCaseSensitive(sort_case_sensitive);
2287 proxy->sort(sort_column, (Qt::SortOrder) sort_order);
2288 filter_edit->setText(filter);
2289 setAutoSort(auto_sort);
2290
2291 if (!listView->isColumnHidden(COL_NUM)) showPositionColumnAct->setChecked(true);
2292 if (!listView->isColumnHidden(COL_NAME)) showNameColumnAct->setChecked(true);
2293 if (!listView->isColumnHidden(COL_TIME)) showDurationColumnAct->setChecked(true);
2294 if (!listView->isColumnHidden(COL_FILENAME)) showFilenameColumnAct->setChecked(true);
2295}
2296
2297QString Playlist::lastDir() {
2298 QString last_dir = latest_dir;
2299 return last_dir;
2300}
2301
2302void Playlist::setPositionColumnVisible(bool b) {
2303 listView->setColumnHidden(COL_NUM, !b);
2304}
2305
2306void Playlist::setNameColumnVisible(bool b) {
2307 listView->setColumnHidden(COL_NAME, !b);
2308}
2309
2310void Playlist::setDurationColumnVisible(bool b) {
2311 listView->setColumnHidden(COL_TIME, !b);
2312}
2313
2314void Playlist::setFilenameColumnVisible(bool b) {
2315 listView->setColumnHidden(COL_FILENAME, !b);
2316}
2317
2318void Playlist::setAutoSort(bool b) {
2319 proxy->setDynamicSortFilter(b);
2320}
2321
2322bool Playlist::autoSort() {
2323 return proxy->dynamicSortFilter();
2324}
2325
2326void Playlist::setSortCaseSensitive(bool b) {
2327 Qt::CaseSensitivity c = b ? Qt::CaseSensitive : Qt::CaseInsensitive;
2328 proxy->setSortCaseSensitivity(c);
2329}
2330
2331bool Playlist::sortCaseSensitive() {
2332 return (proxy->sortCaseSensitivity() == Qt::CaseSensitive);
2333}
2334
2335void Playlist::setFilterCaseSensitive(bool b) {
2336 Qt::CaseSensitivity c = b ? Qt::CaseSensitive : Qt::CaseInsensitive;
2337 proxy->setFilterCaseSensitivity(c);
2338}
2339
2340bool Playlist::filterCaseSensitive() {
2341 return (proxy->filterCaseSensitivity() == Qt::CaseSensitive);
2342}
2343
2344#ifdef PLAYLIST_DOWNLOAD
2345void Playlist::openUrl() {
2346 qDebug("Playlist::openUrl");
2347
2348 InputURL d(this);
2349
2350 // Get url from clipboard
2351 QString clipboard_text = QApplication::clipboard()->text();
2352 if (!clipboard_text.isEmpty() && clipboard_text.contains("://")) {
2353 d.setURL(clipboard_text);
2354 }
2355
2356 for (int n = 0; n < history_urls->count(); n++) {
2357 d.setURL(history_urls->url(n));
2358 }
2359
2360 if (d.exec() == QDialog::Accepted ) {
2361 QString url = d.url();
2362 if (!url.isEmpty()) {
2363 history_urls->addUrl(url);
2364 openUrl(url);
2365 }
2366 }
2367}
2368
2369void Playlist::openUrl(const QString & url) {
2370 qDebug() << "Playlist::openUrl:" << url;
2371 downloader->fetchPage(url);
2372
2373 showLoadingAnimation(true);
2374}
2375
2376void Playlist::playlistDownloaded(QByteArray data) {
2377 qDebug("Playlist::playlistDownloaded");
2378 // Save to a temporary file
2379 QTemporaryFile tf;
2380 tf.open();
2381 tf.write(data);
2382 tf.close();
2383 QString tfile = tf.fileName();
2384 qDebug() << "Playlist::playlistDownloaded: tfile:" << tfile;
2385
2386 if (data.contains("#EXTM3U")) {
2387 load_m3u(tfile, M3U8);
2388 setPlaylistFilename("");
2389 }
2390 else
2391 if (data.contains("[playlist]")) {
2392 load_pls(tfile);
2393 setPlaylistFilename("");
2394 }
2395 else
2396 if (data.contains("xspf.org")) {
2397 loadXSPF(tfile);
2398 setPlaylistFilename("");
2399 }
2400 else {
2401 QMessageBox::warning(this, "SMPlayer", tr("It's not possible to load this playlist") +": "+ tr("Unrecognized format."));
2402 }
2403
2404 showLoadingAnimation(false);
2405}
2406
2407void Playlist::errorOcurred(int error_number, QString error_str) {
2408 showLoadingAnimation(false);
2409
2410 qDebug() << "Playlist::errorOcurred:" << error_number << ":" << error_str;
2411 QMessageBox::warning(this, "SMPlayer", error_str);
2412}
2413
2414void Playlist::showLoadingAnimation(bool b) {
2415 if (b) animation->start(); else animation->stop();
2416 loading_label_action->setVisible(b);
2417}
2418
2419void Playlist::setMaxItemsUrlHistory(int max_items) {
2420 history_urls->setMaxItems(max_items);
2421}
2422
2423int Playlist::maxItemsUrlHistory() {
2424 return history_urls->maxItems();
2425}
2426#endif
2427
2428// Language change stuff
2429void Playlist::changeEvent(QEvent *e) {
2430 if (e->type() == QEvent::LanguageChange) {
2431 retranslateStrings();
2432 } else {
2433 QWidget::changeEvent(e);
2434 }
2435}
2436
2437#include "moc_playlist.cpp"
Note: See TracBrowser for help on using the repository browser.