/*  smplayer, GUI front-end for mplayer.
    Copyright (C) 2006-2024 Ricardo Villalba <ricardo@smplayer.info>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "mpvprocess.h"
#include <QRegExp>
#include <QStringList>
#include <QApplication>
#include <QDebug>

#include "global.h"
#include "preferences.h"
#include "mplayerversion.h"
#include "colorutils.h"

#ifdef USE_IPC
#include <QLocalSocket>
#endif

#ifdef USE_IPC
 #if QT_VERSION >= 0x050000
 #include <QStandardPaths>
 #else
 #include <QDesktopServices>
 #endif
#endif

using namespace Global;

#define CUSTOM_STATUS

#define TOO_CHAPTERS_WORKAROUND

MPVProcess::MPVProcess(QObject * parent)
	: PlayerProcess(parent)
	, notified_mplayer_is_running(false)
	, notified_pause(false)
	, received_end_of_file(false)
	, last_sub_id(-1)
	, mplayer_svn(-1) // Not found yet
	, verbose(false)
#if NOTIFY_SUB_CHANGES
	, subtitle_info_received(false)
	, subtitle_info_changed(false)
	, selected_subtitle(-1)
#endif
#if NOTIFY_AUDIO_CHANGES
	, audio_info_changed(false)
	, selected_audio(-1)
#endif
#if NOTIFY_VIDEO_CHANGES
	, video_info_changed(false)
	, selected_video(-1)
#endif
#if NOTIFY_CHAPTER_CHANGES
	, chapter_info_changed(false)
#endif
	, dvd_current_title(-1)
	, br_current_title(-1)
{
	player_id = PlayerID::MPV;

	connect( this, SIGNAL(lineAvailable(QByteArray)),
			 this, SLOT(parseLine(QByteArray)) );

	connect( this, SIGNAL(finished(int,QProcess::ExitStatus)), 
             this, SLOT(processFinished(int,QProcess::ExitStatus)) );

	connect( this, SIGNAL(error(QProcess::ProcessError)),
             this, SLOT(gotError(QProcess::ProcessError)) );

	/* int svn = MplayerVersion::mplayerVersion("mpv unknown version (C)"); */

	initializeOptionVars();
	initializeRX();

#ifdef USE_IPC
	socket = new QLocalSocket(this);
	#if QT_VERSION >= 0x050000
	QString temp_dir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
	#else
	QString temp_dir = QDesktopServices::storageLocation(QDesktopServices::TempLocation);
	#endif
	socket_name = temp_dir + "/smplayer-mpv-" + QString::number(QCoreApplication::applicationPid(), 16);
	qDebug() << "MPVProcess::MPVProcess: socket_name:" << socket_name;
#endif
}

MPVProcess::~MPVProcess() {
#ifdef USE_IPC
	if (QFile::exists(socket_name)) {
		QFile::remove(socket_name);
	}
#endif
}

#ifdef USE_IPC
void MPVProcess::setSocketName(const QString & name) {
	// Remove old socket
	if (!socket_name.isEmpty() && QFile::exists(socket_name)) {
		QFile::remove(socket_name);
	}
	socket_name = name;
	qDebug() << "MPVProcess::setSocketName:" << socket_name;
}
#endif

bool MPVProcess::start() {
	md.reset();
	notified_mplayer_is_running = false;
	notified_pause = false;
	last_sub_id = -1;
	mplayer_svn = -1; // Not found yet
	received_end_of_file = false;

#if NOTIFY_SUB_CHANGES
	subs.clear();
	subtitle_info_received = false;
	subtitle_info_changed = false;
	selected_subtitle = -1;
#endif

#if NOTIFY_AUDIO_CHANGES
	audios.clear();
	audio_info_changed = false;
	selected_audio = -1;
#endif

#if NOTIFY_VIDEO_CHANGES
	videos.clear();
	video_info_changed = false;
	selected_video = -1;
#endif

#if NOTIFY_CHAPTER_CHANGES
	chapters.clear();
	chapter_info_changed = false;
#endif

	dvd_current_title = -1;
	br_current_title = -1;

	MyProcess::start();

	return waitForStarted();
}

