diff --git a/.gitignore b/.gitignore
index 66708ab8..f56bb8a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,6 @@ locale/
po/gmusicbrowser.pot
nytprof*
t/samples/
+
+.vscode/
+TODO
diff --git a/debian/compat b/debian/compat
index 7ed6ff82..f599e28b 100644
--- a/debian/compat
+++ b/debian/compat
@@ -1 +1 @@
-5
+10
diff --git a/debian/control b/debian/control
index b0c7a1ac..d87f6a04 100644
--- a/debian/control
+++ b/debian/control
@@ -7,9 +7,10 @@ Build-Depends: debhelper (>= 5.0.0), markdown
Standards-Version: 3.7.3
Package: gmusicbrowser
+Version: 1.1.99.2
Architecture: all
-Depends: perl, libgtk3-perl, libgtk-3-0
-Recommends: libintl-perl, libnet-dbus-perl, libglib-object-introspection-perl, gir1.2-gstreamer-1.0, libdigest-crc-perl, libhtml-parser-perl, libio-compress-perl
+Depends: perl, libgtk3-perl, libgtk-3-0, libutf8-all-perl
+Recommends: libintl-perl, libnet-dbus-perl, libglib-object-introspection-perl, gir1.2-gstreamer-1.0, libdigest-crc-perl, libhtml-parser-perl, libio-compress-perl, libtext-autoformat-perl, libxml-perl
Suggests: mpv, mplayer, mpg123, vorbis-tools, alsa-utils, gir1.2-webkit2, gir1.2-notify, gir1.2-wnck, gir1.2-poppler
Description: very customizable jukebox for large collections of music files
The interface is extremely customizable. It has easy access to related songs
diff --git a/gmusicbrowser.pl b/gmusicbrowser.pl
index dbaf30d3..aa0b34fd 100755
--- a/gmusicbrowser.pl
+++ b/gmusicbrowser.pl
@@ -17,9 +17,7 @@
use strict;
use warnings;
-use utf8;
-binmode STDERR,':utf8';
-binmode STDOUT,':utf8';
+use utf8::all;
package main;
@@ -528,18 +526,13 @@ BEGIN
}
# end of command line handling
-our $HTTP_module;
our ($Play_package,%PlayPacks); my ($PlayNext_package,$Vol_package);
BEGIN{
require 'gmusicbrowser_songs.pm';
require 'gmusicbrowser_tags.pm';
require 'gmusicbrowser_layout.pm';
require 'gmusicbrowser_list.pm';
-$HTTP_module= -e $DATADIR.SLASH.'simple_http_wget.pm' && (grep -x $_.SLASH.'wget', split /:/, $ENV{PATH}) ? 'simple_http_wget.pm' :
- -e $DATADIR.SLASH.'simple_http_AE.pm' && (grep -f $_.SLASH.'AnyEvent'.SLASH.'HTTP.pm', @INC) ? 'simple_http_AE.pm' :
- 'simple_http.pm';
-#warn "using $HTTP_module for http requests\n";
-#require $HTTP_module;
+require 'simple_http.pm';
# load gstreamer backend module
if (!$CmdLine{nogst})
@@ -10306,22 +10299,16 @@ sub Start
$self->Done;
}
else
- { unless (eval {require $::HTTP_module}) {warn "Loading $::HTTP_module failed, can't download $display_uri\n"; $self->Done; return}
+ {
warn "Downloading '$display_uri' to '$destpath'\n" if $::debug;
::Progress( $progressid, bartext_append=>$display_uri, title=>_"Downloading");
- $self->{waiting}= Simple_http::get_with_cb(url => $uri, cache=>1, progress=>1, cb => sub { $self->Downloaded(@_); });
- $self->{track_progress} ||= Glib::Timeout->add(200,
- sub { if (my $w= $self->{waiting}) { my ($p,$s)=$w->progress; ::Progress( $progressid, partial=>$p ) if defined $p; }
- else { $self->{track_progress}=0 }
- return $self->{track_progress};
- });
+ Simple_http::get_with_cb(url => $uri, cb => sub { $self->Downloaded(@_); });
return
}
}
sub Downloaded
{ my ($self,$content,%content_prop)=@_;
- delete $self->{waiting};
my $type=$content_prop{type};
my $params= $self->{current};
my $uri= $params->{uri};
@@ -10409,7 +10396,6 @@ sub Done # file done, if no $self->{newfile} it means the file has been skipped
}
sub Abort # GMB::DropURI object must not be used after that
{ my $self=shift;
- $self->{waiting}->abort if $self->{waiting};
Glib::Source->remove( $self->{track_progress} ) if $self->{track_progress};
::Progress( 'DropURI_'.$self, abort=>1 );
$self->{cb_end}() if $self->{cb_end};
diff --git a/gmusicbrowser_songs.pm b/gmusicbrowser_songs.pm
index 3d84a476..f1f856d2 100644
--- a/gmusicbrowser_songs.pm
+++ b/gmusicbrowser_songs.pm
@@ -7,7 +7,7 @@
use strict;
use warnings;
-use utf8;
+use utf8::all;
package Songs;
@@ -1151,6 +1151,24 @@ our %timespan_menu=
editsubmenu=>0,
category=>'extra',
},
+# https://github.com/carl-di-ortus/gmusicbrowser/issues/3
+# https://picard-docs.musicbrainz.org/downloads/MusicBrainz_Picard_Tag_Map.html
+# musicbrainz_trackid =>
+# { name => _"MBID",
+# id3v2 => 'TXXX;MusicBrainz Release Track Id;%v', vorbis => 'musicbrainz_releasetrackid', ape => 'MUSICBRAINZ_RELEASETRACKID', ilst => "----:com.apple.iTunes:MusicBrainz Release Track Id", 'id3v2.3' => 'TXXX;MusicBrainz Release Track Id;%v',
+# flags => 'fgaescpi',
+# type => 'string',
+# category=>'extra',
+# postread=> sub { my $v=shift; warn "V $v"; },
+# },
+# acoustid =>
+# { name => _"AcoustID",
+# id3v2 => 'TXXX;Acoustid Id;%v', vorbis => 'ACOUSTID_ID', ape => 'ACOUSTID_ID', ilst => "----:com.apple.iTunes:Acoustid Id",
+# flags => 'fgaescpi',
+# type => 'string',
+# category=>'extra',
+# postread=> sub { my $v=shift },
+# },
style =>
{ name => _"Styles", width => 180, flags => 'fgaescpil',
type => 'flags',
diff --git a/gmusicbrowser_tags.pm b/gmusicbrowser_tags.pm
index 15ef608c..69ea1910 100644
--- a/gmusicbrowser_tags.pm
+++ b/gmusicbrowser_tags.pm
@@ -19,7 +19,7 @@ BEGIN
}
use strict;
use warnings;
-use utf8;
+use utf8::all;
package FileTag;
diff --git a/plugins/albuminfo.pm b/plugins/albuminfo.pm
index ff3cac45..2b05594d 100644
--- a/plugins/albuminfo.pm
+++ b/plugins/albuminfo.pm
@@ -20,8 +20,8 @@ desc Retrieves album-relevant information (review etc.) from allmusic.com.
package GMB::Plugin::ALBUMINFO;
use strict;
use warnings;
-use utf8;
-require $::HTTP_module;
+use utf8::all;
+require 'simple_http.pm';
use base 'Gtk3::Box';
use constant
{ OPT => 'PLUGIN_ALBUMINFO_',
@@ -153,7 +153,7 @@ sub download_one {
} else {
my $url = AMG_SEARCH_URL.::url_escapeall($album);
warn "Albuminfo: fetching search results from $url\n" if $::debug;
- $self->{waiting} = Simple_http::get_with_cb(url=>$url, cache=>1, cb=>sub {$self->load_search_results($ID,1,\&download_one,@_)});
+ Simple_http::get_with_cb(url=>$url, cb=>sub {$self->load_search_results($ID,1,\&download_one,@_)});
}
}
@@ -512,12 +512,11 @@ sub new_search {
$self->cancel();
$self->{treeview}->get_model->clear;
warn "Albuminfo: fetching search results from $url.\n" if $::debug;
- $self->{waiting} = Simple_http::get_with_cb(cb=>sub {$self->print_results(@_)},url=>$url, cache=>1);
+ Simple_http::get_with_cb(cb=>sub {$self->print_results(@_)},url=>$url);
}
sub print_results {
my ($self,$html,%prop) = @_;
- delete $self->{waiting};
my $result = parse_amg_search_results($html, $prop{type}); # result is a ref to an array of hash refs
my $store= $self->{treeview}->get_model;
$store->set_sort_column_id(5, 'ascending');
@@ -534,8 +533,8 @@ sub entry_selected_cb {
my $url = $store->get($store->get_iter($path),4);
warn "Albuminfo: fetching review from $url\n" if $::debug;
$self->cancel();
- $self->{waiting} = Simple_http::get_with_cb(cb=>sub {$self->{searchview}->hide(); $self->{infoview}->show();
- $self->load_review(::GetSelID($self),0,undef,$url,@_)}, url=>$url, cache=>1);
+ Simple_http::get_with_cb(cb=>sub {$self->{searchview}->hide(); $self->{infoview}->show();
+ $self->load_review(::GetSelID($self),0,undef,$url,@_)}, url=>$url);
}
@@ -580,7 +579,7 @@ sub album_changed {
}
}
warn "Albuminfo: fetching search results from $url\n" if $::debug;
- $self->{waiting} = Simple_http::get_with_cb(cb=>sub {$self->load_search_results($ID,0,undef,@_)}, url=>$url, cache=>1);
+ Simple_http::get_with_cb(cb=>sub {$self->load_search_results($ID,0,undef,@_)}, url=>$url);
}
sub update_titlebox {
@@ -600,7 +599,6 @@ sub update_titlebox {
sub load_search_results {
my ($self,$ID,$md,$cb,$html,%prop) = @_; # $md = 1 if mass_download, 0 otherwise. $cb = callback function if mass_download, undef otherwise.
- delete $self->{waiting};
my $result = parse_amg_search_results($html, $prop{type}); # $result[$i] = {url, album, artist, genres, year}
my ($artist,$year) = ::Songs::Get($ID, qw/artist year/);
my $url;
@@ -616,7 +614,7 @@ sub load_search_results {
}
if ($url) {
warn "Albuminfo: fetching review from $url\n" if $::debug;
- $self->{waiting} = Simple_http::get_with_cb(cb=>sub {$self->load_review($ID,$md,$cb,$url,@_)}, url=>$url, cache=>1);
+ Simple_http::get_with_cb(cb=>sub {$self->load_review($ID,$md,$cb,$url,@_)}, url=>$url);
} else {
$self->{fields} = {};
warn "Albuminfo: album not found in search results\n" if $::debug;
@@ -627,7 +625,6 @@ sub load_search_results {
sub load_review {
my ($self,$ID,$md,$cb,$url,$html,%prop) = @_;
- delete $self->{waiting};
$self->{fields} = parse_amg_album_page($url,$html,$prop{type});
$self->print_review() unless $md;
save_review($ID, $self->{fields}) if $::Options{OPT.'AutoSave'} || $md;
@@ -804,7 +801,6 @@ sub cancel {
my $self = shift;
delete $::ToDo{'9_load_albuminfo'.$self};
delete $::ToDo{'9_refresh_albuminfo'.$self};
- $self->{waiting}->abort() if $self->{waiting};
::Progress('albuminfo', abort=>1);
$self->{abort}=1;
}
diff --git a/plugins/artistinfo.pm b/plugins/artistinfo.pm
index 2fbda13b..d86b8c1c 100644
--- a/plugins/artistinfo.pm
+++ b/plugins/artistinfo.pm
@@ -18,8 +18,8 @@ url http://gmusicbrowser.org/dokuwiki/doku.php?id=plugins:artistinfo
package GMB::Plugin::ARTISTINFO;
use strict;
use warnings;
-use utf8;
-require $::HTTP_module;
+use utf8::all;
+require 'simple_http.pm';
use base 'Gtk3::Box';
use constant
{ OPT => 'PLUGIN_ARTISTINFO_', # MUST begin by PLUGIN_ followed by the plugin ID / package name
@@ -29,7 +29,7 @@ use constant
my %sites =
(
biography => ['http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&artist=%a&api_key=7aa688c2466dc17263847da16f297835&autocorrect=1',_"Biography",_"Show artist's biography"],
- events => ['http://ws.audioscrobbler.com/2.0/?method=artist.getevents&artist=%a&api_key=7aa688c2466dc17263847da16f297835&autocorrect=1',_"Events",_"Show artist's upcoming events"],
+ #events => ['http://ws.audioscrobbler.com/2.0/?method=artist.getevents&artist=%a&api_key=7aa688c2466dc17263847da16f297835&autocorrect=1',_"Events",_"Show artist's upcoming events"],
similar => ['http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&artist=%a&api_key=7aa688c2466dc17263847da16f297835&autocorrect=1&limit=%l',_"Similar",_"Show similar artists"]);
my @External=
@@ -49,7 +49,6 @@ my %menuitem=
test => sub {$_[0]{mainfield} eq 'artist'}, #the menu item is displayed if returns true
);
my $nowplayingaID;
-my $queuewaiting;
my %queuemode=
( order=>10, icon=>'gtk-refresh', short=> _"similar-artists", long=> _"Auto-fill queue with similar artists (from last.fm)", changed=>\&QAutofillSimilarArtists, keep=>1,save=>1,autofill=>1,
);
@@ -97,7 +96,6 @@ sub Stop {
Layout::RegisterWidget(PluginArtistinfo => undef);
@::cMenuAA= grep $_!=\%menuitem, @::SongCMenu;
delete $::QActions{'autofill-similar-artists'}; ::Update_QueueActionList();
- $queuewaiting->abort if $queuewaiting; $queuewaiting=undef;
}
sub new
@@ -271,7 +269,6 @@ sub destroy_event_cb
sub cancel
{ my $self=shift;
delete $::ToDo{'8_artistinfo'.$self};
- $self->{waiting}->abort if $self->{waiting};
}
sub prefbox
@@ -506,25 +503,18 @@ sub load_url
warn "info : loading $url\n" if $::debug;
$self->{url}=$url;
$self->{sw2}->hide; $self->{sw1}->show;
- $self->{waiting}=Simple_http::get_with_cb(cb => sub {$self->loaded(@_)},url => $url, cache => 1);
+ Simple_http::get_with_cb(cb => sub {$self->loaded(@_)},url => $url);
}
sub loaded
{ my ($self,$data,%prop)=@_;
- delete $self->{waiting};
my $buffer=$self->{buffer};
my $type=$prop{type};
unless ($data) { $data=_("Loading failed.").qq( )._("retry").''; $type="text/html"; }
$self->{url}=$prop{url} if $prop{url}; #for redirections
$buffer->delete($buffer->get_bounds);
- my $encoding;
- if ($type && $type=~m#^text/.*; ?charset=([\w-]+)#) {$encoding=$1}
- if ($data=~m/xml version/) { $encoding='utf-8'; }
- $encoding=$1 if $data=~m#get_start_iter;
my $fontsize = $self->{fontsize};
@@ -669,7 +659,7 @@ sub Save_text
}
sub QAutofillSimilarArtists
-{ $queuewaiting->abort if $queuewaiting; $queuewaiting=undef;
+{
return unless $::QueueAction eq 'autofill-similar-artists';
return if $::Options{MaxAutoFill}<=@$::Queue;
return unless $::SongID;
@@ -679,11 +669,11 @@ sub QAutofillSimilarArtists
my $url = GetUrl($sites{similar}[0],$nowplayingaID);
return unless $url;
- $queuewaiting=Simple_http::get_with_cb(url => $url, cb => \&PopulateQueue );
+ Simple_http::get_with_cb(url => $url, cb => \&PopulateQueue );
}
sub PopulateQueue
-{ $queuewaiting=undef;
+{
if ( $nowplayingaID != Songs::Get_gid($::SongID,'artist')) { QAutofillSimilarArtists; return; }
my $data = $_[0];
diff --git a/plugins/audioscrobbler.pm b/plugins/audioscrobbler.pm
index 3e634dec..e6564241 100644
--- a/plugins/audioscrobbler.pm
+++ b/plugins/audioscrobbler.pm
@@ -21,13 +21,13 @@ use constant
SAVEFILE => 'audioscrobbler.queue', #file used to save unsent data
};
use Digest::MD5 'md5_hex';
-require $::HTTP_module;
+require 'simple_http.pm';
our $ignore_current_song;
my $self=bless {},__PACKAGE__;
my @ToSubmit; my $NowPlaying; my $NowPlayingID; my $unsent_saved=0;
-my $interval=5; my ($timeout,$waiting);
+my $interval=5; my $timeout;
my ($HandshakeOK,$submiturl,$nowplayingurl,$sessionid);
my ($Serrors,$Stop);
my $Log= Gtk3::ListStore->new('Glib::String');
@@ -43,9 +43,9 @@ sub Start
$Serrors=$Stop=undef;
}
sub Stop
-{ $waiting->abort if $waiting;
+{
Glib::Source->remove($timeout) if $timeout;
- $timeout=$waiting=undef;
+ $timeout=undef;
::UnWatch($self,$_) for qw/PlayingSong Played Save/;
$self->{on}=undef;
$interval=5;
@@ -86,10 +86,10 @@ sub prefbox
sub update_queue_label
{ my $qbox=shift;
my $label= $qbox->{label};
- if (@ToSubmit && (@ToSubmit>1 || (!$waiting && (!$timeout || $interval>10))))
+ if (@ToSubmit && (@ToSubmit>1 || (!$timeout || $interval>10)))
{ $label->set_text(::__n("%d song waiting to be sent","%d songs waiting to be sent", scalar @ToSubmit ));
$label->get_parent->show;
- $qbox->{button}->set_sensitive(!$waiting);
+ $qbox->{button}->set_sensitive();
}
else { $label->get_parent->hide }
}
@@ -236,14 +236,14 @@ sub Sleep
{ #warn "Sleep\n";
return unless $self->{on};
::QHasChanged('Lastfm_state_change');
- return if $Stop || $waiting || $timeout;
+ return if $Stop || $timeout;
$timeout=Glib::Timeout->add(1000*$interval,\&Awake) if @ToSubmit || $NowPlaying;
#warn "Sleeping $interval seconds\n" if $timeout;
}
sub Awake
{ #warn "Awoke\n";
$timeout=undef;
- return 0 if !$self->{on} || $waiting;
+ return 0 if !$self->{on};
if ($HandshakeOK) { Submit(); }
else { Handshake(); }
Sleep();
@@ -253,11 +253,10 @@ sub Send
{ my ($response_cb,$url,$post)=@_;
my $cb=sub
{ my @response=(defined $_[0])? split "\012",$_[0] : ();
- $waiting=undef;
&$response_cb(@response);
Sleep();
};
- $waiting=Simple_http::get_with_cb(cb => $cb,url => $url,post => $post);
+ Simple_http::post_with_cb(cb => $cb,url => $url,post => $post);
::QHasChanged('Lastfm_state_change');
}
diff --git a/plugins/fetch_cover.pm b/plugins/fetch_cover.pm
index 78c243bf..5cfd98fb 100644
--- a/plugins/fetch_cover.pm
+++ b/plugins/fetch_cover.pm
@@ -14,7 +14,7 @@ desc Adds a menu entry to artist/album context menu, allowing to search the pict
package GMB::Plugin::FETCHCOVER;
use strict;
use warnings;
-require $::HTTP_module;
+require 'simple_http.pm';
use base 'Gtk3::Window';
use constant
{ OPT => 'PLUGIN_FETCHCOVER_',
@@ -205,11 +205,7 @@ sub NewSearch
$self->{url}= $url;
$self->{searchcontext}={}; #hash that the parser can use to store data between searches
warn "fetchcover : loading $url\n" if $::debug;
- $self->{waiting}=Simple_http::get_with_cb
- ( cb => sub {$self->searchresults_cb(@_)},
- url => $url, cache=>1,
- user_agent => $self->{user_agent},
- );
+ Simple_http::get_with_cb(cb => sub {$self->searchresults_cb(@_)}, url => $url);
}
sub InitPage
@@ -410,7 +406,6 @@ sub parse_discogs
sub searchresults_cb
{ my ($self,$result)=@_;
- $self->{waiting}=undef;
warn "Getting results from $self->{url}\n" if $::Verbose;
unless (defined $result) { stop($self,_"connection failed."); return; }
my $parse= $Sites{$self->{mainfield}}{$self->{site}}[2];
@@ -429,10 +424,7 @@ sub abort
my $results=$self->{results};
for my $r ($self,@$results)
{ delete $r->{done};
- $r->{waiting}->abort if $r->{waiting};
- delete $r->{waiting};
}
- delete $self->{waiting};
delete $::ToDo{'8_FetchCovers'.$self};
}
@@ -453,41 +445,33 @@ sub get_next
{ my $self=shift;
my $results=$self->{results};
my $res_id;
- my $waiting;
my $start= $self->{page} * RES_PER_PAGE;
my $end= $start + RES_PER_PAGE -1;
if ($#$results<$end && $self->{nexturl})
{ #load next page
my $url= $self->{url}= delete $self->{nexturl};
- $self->{waiting}=Simple_http::get_with_cb
- ( cb => sub {$self->searchresults_cb(@_)},
- url => $url, cache=>1,
- user_agent => $self->{user_agent},
- );
+ Simple_http::get_with_cb(cb => sub {$self->searchresults_cb(@_)}, url => $url);
}
elsif ($#$results>=$end)
{ $self->{Bnext}->set_sensitive(1);
}
$end=$#$results if $#$results<$end;
for my $id ($start .. $end)
- { #warn "$id : waiting=".$results->[$id]{waiting}." done=".$results->[$id]{done};
- if ($results->[$id]{waiting}) {$waiting++; next};
+ {
next if $results->[$id]{done};
$res_id=$id;
last;
}
- unless (defined $res_id || $waiting || $self->{waiting})
+ unless (defined $res_id)
{ $self->stop;
return;
}
return unless defined $res_id;
- return if $waiting && $waiting > 3; #no more than 4 pictures at once
my $result=$self->{results}[$res_id];
- $result->{waiting}=Simple_http::get_with_cb(url => $result->{url}, referer=>$result->{referer}, cache=>1, cb =>
+ Simple_http::get_with_cb(url => $result->{url}, referer=>$result->{referer}, cb =>
sub
{ my $pixdata=$_[0];
- $result->{waiting}=undef;
my $loader;
$loader= GMB::Picture::LoadPixData($pixdata,PREVIEW_SIZE) if $pixdata;
if ($loader)
diff --git a/plugins/listenbrainz.pm b/plugins/listenbrainz.pm
new file mode 100644
index 00000000..f3e04107
--- /dev/null
+++ b/plugins/listenbrainz.pm
@@ -0,0 +1,229 @@
+# Copyright (C) 2024 Carl di Ortus
+#
+# This file is part of Gmusicbrowser.
+# Gmusicbrowser is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation
+
+=for gmbplugin LISTENBRAINZ
+name listenbrainz
+title listenbrainz.org plugin
+desc Submit played songs to listenbrainz
+=cut
+
+
+package GMB::Plugin::LISTENBRAINZ;
+use strict;
+use warnings;
+use JSON;
+use List::Util qw(max);
+use constant
+{ CLIENTID => 'gmb', VERSION => '0.1',
+ OPT => 'PLUGIN_LISTENBRAINZ_', #used to identify the plugin's options
+ SAVEFILE => 'listenbrainz.queue', #file used to save unsent data
+};
+require 'simple_http.pm';
+
+our $ignore_current_song;
+
+my $self=bless {},__PACKAGE__;
+my @ToSubmit; my @NowPlaying; my $NowPlayingID;
+my $interval=10; my ($timeout);
+my ($Stop);
+my $Log= Gtk3::ListStore->new('Glib::String');
+
+sub Start
+{ ::Watch($self,PlayingSong=> \&SongChanged);
+ ::Watch($self,Played => \&Played);
+ $self->{on}=1;
+ Sleep();
+ SongChanged() if $::TogPlay;
+ $Stop=undef;
+}
+sub Stop
+{
+ @NowPlaying=undef;
+ Glib::Source->remove($timeout) if $timeout;
+ $timeout=undef;
+ ::UnWatch($self,$_) for qw/PlayingSong Played/;
+ $self->{on}=undef;
+ $interval=10;
+}
+
+sub prefbox
+{ my $vbox= Gtk3::VBox->new(::FALSE, 2);
+ my $sg1= Gtk3::SizeGroup->new('horizontal');
+ my $sg2= Gtk3::SizeGroup->new('horizontal');
+ my $entry2=::NewPrefEntry(OPT.'TOKEN',_"Token :", cb => \&Stop, sizeg1 => $sg1,sizeg2=>$sg2, hide => 1);
+ my $label2= Gtk3::Button->new(_"(see https://listenbrainz.org)");
+ $label2->set_relief('none');
+ $label2->signal_connect(clicked => sub
+ { my $url='https://listenbrainz.org';
+ my $user=$::Options{OPT.'USER'};
+ $url.="/user/$user/" if defined $user && $user ne '';
+ ::openurl($url);
+ });
+ my $ignore= Gtk3::CheckButton->new(_"Don't submit current song");
+ $ignore->signal_connect(toggled=>sub { return if $_[0]->{busy}; $ignore_current_song= $_[0]->get_active ? $::SongID : undef; ::HasChanged('Listenbrainz_ignore_current'); });
+ ::Watch($ignore,Listenbrainz_ignore_current => sub { $_[0]->{busy}=1; $_[0]->set_active(defined $ignore_current_song); delete $_[0]->{busy}; } );
+ my $queue= Gtk3::Label->new;
+ my $sendnow= Gtk3::Button->new(_"Send now");
+ $sendnow->signal_connect(clicked=> \&SendNow);
+ my $qbox= ::Hpack($queue,$sendnow);
+ $vbox->pack_start($_,::FALSE,::FALSE,0) for $label2,$entry2,$ignore,$qbox;
+ $vbox->add( ::LogView($Log) );
+ $qbox->{label}=$queue;
+ $qbox->{button}=$sendnow;
+ $qbox->show_all;
+ return $vbox;
+}
+
+sub SongChanged
+{
+ @NowPlaying=undef;
+ if (defined $ignore_current_song)
+ { return if defined $::SongID && $::SongID == $ignore_current_song;
+ $ignore_current_song=undef; ::HasChanged('Listenbrainz_ignore_current');
+ }
+ my ($title,$artist,$album)= Songs::Get($::SongID,qw/title artist album/);
+ return if $title eq '' || $artist eq '';
+ @NowPlaying= ( $artist, $title, $album );
+ $NowPlayingID=$::SongID;
+ $interval=10;
+ SendNow();
+}
+
+sub Played
+{ my (undef,$ID,undef,$start_time,$seconds,$coverage)=@_;
+ return if $ignore_current_song;
+ return unless $seconds>10;
+ my $length= Songs::Get($ID,'length');
+ if ($length>=30 && ($seconds >= 240 || $coverage >= .5) )
+ { my ($title,$artist,$album)= Songs::Get($ID,qw/title artist album/);
+ return if $title eq '' || $artist eq '';
+ @ToSubmit= ( $artist, $title, $album );
+ $interval=10;
+ Sleep();
+ ::QHasChanged('Listenbrainz_state_change');
+ }
+}
+
+sub Submit
+{
+ my $i=0;
+ my $url= 'https://api.listenbrainz.org/1/submit-listens';
+ my $listen_type;
+ my $listened_at;
+ my @payload;
+ if (@ToSubmit)
+ { @payload= @ToSubmit;
+ $listen_type= "single";
+ $listened_at= time();
+ }
+ elsif (@NowPlaying)
+ { if (!defined $::PlayingID || $::PlayingID!=$NowPlayingID) { @NowPlaying=undef; return }
+ @payload= @NowPlaying;
+ $listen_type= "playing_now";
+ $listened_at= undef;
+ }
+ else { return; }
+ my $post= {
+ listen_type => $listen_type,
+ payload => [
+ {
+ track_metadata => {
+ artist_name => $payload[0],
+ track_name => $payload[1]
+ }
+ }
+ ]
+ };
+ $post->{payload}[0]->{listened_at} = $listened_at if $listened_at;
+ $post->{payload}[0]->{track_metadata}->{release_name} = $payload[2] if $payload[2];
+ my $response_cb=sub
+ {
+ my ($response,$error)=@_;
+
+ if ($error)
+ { Log(_("Submit failed : ").$error);
+ Log(_("Response : ").$response) if $response;
+ $interval*=2;
+ $interval=max($interval, 300);
+ return;
+ }
+
+ unlink $::HomeDir.SAVEFILE;
+ if (@ToSubmit) {
+ Log( _("Submit OK ") .
+ ::__x( _"{song} by {artist}", song=> $payload[1], artist => $payload[0]) );
+ undef @ToSubmit;
+ $interval=10;
+ return
+ };
+ if (@NowPlaying) {
+ Log( _("NowPlaying OK ") .
+ ::__x( _"{song} by {artist}", song=> $payload[1], artist => $payload[0]) );
+ $interval=60;
+ return
+ };
+ };
+
+ my $authtoken=$::Options{OPT.'TOKEN'};
+ Save($post);
+ Send($response_cb,$url,$::HomeDir.SAVEFILE,$authtoken);
+}
+
+sub SendNow
+{ $interval=10;
+ $Stop=undef;
+ Awake();
+}
+
+sub Sleep
+{ return unless $self->{on};
+ ::QHasChanged('Listenbrainz_state_change');
+ return if $Stop || $timeout;
+ $timeout=Glib::Timeout->add(1000*$interval,\&Awake) if @ToSubmit || @NowPlaying;
+}
+
+sub Awake
+{ Glib::Source->remove($timeout) if $timeout;
+ $timeout=undef;
+ return 0 if !$self->{on};
+ Submit();
+ Sleep();
+ return 0;
+}
+
+sub Send
+{ my ($response_cb,$url,$post,$authtoken)=@_;
+ my $cb=sub
+ { my @response=(defined $_[0])? split "\012",$_[0] : ();
+ &$response_cb(@response);
+ Sleep();
+ };
+
+ Simple_http::post_with_cb(cb => $cb,url => $url,post => $post,authtoken => $authtoken);
+ ::QHasChanged('Listenbrainz_state_change');
+}
+
+sub Log
+{ my $text=$_[0];
+ $Log->set( $Log->prepend,0, localtime().' '.$text );
+ warn "$text\n" if $::debug;
+ if (my $iter=$Log->iter_nth_child(undef,50)) { $Log->remove($iter); }
+}
+
+sub Save
+{ my $savebody=$_[0];
+ unless ($savebody)
+ { unlink $::HomeDir.SAVEFILE; return }
+ my $fh;
+ unless (open $fh,'>:utf8',$::HomeDir.SAVEFILE)
+ { warn "Error creating '$::HomeDir".SAVEFILE."' : $!\nUnsent listenbrainz.org data will be lost.\n"; return; }
+ my $json=(to_json $savebody);
+ print $fh $json;
+ close $fh;
+}
+
+1;
diff --git a/plugins/lyrics.pm b/plugins/lyrics.pm
index 4cf862c1..b90ad883 100644
--- a/plugins/lyrics.pm
+++ b/plugins/lyrics.pm
@@ -14,16 +14,19 @@ desc Search and display lyrics
package GMB::Plugin::LYRICS;
use strict;
use warnings;
-use utf8;
-require $::HTTP_module;
+use utf8::all;
+require 'simple_http.pm';
our @ISA;
BEGIN {push @ISA,'GMB::Context';}
use base 'Gtk3::Box';
+use Text::Autoformat;
+use HTML::TreeBuilder;
use constant
{ OPT => 'PLUGIN_LYRICS_', # MUST begin by PLUGIN_ followed by the plugin ID / package name
};
my $notfound=_"No lyrics found";
+my $instrumental=_"Instrumental";
my @justification=
( left => _"Left aligned",
@@ -48,54 +51,51 @@ my @ContextMenuAppend=
},
);
-my %Sites= # id => [name,url,?post?,function] if the function return 1 => lyrics can be saved
-( #lyrc => ['lyrc','http://lyrc.com.ar/en/tema1en.php','artist=%a&songname=%t'],
- #lyrc => ['lyrc','http://lyrc.com.ar/en/tema1en.php?artist=%a&songname=%t',undef,sub
- # { local $_=$_[0];
- # return -1 if m#]+add[^>]+>(?:[^<]*?[b-z]\w*)*[^<]*Add a lyric.(?:[^<]*?[b-z]\w*)*[^<]*#i;
- # return 1 if s#]+badsong[^>]+>BADSONG##i;
- # return 0;
- # }],
- #leoslyrics => ['leolyrics','http://api.leoslyrics.com/api_search.php?artist=%a&songtitle=%t'],
- #google => ['google','http://www.google.com/search?q="%a"+"%t"'],
- lyriki => ['lyriki','http://lyriki.com/index.php?title=%a:%t',undef,
- sub { my $no= $_[0]=~m/
/s; $_[0]=~s/^.*(.*?).*$/$1/s && !$no; }],
- #lyricsplugin => [lyricsplugin => 'http://www.lyricsplugin.com/winamp03/plugin/?title=%t&artist=%a',undef,
- # sub { my $ok=$_[0]=~m#
.*\w\n.*\w.*
#s; $_[0]=~s/
['lyrics-songs',sub { ::ReplaceFields($_[0], "http://letras.terra.com.br/winamp.php?musica=%t&artista=%a", sub {::url_escapeall(::superlc($_[0]));}) },undef,
-# sub { my $is_suggestion= $_[0]=~m#
Provável música
#i;
-# my $l=html_extract($_[0],div=>'letra');
-# $l=~s#