void MPVProcess::initializeRX() {
#ifdef CUSTOM_STATUS
	rx_av.setPattern("STATUS: ([0-9\\.-]+) / ([0-9\\.-]+) P: (yes|no) B: (yes|no) I: (yes|no) VB: ([0-9\\.-]+) AB: ([0-9\\.-]+)");
#else
	rx_av.setPattern("(\\((.*)\\) |)(AV|V|A): ([0-9]+):([0-9]+):([0-9]+) / ([0-9]+):([0-9]+):([0-9]+)"); //AV: 00:02:15 / 00:09:56
#endif

	rx_dsize.setPattern("^INFO_VIDEO_DSIZE=(\\d+)x(\\d+)");
	rx_vo.setPattern("^VO: \\[(.*)\\]");
	rx_ao.setPattern("^AO: \\[(.*)\\]");
	rx_paused.setPattern("^\\(Paused\\)");
	rx_endoffile.setPattern("^Exiting... \\(End of file\\)");

	rx_audio.setPattern("^.* Audio\\s+--aid=(\\d+)( --alang=([a-zA-Z-]+)|)([ \\(\\)\\*]+)('(.*)'|)");
	rx_subs.setPattern("^.* Subs\\s+--sid=(\\d+)( --slang=([a-zA-Z-]+)|)([ \\(\\)\\*]+)('(.*)'|)");

	/*
	rx_videocodec.setPattern("^INFO_VIDEO_CODEC=(.*)\\s");
	rx_videocodec.setMinimal(true);

	rx_audiocodec.setPattern("^INFO_AUDIO_CODEC=(.*)\\s");
	rx_audiocodec.setMinimal(true);
	*/

#if !NOTIFY_VIDEO_CHANGES
	rx_video.setPattern("^.* Video\\s+--vid=(\\d+)([ \\(\\)\\*]+)('(.*)'|)");
#endif

	rx_chaptername.setPattern("^INFO_CHAPTER_(\\d+)_NAME=(.*)");
	rx_trackinfo.setPattern("^INFO_TRACK_(\\d+): (audio|video|sub) (\\d+) '(.*)' '(.*)' (yes|no)");

	rx_forbidden.setPattern("HTTP error 403 Forbidden");

#if DVDNAV_SUPPORT
	rx_switch_title.setPattern("^\\[dvdnav\\] DVDNAV, switched to title: (\\d+)");
#endif

	rx_playing.setPattern("^Playing:.*|^\\[ytdl_hook\\].*");
	rx_generic.setPattern("^([A-Z_]+)=(.*)");
	rx_stream_title.setPattern("(?:icy-title: |^ Title: )(.*)");
	rx_debug.setPattern("^(INFO|METADATA)_.*=\\$.*");

	#if 0
	static QRegExp rx_subs2("^Sub:( >|) \\((\\d+)\\) '(.*)'");
	static QRegExp rx_videoinfo("^\\[vd\\] VIDEO: .* (\\d+)x(\\d+) .* ([0-9.]+) fps"); // [vd] VIDEO:  624x352  25.000 fps  1018.5 kbps (127.3 kB/s)
	#endif
}

void MPVProcess::parseLine(QByteArray ba) {
	//qDebug() << "MPVProcess::parseLine:" << ba;

	if (ba.isEmpty()) return;

	QString tag;
	QString value;

#if COLOR_OUTPUT_SUPPORT
    QString line = ColorUtils::stripColorsTags(QString::fromLocal8Bit(ba));
#else
	#ifdef Q_OS_WIN
	QString line = QString::fromUtf8(ba);
	#else
	QString line = QString::fromLocal8Bit(ba);
	#endif
#endif

	if (line.contains("IGNORE_THIS")) return;

	if (verbose) {
		line.replace("[statusline] ", "");
		line.replace("[cplayer] ", "");
		line.replace("[term-msg] ", "");

		if (rx_debug.indexIn(line) > -1) {
			line = "[debug] " + line;
		}
	}

	static double last_sec = -1;

	// Parse A: V: line
	//qDebug() << "MPVProcess::parseLine:" << line;
	if (rx_av.indexIn(line) > -1) {
		#ifdef CUSTOM_STATUS
		double sec = rx_av.cap(1).toDouble();
		double length = rx_av.cap(2).toDouble();
		bool paused = (rx_av.cap(3) == "yes");
		bool buffering = (rx_av.cap(4) == "yes");
		bool idle = (rx_av.cap(5) == "yes");
		int video_bitrate = rx_av.cap(6).toInt();
		int audio_bitrate = rx_av.cap(7).toInt();

		if (length != md.duration) {
			md.duration = length;
			#if DVDNAV_SUPPORT
			emit receivedDuration(length);
			#endif
		}

		if (paused && notified_pause) {
			if (last_sec != sec) {
				last_sec = sec;
				emit receivedCurrentSec(sec);
				emit receivedPause();
			}
			return;
		}

		if (paused) {
			if (last_sec != sec) {
				last_sec = sec;
				emit receivedCurrentSec(sec);
			}
			notified_pause = true;
			qDebug("MPVProcess::parseLine: paused");
			emit receivedPause();
			return;
		}
		else
		if (buffering) {
			qDebug("MPVProcess::parseLine: buffering");
			emit receivedBuffering();
			return;
		}
		else
		if (idle) {
			qDebug("MPVProcess::parseLine: idle");
			emit receivedBuffering();
			return;
		}
		notified_pause = false;

		if (video_bitrate != md.video_bitrate) {
			md.video_bitrate = video_bitrate;
			emit receivedVideoBitrate(video_bitrate);
		}
		if (audio_bitrate != md.audio_bitrate) {
			md.audio_bitrate = audio_bitrate;
			emit receivedAudioBitrate(audio_bitrate);
		}

		#else

		//qDebug() << rx_av.cap(1);
		//qDebug() << rx_av.cap(2);

		QString status = rx_av.cap(2).trimmed();

		int i = 4;
		int h = rx_av.cap(i++).toInt();
		int m = rx_av.cap(i++).toInt();
		int s = rx_av.cap(i++).toInt();
		//qDebug("%d:%d:%d", h, m, s);
		double sec = h * 3600 + m * 60 + s;

		h = rx_av.cap(i++).toInt();
		m = rx_av.cap(i++).toInt();
		s = rx_av.cap(i++).toInt();
		double length = h * 3600 + m * 60 + s;
		if (length != md.duration) {
			md.duration = length;
			#if DVDNAV_SUPPORT
			emit receivedDuration(length);
			#endif
		}

		if (status == "Paused") {
			emit receivedPause();
			return;
		}
		else
		if ((status == "...") || (status == "Buffering")) {
			emit receivedBuffering();
			return;
		}

		if (!status.isEmpty()) {
			qDebug() << "MPVProcess::parseLine: status:" << status;
		}

		#endif

#if NOTIFY_SUB_CHANGES
		if (notified_mplayer_is_running) {
			if (subtitle_info_changed && sec > 0.4) {
				qDebug("MPVProcess::parseLine: subtitle_info_changed");
				subtitle_info_changed = false;
				subtitle_info_received = false;
				emit subtitleInfoChanged(subs, selected_subtitle);
			}
			if (subtitle_info_received) {
				qDebug("MPVProcess::parseLine: subtitle_info_received");
				subtitle_info_received = false;
				emit subtitleInfoReceivedAgain(subs);
			}
		}
#endif

#if NOTIFY_AUDIO_CHANGES
		if (notified_mplayer_is_running) {
			if (audio_info_changed && sec > 0.4) {
				qDebug("MPVProcess::parseLine: audio_info_changed");
				audio_info_changed = false;
				emit audioInfoChanged(audios, selected_audio);
			}
		}
#endif

#if NOTIFY_VIDEO_CHANGES
		if (notified_mplayer_is_running) {
			if (video_info_changed) {
				qDebug("MPVProcess::parseLine: video_info_changed");
				video_info_changed = false;
				emit videoInfoChanged(videos, selected_video);
			}
		}
#endif

#if NOTIFY_CHAPTER_CHANGES
		if (notified_mplayer_is_running) {
			if (chapter_info_changed) {
				qDebug("MPVProcess::parseLine: chapter_info_changed");
				chapter_info_changed = false;
				emit chaptersChanged(chapters);
			}
		}
#endif

		if (!notified_mplayer_is_running) {
			qDebug() << "MPVProcess::parseLine: starting sec:" << sec;

			if (md.video_width == 0 || md.video_height == 0) {
				md.novideo = true;
				emit receivedNoVideo();
			}

			/*
			if ( (md.n_chapters <= 0) && (dvd_current_title > 0) && 
                 (md.titles.find(dvd_current_title) != -1) )
			{
				int idx = md.titles.find(dvd_current_title);
				md.n_chapters = md.titles.itemAt(idx).chapters();
				qDebug("MPVProcess::parseLine: setting chapters to %d", md.n_chapters);
			}
			*/

			/*
			#if CHECK_VIDEO_CODEC_FOR_NO_VIDEO
			// Another way to find out if there's no video
			if (md.video_codec.isEmpty()) {
				md.novideo = true;
				emit receivedNoVideo();
			}
			#endif
			*/

			emit receivedStartingTime(sec);
			emit mplayerFullyLoaded();

			emit receivedCurrentFrame(0); // Ugly hack: set the frame counter to 0

			notified_mplayer_is_running = true;

			// Wait some secs to ask for bitrate
			/*
			QTimer::singleShot(12000, this, SLOT(requestBitrateInfo()));
			*/
		}

		emit receivedCurrentSec( sec );
	}
	else {
		emit lineAvailable(line);

		// Parse other things
		qDebug() << "MPVProcess::parseLine:" << line;

		// End of file
		if (rx_endoffile.indexIn(line) > -1)  {
			qDebug("MVPProcess::parseLine: detected end of file");
			if (!received_end_of_file) {
				// In case of playing VCDs or DVDs, maybe the first title
				// is not playable, so the GUI doesn't get the info about
				// available titles. So if we received the end of file
				// first let's pretend the file has started so the GUI can have
				// the data.
				if ( !notified_mplayer_is_running) {
					emit mplayerFullyLoaded();
				}
				// Send signal once the process is finished, not now!
				received_end_of_file = true;
			}
		}
		else

		// Window resolution
		if (rx_dsize.indexIn(line) > -1) {
			int w = rx_dsize.cap(1).toInt();
			int h = rx_dsize.cap(2).toInt();
			emit receivedWindowResolution( w, h );
		}
		else
	
		// VO
		if (rx_vo.indexIn(line) > -1) {
			emit receivedVO( rx_vo.cap(1) );
			// Ask for window resolution
			sendCommand("print_text INFO_VIDEO_DSIZE=${=dwidth}x${=dheight}");
		}
		else
		
		// AO
		if (rx_ao.indexIn(line) > -1) {
			emit receivedAO( rx_ao.cap(1) );
		}
		else


		// Audio
		if (rx_audio.indexIn(line) > -1) {
			int ID = rx_audio.cap(1).toInt();
			QString lang = rx_audio.cap(3);
			QString title = rx_audio.cap(6);
			qDebug() << "MPVProcess::parseLine: audio id:" << ID << "lang:" << lang << "name:" << title;

			#if NOTIFY_AUDIO_CHANGES
			updateAudioTrack(ID, title, lang, false);
			#else
			if (md.taudios.find(ID) == -1) {
				md.taudios.addID(ID);
				md.taudios.addName(ID, title);
				md.taudios.addLang(ID, lang);
			}
			#endif
		}
		else

		if (rx_stream_title.indexIn(line) > -1) {
			QString s = rx_stream_title.cap(1);
			qDebug() << "MPVProcess::parseLine: stream_title:" << s;
			md.stream_title = s;
			emit receivedStreamTitle(s);
		}
		else

		// Subtitles
		if (rx_subs.indexIn(line) > -1) {
			int ID = rx_subs.cap(1).toInt();
			QString lang = rx_subs.cap(3);
			QString title = rx_subs.cap(6);
			qDebug() << "MPVProcess::parseLine: sub id:" << ID << "lang:" << lang << "name:" << title;

			#if NOTIFY_SUB_CHANGES
			updateSubtitleTrack(ID, title, lang, false);
			#else
			if (md.tsubs.find(SubData::Sub, ID) == -1) {
				md.tsubs.add(SubData::Sub, ID);
				md.tsubs.changeName(SubData::Sub, ID, title);
				md.tsubs.changeLang(SubData::Sub, ID, lang);
			}
			#endif
		}
		else
		#if 0
		if (rx_subs2.indexIn(line) > -1) {
			bool selected = (rx_subs2.cap(1) == " >");
			int ID = rx_subs2.cap(2).toInt();
			QString title = rx_subs2.cap(3);
			qDebug("MPVProcess::parseLine: sub id: %d title: '%s' selected: %d", ID, title.toUtf8().constData(), selected);
			if (md.subs.find(SubData::Sub, ID) == -1) {
				md.subs.add(SubData::Sub, ID);
				md.subs.changeName(SubData::Sub, ID, title);
			}
		}
		else
		#endif

#if !NOTIFY_VIDEO_CHANGES
		// Video
		if (rx_video.indexIn(line) > -1) {
			int ID = rx_video.cap(1).toInt();
			QString title = rx_video.cap(4);
			qDebug("MPVProcess::parseLine: video id: %d, name: '%s'", ID, title.toUtf8().constData());
			//md.videos.addID(ID);
			md.tvideos.addName(ID, title);
		}
		else
#endif

#if 0
		// Video info
		if (rx_videoinfo.indexIn(line) > -1) {
			md.video_width = rx_videoinfo.cap(1).toInt();
			md.video_height = rx_videoinfo.cap(2).toInt();
			md.video_fps = rx_videoinfo.cap(3);
			qDebug("MPVProcess::parseLine: width: %d height: %d fps: %s", md.video_width, md.video_height, md.video_fps.toUtf8().constData());
		}
		else
#endif

		// Chapters
		if (rx_chaptername.indexIn(line) > -1) {
			int ID = rx_chaptername.cap(1).toInt();
			QString title = rx_chaptername.cap(2);
			if (title.isEmpty()) title = QString::number(ID + 1);
			#if NOTIFY_CHAPTER_CHANGES
			chapters.addName(ID, title);
			chapter_info_changed = true;
			#else
			md.chapters.addName(ID, title);
			#endif
			qDebug() << "MPVProcess::parseLine: chapter id:" << ID << "title:" << title;
			//md.chapters.list();
		}
		else

#if NOTIFY_VIDEO_CHANGES || NOTIFY_AUDIO_CHANGES || NOTIFY_SUB_CHANGES
		// Track info
		if (rx_trackinfo.indexIn(line) > -1) {
			int ID = rx_trackinfo.cap(3).toInt();
			QString type = rx_trackinfo.cap(2);
			QString name = rx_trackinfo.cap(5);
			QString lang = rx_trackinfo.cap(4);
			QString selected = rx_trackinfo.cap(6);
			qDebug() << "MPVProcess::parseLine: ID:" << ID << "type:" << type << "name:" << name << "lang:" << lang << "selected:" << selected;
			/*
			if (lang == "(unavailable)") lang = "";
			if (name == "(unavailable)") name = "";
			*/
			if (type == "video") {
				#if NOTIFY_VIDEO_CHANGES
				updateVideoTrack(ID, name, lang, (selected == "yes"));
				#endif
			}
			else
			if (type == "audio") {
				#if NOTIFY_AUDIO_CHANGES
				updateAudioTrack(ID, name, lang, (selected == "yes"));
				#endif
			}
			else
			if (type == "sub") {
				#if NOTIFY_SUB_CHANGES
				updateSubtitleTrack(ID, name, lang, (selected == "yes"));
				#endif
			}
		}
		else
#endif

#if DVDNAV_SUPPORT
		if (rx_switch_title.indexIn(line) > -1) {
			int title = rx_switch_title.cap(1).toInt();
			qDebug("MPVProcess::parseLine: title changed to %d", title);
			// Ask for track info
			// Wait 10 secs. because it can take a while until the title start to play
			QTimer::singleShot(10000, this, SLOT(requestChapterInfo()));
		}
		else
#endif

		//Playing
		if (rx_playing.indexIn(line) > -1) {
			emit receivedPlaying();
		}
		else
		/*
		if (rx_videocodec.indexIn(line) > -1) {
			md.video_codec = rx_videocodec.cap(1);
			qDebug() << "MPVProcess::parseLine: md.video_codec:" << md.video_codec;
		}
		else
		if (rx_audiocodec.indexIn(line) > -1) {
			md.audio_codec = rx_audiocodec.cap(1);
			qDebug() << "MPVProcess::parseLine: md.audio_codec:" << md.audio_codec;
		}
		else
		*/

		if (rx_forbidden.indexIn(line) > -1) {
			qDebug("MVPProcess::parseLine: 403 forbidden");
			emit receivedForbiddenText();
		}

		//Generic things
		if (rx_generic.indexIn(line) > -1) {
			tag = rx_generic.cap(1);
			value = rx_generic.cap(2);
			//qDebug("MPVProcess::parseLine: tag: %s", tag.toUtf8().constData());
			//qDebug("MPVProcess::parseLine: value: %s", value.toUtf8().constData());

			if (tag == "INFO_VIDEO_WIDTH") {
				md.video_width = value.toInt();
				qDebug("MPVProcess::parseLine: md.video_width set to %d", md.video_width);
			}
			else
			if (tag == "INFO_VIDEO_HEIGHT") {
				md.video_height = value.toInt();
				qDebug("MPVProcess::parseLine: md.video_height set to %d", md.video_height);
			}
			else
			if (tag == "INFO_VIDEO_ASPECT") {
				md.video_aspect = value.toDouble();
				if ( md.video_aspect == 0.0 ) {
					// I hope width & height are already set.
					md.video_aspect = (double) md.video_width / md.video_height;
				}
				qDebug() << "MPVProcess::parseLine: md.video_aspect set to" << md.video_aspect;
			}
			if (tag == "INFO_VIDEO_BITRATE") {
				int bitrate = value.toInt();
				emit receivedVideoBitrate(bitrate);
			}
			else
			if (tag == "INFO_LENGTH") {
				md.duration = value.toDouble();
				qDebug() << "MPVProcess::parseLine: md.duration set to" << md.duration;
			}
			else
			if (tag == "INFO_DEMUXER") {
				md.demuxer = value;
			}
			else
			if (tag == "INFO_VIDEO_FORMAT") {
				md.video_format = value;
			}
			if (tag == "INFO_VIDEO_FPS") {
				md.video_fps = value;
			}
			else
			if (tag == "INFO_VIDEO_CODEC") {
				md.video_codec = value;
			}
			else
			if (tag == "INFO_AUDIO_CODEC") {
				md.audio_codec = value;
			}
			else
			if (tag == "INFO_DEMUX_ROTATION") {
				emit receivedDemuxRotation(value.toInt());
			}
			else
			if (tag == "INFO_AUDIO_BITRATE") {
				int bitrate = value.toInt();
				emit receivedAudioBitrate(bitrate);
			}
			else
			if (tag == "INFO_AUDIO_RATE") {
				md.audio_rate = value.toInt();
			}
			else
			if (tag == "INFO_AUDIO_NCH") {
				md.audio_nch = value.toInt();
			}
			else
			if (tag == "INFO_AUDIO_FORMAT") {
				md.audio_format = value;
			}
			else
			if (tag == "INFO_CHAPTERS") {
				md.n_chapters = value.toInt();
				#ifdef TOO_CHAPTERS_WORKAROUND
				if (md.n_chapters > 1000) {
					qDebug("MPVProcess::parseLine: warning too many chapters: %d", md.n_chapters);
					qDebug("                       chapters will be ignored"); 
					md.n_chapters = 0;
				}
				#endif
				for (int n = 0; n < md.n_chapters; n++) {
					sendCommand(QString("print_text INFO_CHAPTER_%1_NAME=${chapter-list/%1/title}").arg(n));
				}
			}
			else
			if (tag == "INFO_TITLES") {
				int n_titles = value.toInt();
				for (int ID = 0; ID < n_titles; ID++) {
					md.titles.addName(ID, QString::number(ID+1));
				}
			}
			else
			if (tag == "METADATA_TITLE") {
				if (!value.isEmpty()) md.clip_name = value;
			}
			else
			if (tag == "METADATA_ARTIST") {
				if (!value.isEmpty()) md.clip_artist = value;
			}
			else
			if (tag == "METADATA_DATE") {
				if (!value.isEmpty()) md.clip_date = value;
			}
			else
			if (tag == "METADATA_ALBUM") {
				if (!value.isEmpty()) md.clip_album = value;
			}
			else
			if (tag == "METADATA_COPYRIGHT") {
				if (!value.isEmpty()) md.clip_copyright = value;
			}
			else
			if (tag == "METADATA_TRACK") {
				if (!value.isEmpty()) md.clip_track = value;
			}
			else
			if (tag == "METADATA_GENRE") {
				if (!value.isEmpty()) md.clip_genre = value;
			}
			else
			if (tag == "INFO_MEDIA_TITLE") {
				if (!value.isEmpty() && value != "mp4" && !value.startsWith("mp4&") /*&& md.clip_name.isEmpty()*/) {
					md.clip_name = value;
				}
			}
			else
			if (tag == "INFO_STREAM_PATH") {
				QRegExp rx("edl://%\\d+%(.*)");
				if (rx.indexIn(line) > -1) {
					md.stream_path = rx.cap(1);
				} else {
					md.stream_path = value;
				}
			}
			else
			if (tag == "MPV_VERSION") {
				mpv_version = value;
				if (mpv_version.startsWith("mpv ")) mpv_version = mpv_version.mid(4);
				qDebug() << "MPVProcess::parseLine: mpv version:" << mpv_version;
				MplayerVersion::mplayerVersion("mpv " + mpv_version + " (C)");
			}
#if NOTIFY_VIDEO_CHANGES || NOTIFY_AUDIO_CHANGES || NOTIFY_SUB_CHANGES
			else
			if (tag == "INFO_TRACKS_COUNT") {
				int tracks = value.toInt();
				for (int n = 0; n < tracks; n++) {
					sendCommand(QString("print_text \"INFO_TRACK_%1: "
						"${track-list/%1/type} "
						"${track-list/%1/id} "
						"'${track-list/%1/lang:}' "
						"'${track-list/%1/title:}' "
						"${track-list/%1/selected}\"").arg(n));
				}
			}
#endif
		}
	}
}

void MPVProcess::requestChapterInfo() {
	sendCommand("print_text \"INFO_CHAPTERS=${=chapters}\"");
}

/*
void MPVProcess::requestBitrateInfo() {
	sendCommand("print_text INFO_VIDEO_BITRATE=${=video-bitrate}");
	sendCommand("print_text INFO_AUDIO_BITRATE=${=audio-bitrate}");
}
*/

#if NOTIFY_VIDEO_CHANGES
void MPVProcess::updateVideoTrack(int ID, const QString & name, const QString & lang, bool selected) {
	qDebug("MPVProcess::updateVideoTrack: ID: %d", ID);

	int idx = videos.find(ID);
	if (idx == -1) {
		video_info_changed = true;
		videos.addName(ID, name);
		videos.addLang(ID, lang);
	} else {
		// Track already existed
		if (videos.itemAt(idx).name() != name) {
			video_info_changed = true;
			videos.addName(ID, name);
		}
		if (videos.itemAt(idx).lang() != lang) {
			video_info_changed = true;
			videos.addLang(ID, lang);
		}
	}

	if (selected && selected_video != ID) {
		selected_video = ID;
		video_info_changed = true;
	}
}
#endif

#if NOTIFY_AUDIO_CHANGES
void MPVProcess::updateAudioTrack(int ID, const QString & name, const QString & lang, bool selected) {
	qDebug("MPVProcess::updateAudioTrack: ID: %d", ID);

	int idx = audios.find(ID);
	if (idx == -1) {
		audio_info_changed = true;
		audios.addName(ID, name);
		audios.addLang(ID, lang);
	} else {
		// Track already existed
		if (audios.itemAt(idx).name() != name) {
			audio_info_changed = true;
			audios.addName(ID, name);
		}
		if (audios.itemAt(idx).lang() != lang) {
			audio_info_changed = true;
			audios.addLang(ID, lang);
		}
	}

	if (selected && selected_audio != ID) {
		selected_audio = ID;
		audio_info_changed = true;
	}
}
#endif

#if NOTIFY_SUB_CHANGES
void MPVProcess::updateSubtitleTrack(int ID, const QString & name, const QString & lang, bool selected) {
	qDebug("MPVProcess::updateSubtitleTrack: ID: %d", ID);

	int idx = subs.find(SubData::Sub, ID);
	if (idx == -1) {
		subtitle_info_changed = true;
		subs.add(SubData::Sub, ID);
		subs.changeName(SubData::Sub, ID, name);
		subs.changeLang(SubData::Sub, ID, lang);
	}
	else {
		// Track already existed
		if (subs.itemAt(idx).name() != name) {
			subtitle_info_changed = true;
			subs.changeName(SubData::Sub, ID, name);
		}
		if (subs.itemAt(idx).lang() != lang) {
			subtitle_info_changed = true;
			subs.changeLang(SubData::Sub, ID, lang);
		}
	}

	if (selected && selected_subtitle != ID) {
		selected_subtitle = ID;
		subtitle_info_changed = true;
	}
}
#endif

// Called when the process is finished
void MPVProcess::processFinished(int exitCode, QProcess::ExitStatus exitStatus) {
	qDebug("MPVProcess::processFinished: exitCode: %d, status: %d", exitCode, (int) exitStatus);
	// Send this signal before the endoffile one, otherwise
	// the playlist will start to play next file before all
	// objects are notified that the process has exited.
	emit processExited();
	if (received_end_of_file) emit receivedEndOfFile();
}

void MPVProcess::gotError(QProcess::ProcessError error) {
	qDebug("MPVProcess::gotError: %d", (int) error);
}

#ifdef USE_IPC
void MPVProcess::sendCommand(QString text) {
	qDebug() << "MPVProcess::sendCommand:" << text;

	if (!isRunning()) {
		qWarning("MPVProcess::sendCommand: mpv is not running. Command ignored.");
		return;
	}

	if (socket->state() != QLocalSocket::ConnectedState) {
		qDebug() << "MPVProcess::sendCommand: state:" << socket->state();
		qDebug() << "MPVProcess::sendCommand: error:" << socket->errorString();
		socket->close();
		socket->connectToServer(socket_name, QIODevice::ReadWrite | QIODevice::Text);
		socket->waitForConnected();
		qDebug() << "MPVProcess::sendCommand: state:" << socket->state();
	}
	socket->write(text.toUtf8() +"\n");
	socket->flush();
}
#endif

#include "mpvoptions.cpp"
#include "moc_mpvprocess.cpp"
