Merge lp:~stolowski/unity-lens-video/vala-rewrite into lp:unity-lens-video

Proposed by Paweł Stołowski
Status: Merged
Approved by: Paweł Stołowski
Approved revision: 158
Merged at revision: 106
Proposed branch: lp:~stolowski/unity-lens-video/vala-rewrite
Merge into: lp:unity-lens-video
Diff against target: 4321 lines (+3341/-668)
50 files modified
Makefile.am (+5/-0)
Makefile.am.coverage (+48/-0)
Makefile.decl (+75/-0)
acinclude.m4 (+40/-0)
autogen.sh (+13/-0)
configure.ac (+208/-0)
data/Makefile.am (+27/-0)
data/unity-lens-video.service.in (+1/-1)
data/unity-scope-video-remote.service.in (+3/-0)
data/video-remote.scope (+3/-0)
debian/changelog (+11/-0)
debian/control (+26/-13)
debian/copyright (+6/-8)
debian/rules (+7/-2)
debian/unity-lens-video.install (+3/-0)
debian/unity-scope-video-remote.install (+3/-0)
debian/watch (+1/-1)
m4/gcov.m4 (+86/-0)
po/POTFILES.in (+5/-2)
po/POTFILES.skip (+7/-0)
po/unity-lens-video.pot (+0/-70)
setup.cfg (+0/-10)
setup.py (+0/-18)
src/Makefile.am (+133/-0)
src/blacklist-tracker.vala (+128/-0)
src/config.vala.in (+16/-0)
src/daemon.vala (+79/-0)
src/locate.vala (+112/-0)
src/main.vala (+59/-0)
src/remote-scope-globals.vala (+25/-0)
src/remote-scope.vala (+491/-0)
src/remote-uri.vala (+52/-0)
src/remote-video-main.vala (+60/-0)
src/scope.vala (+427/-0)
src/thumbnailer.vala (+115/-0)
src/ubuntu-video-search.vala (+229/-0)
src/unity-lens-video (+0/-543)
src/utils.vala (+77/-0)
src/video-file.vala (+59/-0)
tests/unit/Makefile.am (+120/-0)
tests/unit/config-tests.vala.in (+6/-0)
tests/unit/data/videosearch_details1.txt (+26/-0)
tests/unit/data/videosearch_input1.txt (+44/-0)
tests/unit/data/videosearch_input2.txt (+17/-0)
tests/unit/test-locate.vala (+57/-0)
tests/unit/test-ubuntu-video-search.vala (+329/-0)
tests/unit/test-utils.vala (+60/-0)
tests/unit/thumbnailer-mock.vala (+29/-0)
vapi/Makefile.am (+5/-0)
vapi/libsoup-gnome-2.4.vapi (+8/-0)
To merge this branch: bzr merge lp:~stolowski/unity-lens-video/vala-rewrite
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Michal Hruby (community) Approve
Review via email: mp+135935@code.launchpad.net

Commit message

This is video lens & remote video scope, rewritten in vala. Majority of the logic in the code is based on the original logic of python code and it shares the same shortcomings (e.g. it's based on "locate"), but this is a starting point for further improvements. This branch also introduces unit tests.

Description of the change

This is video lens & remote video scope, rewritten in vala. Majority of the logic in the code is based on the original logic of python code and it shares the same shortcomings (e.g. it's based on "locate"), but this is a starting point for further improvements. This branch also introduces unity tests.

To post a comment you must log in.
Revision history for this message
Michal Hruby (mhr3) wrote :

How was the rule? Diffs above 1k lines must be needs fixing? :)

889 + public string locate_bin { get; set; default = "/usr/bin/locate"; }

Why the absolute paths?

906 + GLib.Process.spawn_command_line_sync (@"$locate_bin -id $cache_db $query", out stdout);

A limit should be added here, no need for thousands of results. (-l)

939 + int video_count = 0;

Unused.

1128 + recommendations = new Gee.ArrayList<RemoteVideoFile?> (null);

Please get rid of the null, it's default param, and kinda confusing when specified.

1133 + zeitgeist_init ();

Not really sure the lens should be inserting events to zeitgeist, it has browser extensions that would log these kind of events (although they're not installed by default).

1144 + session.user_agent = "Unity Video Lens Remote Scope v0.4";

Let's use the version from configure.ac

1148 + search_changed.connect ((search, search_type, cancellable) => {
1149 + update_search_async.begin (search, search_type, cancellable);
1150 + });

Parse error, the brackets don't match indentation :P (present in multiple lambda instances)

1158 + preferences.notify["remote-content-search"].connect ((obj, pspec) => {
1159 + queue_search_changed (SearchType.GLOBAL);
1160 + });

This scope doesn't participate in global searches, should be DEFAULT.

1205 + recommendations = results;

if (results != null) ...

1678 + if (Utils.dbus_name_has_owner (BUS_NAME))
1679 + {

There should probably be a wrapper for this in Extras.

2194 + If nothing has been found, it tries to generate a thumbnail with Totem,
2195 + stores and uses it.

Really not liking that, unity should do this for us, we're slowing down the result model populating because of thumbnails.

3227 +check_PROGRAMS = \

For future - let's try to minimize the number of test vala binaries, it's overcomplicating the build process, plus GTest supports -p to pick prefix of tests that you want to run.

3332 +# Create dummy video files, needed by tests

Can you just make these part of the bzr tree (inside tests/data)

3340 +# Copy over test input files, needed by tests when built & run our-of-src directory

Why do these have to be copied over? Just load them from the srcdir? The fewer non-standard make rules, the better. (plus no need for these to be .in if they don't depend on any configure variable)

review: Needs Fixing
Revision history for this message
Paweł Stołowski (stolowski) wrote :

Implemented all (hopefully) suggested corrections. As discussed on IRC, zeitgeist and thumbnail handling should stay as is for now.

Revision history for this message
Michal Hruby (mhr3) wrote :

Approving from my side, the behavior didn't change, but mem usage went down from 19mb to 6mb. Please approve globally once the packaging is updated.

review: Approve
Revision history for this message
Michal Hruby (mhr3) wrote :

Setting to Needs fixing again, as we need to ensure that the recent SRU changes for remote-video-scope that went to trunk are backported here as well.

review: Needs Fixing
Revision history for this message
Michal Hruby (mhr3) wrote :

About time! :)

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Terry (mterry) wrote :

You should be able to fix the build by merging from trunk (at least, debian/changelog, which conflicts)

157. By Paweł Stołowski

Merged trunk.

158. By Paweł Stołowski

Fixed changelog entries for merging with trunk.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'AUTHORS'
=== added file 'ChangeLog'
=== added file 'Makefile.am'
--- Makefile.am 1970-01-01 00:00:00 +0000
+++ Makefile.am 2013-02-19 17:19:01 +0000
@@ -0,0 +1,5 @@
1SUBDIRS = src po tests/unit vapi data
2
3DISTCHECK_CONFIGURE_FLAGS = --enable-localinstall
4
5include $(top_srcdir)/Makefile.am.coverage
06
=== added file 'Makefile.am.coverage'
--- Makefile.am.coverage 1970-01-01 00:00:00 +0000
+++ Makefile.am.coverage 2013-02-19 17:19:01 +0000
@@ -0,0 +1,48 @@
1
2# Coverage targets
3
4.PHONY: clean-gcno clean-gcda \
5 coverage-html generate-coverage-html clean-coverage-html \
6 coverage-gcovr generate-coverage-gcovr clean-coverage-gcovr
7
8clean-local: clean-gcno clean-coverage-html clean-coverage-gcovr
9
10if HAVE_GCOV
11
12clean-gcno:
13 @echo Removing old coverage instrumentation
14 -find -name '*.gcno' -print | xargs -r rm
15
16clean-gcda:
17 @echo Removing old coverage results
18 -find -name '*.gcda' -print | xargs -r rm
19
20coverage-html: clean-gcda
21 -$(MAKE) $(AM_MAKEFLAGS) -k check
22 $(MAKE) $(AM_MAKEFLAGS) generate-coverage-html
23
24generate-coverage-html:
25 @echo Collecting coverage data
26 $(LCOV) --directory $(top_builddir) --capture --output-file coverage.info --no-checksum --compat-libtool
27 LANG=C $(GENHTML) --prefix $(top_builddir) --output-directory coveragereport --title "Code Coverage" --legend --show-details coverage.info
28
29clean-coverage-html: clean-gcda
30 -$(LCOV) --directory $(top_builddir) -z
31 -rm -rf coverage.info coveragereport
32
33if HAVE_GCOVR
34
35coverage-gcovr: clean-gcda
36 -$(MAKE) $(AM_MAKEFLAGS) -k check
37 $(MAKE) $(AM_MAKEFLAGS) generate-coverage-gcovr
38
39generate-coverage-gcovr:
40 @echo Generating coverage GCOVR report
41 $(GCOVR) -x -r $(top_builddir) -o $(top_builddir)/coverage.xml
42
43clean-coverage-gcovr: clean-gcda
44 -rm -rf $(top_builddir)/coverage.xml
45
46endif # HAVE_GCOVR
47
48endif # HAVE_GCOV
049
=== added file 'Makefile.decl'
--- Makefile.decl 1970-01-01 00:00:00 +0000
+++ Makefile.decl 2013-02-19 17:19:01 +0000
@@ -0,0 +1,75 @@
1# GLIB - Library of useful C routines
2#
3# This file is copied almost verbatim from the GLib-2.0 distribution
4#
5
6GTESTER = gtester
7GTESTER_REPORT = gtester-report
8
9# initialize variables for unconditional += appending
10EXTRA_DIST =
11TEST_PROGS =
12
13### testing rules
14
15# test: run all tests in cwd and subdirs
16test: test-nonrecursive
17 @ for subdir in $(SUBDIRS) . ; do \
18 test "$$subdir" = "." -o "$$subdir" = "po" || \
19 ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \
20 done
21
22# test-nonrecursive: run tests only in cwd
23test-nonrecursive: ${TEST_PROGS}
24 @test -z "${TEST_PROGS}" || G_DEBUG=gc-friendly MALLOC_CHECK_=2 MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256)) ${GTESTER} --verbose ${TEST_PROGS}
25
26# test-report: run tests in subdirs and generate report
27# perf-report: run tests in subdirs with -m perf and generate report
28# full-report: like test-report: with -m perf and -m slow
29test-report perf-report full-report: ${TEST_PROGS}
30 @test -z "${TEST_PROGS}" || { \
31 case $@ in \
32 test-report) test_options="-k";; \
33 perf-report) test_options="-k -m=perf";; \
34 full-report) test_options="-k -m=perf -m=slow";; \
35 esac ; \
36 if test -z "$$GTESTER_LOGDIR" ; then \
37 ${GTESTER} --verbose $$test_options -o test-report.xml ${TEST_PROGS} ; \
38 elif test -n "${TEST_PROGS}" ; then \
39 ${GTESTER} --verbose $$test_options -o `mktemp "$$GTESTER_LOGDIR/log-XXXXXX"` ${TEST_PROGS} ; \
40 fi ; \
41 }
42 @ ignore_logdir=true ; \
43 if test -z "$$GTESTER_LOGDIR" ; then \
44 GTESTER_LOGDIR=`mktemp -d "\`pwd\`/.testlogs-XXXXXX"`; export GTESTER_LOGDIR ; \
45 ignore_logdir=false ; \
46 fi ; \
47 REVISION=$(VERSION) ; \
48 for subdir in $(SUBDIRS) . ; do \
49 test "$$subdir" = "." -o "$$subdir" = "po" || \
50 ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \
51 done ; \
52 $$ignore_logdir || { \
53 echo '<?xml version="1.0"?>' > $@.xml ; \
54 echo '<report-collection>' >> $@.xml ; \
55 echo '<info>' >> $@.xml ; \
56 echo ' <package>$(PACKAGE)</package>' >> $@.xml ; \
57 echo ' <version>$(VERSION)</version>' >> $@.xml ; \
58 echo " <revision>$$REVISION</revision>" >> $@.xml ; \
59 echo '</info>' >> $@.xml ; \
60 for lf in `ls -L "$$GTESTER_LOGDIR"/.` ; do \
61 sed '1,1s/^<?xml\b[^>?]*?>//' <"$$GTESTER_LOGDIR"/"$$lf" >> $@.xml ; \
62 done ; \
63 echo >> $@.xml ; \
64 echo '</report-collection>' >> $@.xml ; \
65 rm -rf "$$GTESTER_LOGDIR"/ ; \
66 ${GTESTER_REPORT} --version 2>/dev/null 1>&2 ; test "$$?" != 0 || ${GTESTER_REPORT} $@.xml >$@.html ; \
67 }
68.PHONY: test test-report perf-report full-report test-nonrecursive
69
70# run tests in cwd as part of make check
71if ENABLE_HEADLESS_TESTS
72check-local: test-headless
73else
74check-local: test-nonrecursive
75endif
076
=== added file 'NEWS'
=== added file 'acinclude.m4'
--- acinclude.m4 1970-01-01 00:00:00 +0000
+++ acinclude.m4 2013-02-19 17:19:01 +0000
@@ -0,0 +1,40 @@
1dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR)
2dnl
3dnl example
4dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir)
5dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local
6
7AC_DEFUN([AS_AC_EXPAND],
8[
9 EXP_VAR=[$1]
10 FROM_VAR=[$2]
11
12 dnl first expand prefix and exec_prefix if necessary
13 prefix_save=$prefix
14 exec_prefix_save=$exec_prefix
15
16 dnl if no prefix given, then use /usr/local, the default prefix
17 if test "x$prefix" = "xNONE"; then
18 prefix=$ac_default_prefix
19 fi
20 dnl if no exec_prefix given, then use prefix
21 if test "x$exec_prefix" = "xNONE"; then
22 exec_prefix=$prefix
23 fi
24
25 full_var="$FROM_VAR"
26 dnl loop until it doesn't change anymore
27 while true; do
28 new_full_var="`eval echo $full_var`"
29 if test "x$new_full_var"="x$full_var"; then break; fi
30 full_var=$new_full_var
31 done
32
33 dnl clean up
34 full_var=$new_full_var
35 AC_SUBST([$1], "$full_var")
36
37 dnl restore prefix and exec_prefix
38 prefix=$prefix_save
39 exec_prefix=$exec_prefix_save
40])
041
=== added file 'autogen.sh'
--- autogen.sh 1970-01-01 00:00:00 +0000
+++ autogen.sh 2013-02-19 17:19:01 +0000
@@ -0,0 +1,13 @@
1#!/bin/sh
2
3srcdir=`dirname $0`
4
5PKG_NAME="unity-lens-video"
6
7which gnome-autogen.sh || {
8 echo "You need gnome-common from GNOME SVN"
9 exit 1
10}
11
12USE_GNOME2_MACROS=1 \
13. gnome-autogen.sh "$@"
014
=== added file 'configure.ac'
--- configure.ac 1970-01-01 00:00:00 +0000
+++ configure.ac 2013-02-19 17:19:01 +0000
@@ -0,0 +1,208 @@
1AC_INIT(unity-lens-video, 6.8.0, https://launchpad.net/unity-lens-video)
2AC_COPYRIGHT([Copyright 2012 Canonical])
3
4AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
5
6#####################################################
7# Silent build rules
8#####################################################
9m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
10
11AC_PREREQ(2.59)
12
13AC_CONFIG_HEADERS([config.h])
14
15#####################################################
16# Init the other things we depend on
17#####################################################
18AM_MAINTAINER_MODE
19AM_PROG_VALAC([0.16.0])
20AS_IF([test -z "$VALAC"], [AC_MSG_ERROR(["No valac compiler found."])])
21AC_PROG_CC
22AM_PROG_CC_C_O
23AC_HEADER_STDC
24
25LT_INIT
26AC_CONFIG_MACRO_DIR([m4])
27
28#############################################
29# Gettext
30#############################################
31GETTEXT_PACKAGE="$PACKAGE"
32AC_SUBST(GETTEXT_PACKAGE)
33#AC_SUBST([CONFIG_STATUS_DEPENDENCIES],['$(top_srcdir)/po/LINGUAS'])
34AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [gettext domain])
35AM_GLIB_GNU_GETTEXT
36
37# AM_GNOME_GETTEXT above substs $DATADIRNAME
38# this is the directory where the *.{mo,gmo} files are installed
39localedir='${prefix}/${DATADIRNAME}/locale'
40AC_SUBST(localedir)
41
42IT_PROG_INTLTOOL([0.40.0])
43
44AC_DEFINE_UNQUOTED(LOCALE_DIR, "${PREFIX}/${DATADIRNAME}/locale",[Locale directory])
45AC_DEFINE_UNQUOTED(DATADIR, "${PREFIX}/${DATADIRNAME}",[Data directory])
46AC_DEFINE_UNQUOTED(PREFIXDIR, "${PREFIX}",[Prefix directory])
47
48######################################################
49# intltool rule for generating translated .lens file
50######################################################
51INTLTOOL_LENS_RULE='%.lens: %.lens.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@'
52AC_SUBST(INTLTOOL_LENS_RULE)
53
54###########################
55# gcov coverage reporting
56###########################
57m4_include([m4/gcov.m4])
58AC_TDD_GCOV
59AM_CONDITIONAL([HAVE_GCOV], [test "x$ac_cv_check_gcov" = xyes])
60AM_CONDITIONAL([HAVE_LCOV], [test "x$ac_cv_check_lcov" = xyes])
61AM_CONDITIONAL([HAVE_GCOVR], [test "x$ac_cv_check_gcovr" = xyes])
62AC_SUBST(COVERAGE_CFLAGS)
63AC_SUBST(COVERAGE_LDFLAGS)
64
65#####################################################
66# Check for module and library dependancies
67#####################################################
68GLIB_REQUIRED=2.27
69PKG_CHECK_MODULES(LENS_DAEMON,
70 glib-2.0 >= $GLIB_REQUIRED
71 gobject-2.0 >= $GLIB_REQUIRED
72 gio-2.0 >= $GLIB_REQUIRED
73 gio-unix-2.0 >= $GLIB_REQUIRED
74 dee-1.0 >= 1.0.7
75 gee-1.0
76 libsoup-gnome-2.4
77 json-glib-1.0
78 zeitgeist-1.0 >= 0.3.8
79 unity >= 6.90.2
80 unity-extras >= 6.90.2
81 )
82
83AC_SUBST(LENS_DAEMON_CFLAGS)
84AC_SUBST(LENS_DAEMON_LIBS)
85
86####################################################################
87# C compiler warnings
88####################################################################
89AC_ARG_ENABLE([c-warnings],
90 AC_HELP_STRING([--enable-c-warnings=@<:@no/yes@:>@], [show warnings from the C compiler @<:@default=no@:>@]),,
91 [enable_c_warnings=no])
92
93if test "x$enable_c_warnings" = "xyes"; then
94 AC_DEFINE(ENABLE_C_WARNINGS, 1, [show warnings from the C compiler])
95fi
96
97AM_CONDITIONAL(ENABLE_C_WARNINGS, test "$enable_c_warnings" = "yes")
98
99#####################################################
100# local install for distcheck and stand-alone running
101#####################################################
102with_localinstall="no"
103AC_ARG_ENABLE(localinstall,
104 AS_HELP_STRING([--enable-localinstall],
105 [Install all of the files locally instead of in system directories (for distcheck)]),
106 with_localinstall=$enableval,
107 with_localinstall=no)
108
109AM_CONDITIONAL([HAVE_LOCALINSTALL], [test "x$with_localinstall" = "xyes"])
110
111####################################################################
112# Headless tests
113####################################################################
114AC_ARG_ENABLE([headless-tests],
115 AS_HELP_STRING([--enable-headless-tests=@<:@no/yes@:>@],[enable headless test suite (requires Xvfb) @<:@default=no@:>@]),,
116 [enable_headless_tests=no])
117
118AM_CONDITIONAL([ENABLE_HEADLESS_TESTS],[test "x$enable_headless_tests" != "xno"])
119
120if test "x$enable_headless_tests" = "xyes"; then
121 AC_PATH_PROG([XVFB],[xvfb-run])
122fi
123
124#####################################################
125# local install for distcheck and stand-alone running
126#####################################################
127with_localinstall="no"
128AC_ARG_ENABLE(localinstall,
129 AS_HELP_STRING([--enable-localinstall],
130 [Install all of the files locally instead of in system directories (for distcheck)]),
131 with_localinstall=$enableval,
132 with_localinstall=no)
133
134AM_CONDITIONAL([HAVE_LOCALINSTALL], [test "x$with_localinstall" = "xyes"])
135
136#####################################################
137# Expand variables needed for config.vala
138#####################################################
139AS_AC_EXPAND(PREFIX, $prefix)
140AC_SUBST(PREFIX)
141
142AS_AC_EXPAND(DATADIR, $datarootdir)
143AC_SUBST(DATADIR)
144
145#####################################################
146# Look for dbus service dir
147#####################################################
148if test "x$with_localinstall" = "xyes"; then
149 DBUSSERVICEDIR="${datadir}/dbus-1/services/"
150else
151 DBUSSERVICEDIR=`$PKG_CONFIG --variable=session_bus_services_dir dbus-1`
152fi
153AC_SUBST(DBUSSERVICEDIR)
154
155#####################################################
156# Look for correct Lenses dir
157#####################################################
158if test "x$with_localinstall" = "xyes"; then
159 LENSESDIR="${datadir}/unity/lenses"
160else
161 LENSESDIR=`$PKG_CONFIG --variable=lensesdir unity`
162fi
163AC_SUBST(LENSESDIR)
164
165#############################################
166# GSettings macros
167#############################################
168
169GLIB_GSETTINGS
170
171#####################################################
172# Create the Makefiles
173#####################################################
174AC_CONFIG_FILES([
175 Makefile
176 data/Makefile
177 data/video.lens.in
178 src/Makefile
179 po/Makefile.in
180 src/config.vala
181 tests/unit/config-tests.vala
182 tests/unit/Makefile
183 vapi/Makefile
184])
185AC_OUTPUT
186
187#####################################################
188# Output the results
189#####################################################
190AC_MSG_NOTICE([
191
192 Unity Video Lens Daemon $VERSION
193 ----------------------------------
194
195 Prefix : ${prefix}
196
197 Local install : ${with_localinstall}
198
199 Extra CFlags : ${CPPFLAGS} $MAINTAINER_CFLAGS
200 Extra ValaFlags : ${CPPFLAGS} $MAINTAINER_VALAFLAGS
201
202 Lenses Directory: ${LENSESDIR}
203
204 Testing
205 Headless tests : ${enable_headless_tests}
206 Coverage reporting : ${use_gcov}
207
208])
0209
=== added directory 'data'
=== added file 'data/Makefile.am'
--- data/Makefile.am 1970-01-01 00:00:00 +0000
+++ data/Makefile.am 2013-02-19 17:19:01 +0000
@@ -0,0 +1,27 @@
1dbus_servicesdir = $(DBUSSERVICEDIR)
2service_in_files = \
3 unity-scope-video-remote.service.in \
4 unity-lens-video.service.in \
5 $(NULL)
6
7dbus_services_DATA = $(service_in_files:.service.in=.service)
8
9%.service: %.service.in
10 $(AM_V_GEN)sed -e "s|\@pkglibexecdir\@|$(pkglibexecdir)|" $< > $@
11
12lens_in_files = video.lens.in video-remote.scope
13lensdir = $(LENSESDIR)/video
14lens_DATA = $(lens_in_files:.lens.in=.lens)
15
16@INTLTOOL_LENS_RULE@
17
18EXTRA_DIST = \
19 $(service_in_files) \
20 $(lens_in_files) \
21 $(NULL)
22
23CLEANFILES = \
24 unity-scope-video-remote.service \
25 unity-lens-video.service \
26 video.lens \
27 $(NULL)
028
=== renamed file 'unity-lens-video.desktop' => 'data/unity-lens-video.desktop'
=== renamed file 'unity-lens-video.png' => 'data/unity-lens-video.png'
=== renamed file 'unity-lens-video.service' => 'data/unity-lens-video.service.in'
--- unity-lens-video.service 2012-02-02 19:43:07 +0000
+++ data/unity-lens-video.service.in 2013-02-19 17:19:01 +0000
@@ -1,3 +1,3 @@
1[D-BUS Service]1[D-BUS Service]
2Name=net.launchpad.lens.video2Name=net.launchpad.lens.video
3Exec=/usr/lib/unity-lens-video/unity-lens-video3Exec=@pkglibexecdir@/unity-video-lens-daemon
44
=== added file 'data/unity-scope-video-remote.service.in'
--- data/unity-scope-video-remote.service.in 1970-01-01 00:00:00 +0000
+++ data/unity-scope-video-remote.service.in 2013-02-19 17:19:01 +0000
@@ -0,0 +1,3 @@
1[D-BUS Service]
2Name=net.launchpad.scope.RemoteVideos
3Exec=@pkglibexecdir@/unity-scope-video-remote
04
=== added file 'data/video-remote.scope'
--- data/video-remote.scope 1970-01-01 00:00:00 +0000
+++ data/video-remote.scope 2013-02-19 17:19:01 +0000
@@ -0,0 +1,3 @@
1[Scope]
2DBusName=net.launchpad.scope.RemoteVideos
3DBusPath=/net/launchpad/scope/remotevideos
04
=== renamed file 'video.lens.in' => 'data/video.lens.in.in'
=== modified file 'debian/changelog'
--- debian/changelog 2012-12-05 09:29:20 +0000
+++ debian/changelog 2013-02-19 17:19:01 +0000
@@ -1,3 +1,14 @@
1unity-lens-video (0.3.14daily12.12.05-0ubuntu2) UNRELEASED; urgency=low
2
3 * debian/control:
4 - Update Build-Depends for Vala rewrite
5 - Add unity-scope-video-remote to this source package
6 * debian/rules:
7 - Use dh-autoreconf
8 - Enable xvfb tests
9
10 -- Michael Terry <mterry@ubuntu.com> Tue, 19 Feb 2013 17:10:20 +0000
11
1unity-lens-video (0.3.14daily12.12.05-0ubuntu1) raring; urgency=low12unity-lens-video (0.3.14daily12.12.05-0ubuntu1) raring; urgency=low
213
3 [ Michael Terry ]14 [ Michael Terry ]
415
=== modified file 'debian/control'
--- debian/control 2012-11-08 04:06:55 +0000
+++ debian/control 2013-02-19 17:19:01 +0000
@@ -2,28 +2,41 @@
2Section: gnome2Section: gnome
3Priority: optional3Priority: optional
4Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>4Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
5XSBC-Original-Maintainer: David Calle <davidc@framli.eu>5Build-Depends: debhelper (>= 9),
6Build-Depends: debhelper (>= 9), 6 dh-autoreconf,
7 python,
8 python-distutils-extra,
9 dh-translations,7 dh-translations,
10Standards-Version: 3.9.38 gnome-common,
11Homepage: https://launchpad.net/unity-lens-videos9 libdee-dev (>= 1.0.7),
10 libgee-dev,
11 libglib2.0-dev (>= 2.27),
12 libjson-glib-dev,
13 libsoup-gnome2.4-dev,
14 libunity-dev (>= 6.90.2),
15 libzeitgeist-dev (>= 0.3.8),
16 valac-0.18,
17 xvfb,
18Standards-Version: 3.9.4
19Homepage: https://launchpad.net/unity-lens-video
12# If you aren't a member of ~unity-team but need to upload packaging changes,20# If you aren't a member of ~unity-team but need to upload packaging changes,
13# just go ahead. ~unity-team will notice and sync up the code again.21# just go ahead. ~unity-team will notice and sync up the code again.
14Vcs-Bzr: https://code.launchpad.net/~unity-team/unity-lens-videos/trunk22Vcs-Bzr: https://code.launchpad.net/~unity-team/unity-lens-video/trunk
1523
16Package: unity-lens-video24Package: unity-lens-video
17Architecture: all25Architecture: any
18Depends: ${misc:Depends},26Depends: ${misc:Depends},
19 ${python:Depends},27 ${shlibs:Depends},
20 gir1.2-unity-5.0,
21 gir1.2-dee-1.0,
22 gir1.2-glib-2.0,
23 python-zeitgeist,
24 unity-lens-music (>= 6.6.0),28 unity-lens-music (>= 6.6.0),
25Recommends: unity-scope-video-remote29Recommends: unity-scope-video-remote
26Breaks: unity (<< 6.0.0),30Breaks: unity (<< 6.0.0),
27Description: Unity Video lens31Description: Unity Video lens
28 A plugin to search videos in the Dash.32 A plugin to search videos in the Dash.
2933
34Package: unity-scope-video-remote
35Architecture: any
36Depends: ${misc:Depends},
37 ${shlibs:Depends},
38 gvfs-bin,
39 unity-lens-video,
40Enhances: unity-lens-video
41Description: Remote videos engine
42 This scope adds a remote videos search engine to the Video lens.
3043
=== modified file 'debian/copyright'
--- debian/copyright 2012-02-14 14:21:28 +0000
+++ debian/copyright 2013-02-19 17:19:01 +0000
@@ -1,15 +1,13 @@
1Format: http://dep.debian.net/deps/dep51Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
2Source: https://launchpad.net/unity-lens-videos2Source: https://launchpad.net/unity-lens-video
3Upstream-Name: unity-lens-video3Upstream-Name: Unity Video Lens
4Upstream-Contact: David Calle <davidc@framli.eu>
54
6Files: *5Files: *
7Copyright: 2011 David Calle <davidc@framli.eu>6Copyright: 2011-2013 Canonical Ltd
8License: GPL-3.0+7License: GPL-3
9 This program is free software: you can redistribute it and/or modify8 This program is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by9 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or10 the Free Software Foundation, version 3 of the License.
12 (at your option) any later version.
13 .11 .
14 This program is distributed in the hope that it will be useful,12 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of13 but WITHOUT ANY WARRANTY; without even the implied warranty of
1614
=== modified file 'debian/rules'
--- debian/rules 2012-11-08 04:06:55 +0000
+++ debian/rules 2013-02-19 17:19:01 +0000
@@ -1,9 +1,14 @@
1#!/usr/bin/make -f1#!/usr/bin/make -f
2# -*- makefile -*-2# -*- makefile -*-
33
4
5%:4%:
6 dh $@ --with python2,translations5 dh $@ --with autoreconf,translations
6
7override_dh_autoreconf:
8 NOCONFIGURE=1 dh_autoreconf ./autogen.sh
9
10override_dh_auto_configure:
11 dh_auto_configure -- --enable-headless-tests
712
8override_dh_install:13override_dh_install:
9 dh_install --fail-missing14 dh_install --fail-missing
1015
=== added file 'debian/unity-lens-video.install'
--- debian/unity-lens-video.install 1970-01-01 00:00:00 +0000
+++ debian/unity-lens-video.install 2013-02-19 17:19:01 +0000
@@ -0,0 +1,3 @@
1/usr/lib/*/unity-lens-video/unity-video-lens-daemon
2/usr/share/dbus-1/services/unity-lens-video.service
3/usr/share/unity/lenses/video/video.lens
04
=== added file 'debian/unity-scope-video-remote.install'
--- debian/unity-scope-video-remote.install 1970-01-01 00:00:00 +0000
+++ debian/unity-scope-video-remote.install 2013-02-19 17:19:01 +0000
@@ -0,0 +1,3 @@
1/usr/lib/*/unity-lens-video/unity-scope-video-remote
2/usr/share/dbus-1/services/unity-scope-video-remote.service
3/usr/share/unity/lenses/video/video-remote.scope
04
=== modified file 'debian/watch'
--- debian/watch 2012-03-21 21:41:31 +0000
+++ debian/watch 2013-02-19 17:19:01 +0000
@@ -1,2 +1,2 @@
1version=31version=3
2http://launchpad.net/unity-lens-videos/+download .*/unity-lens-video-([0-9.]+)\.tar\.gz2http://launchpad.net/unity-lens-video/+download .*/unity-lens-video-([0-9.]+)\.tar\.gz
33
=== added directory 'm4'
=== added file 'm4/gcov.m4'
--- m4/gcov.m4 1970-01-01 00:00:00 +0000
+++ m4/gcov.m4 2013-02-19 17:19:01 +0000
@@ -0,0 +1,86 @@
1# Checks for existence of coverage tools:
2# * gcov
3# * lcov
4# * genhtml
5# * gcovr
6#
7# Sets ac_cv_check_gcov to yes if tooling is present
8# and reports the executables to the variables LCOV, GCOVR and GENHTML.
9AC_DEFUN([AC_TDD_GCOV],
10[
11 AC_ARG_ENABLE(gcov,
12 AS_HELP_STRING([--enable-gcov],
13 [enable coverage testing with gcov]),
14 [use_gcov=$enableval], [use_gcov=no])
15
16 if test "x$use_gcov" = "xyes"; then
17 # we need gcc:
18 if test "$GCC" != "yes"; then
19 AC_MSG_ERROR([GCC is required for --enable-gcov])
20 fi
21
22 # Check if ccache is being used
23 AC_CHECK_PROG(SHTOOL, shtool, shtool)
24 case `$SHTOOL path $CC` in
25 *ccache*[)] gcc_ccache=yes;;
26 *[)] gcc_ccache=no;;
27 esac
28
29 if test "$gcc_ccache" = "yes" && (test -z "$CCACHE_DISABLE" || test "$CCACHE_DISABLE" != "1"); then
30 AC_MSG_ERROR([ccache must be disabled when --enable-gcov option is used. You can disable ccache by setting environment variable CCACHE_DISABLE=1.])
31 fi
32
33 lcov_version_list="1.6 1.7 1.8 1.9"
34 AC_CHECK_PROG(LCOV, lcov, lcov)
35 AC_CHECK_PROG(GENHTML, genhtml, genhtml)
36
37 if test "$LCOV"; then
38 AC_CACHE_CHECK([for lcov version], glib_cv_lcov_version, [
39 glib_cv_lcov_version=invalid
40 lcov_version=`$LCOV -v 2>/dev/null | $SED -e 's/^.* //'`
41 for lcov_check_version in $lcov_version_list; do
42 if test "$lcov_version" = "$lcov_check_version"; then
43 glib_cv_lcov_version="$lcov_check_version (ok)"
44 fi
45 done
46 ])
47 else
48 lcov_msg="To enable code coverage reporting you must have one of the following lcov versions installed: $lcov_version_list"
49 AC_MSG_ERROR([$lcov_msg])
50 fi
51
52 case $glib_cv_lcov_version in
53 ""|invalid[)]
54 lcov_msg="You must have one of the following versions of lcov: $lcov_version_list (found: $lcov_version)."
55 AC_MSG_ERROR([$lcov_msg])
56 LCOV="exit 0;"
57 ;;
58 esac
59
60 if test -z "$GENHTML"; then
61 AC_MSG_ERROR([Could not find genhtml from the lcov package])
62 fi
63
64 ac_cv_check_gcov=yes
65 ac_cv_check_lcov=yes
66
67 # Remove all optimization flags from CFLAGS
68 changequote({,})
69 CFLAGS=`echo "$CFLAGS" | $SED -e 's/-O[0-9]*//g'`
70 changequote([,])
71
72 # Add the special gcc flags
73 COVERAGE_CFLAGS="-O0 -fprofile-arcs -ftest-coverage"
74 COVERAGE_CXXFLAGS="-O0 -fprofile-arcs -ftest-coverage"
75 COVERAGE_LDFLAGS="-lgcov"
76
77 # Check availability of gcovr
78 AC_CHECK_PROG(GCOVR, gcovr, gcovr)
79 if test -z "$GCOVR"; then
80 ac_cv_check_gcovr=no
81 else
82 ac_cv_check_gcovr=yes
83 fi
84
85fi
86]) # AC_TDD_GCOV
087
=== modified file 'po/POTFILES.in'
--- po/POTFILES.in 2012-02-22 10:47:03 +0000
+++ po/POTFILES.in 2013-02-19 17:19:01 +0000
@@ -1,3 +1,6 @@
1[encoding: UTF-8]1[encoding: UTF-8]
2src/unity-lens-video2src/main.vala
3[type: gettext/ini]video.lens.in3src/remote-video-main.vala
4src/ubuntu-video-search.vala
5tests/unit/test-ubuntu-video-search.vala
6[type: gettext/ini]data/video.lens.in
47
=== added file 'po/POTFILES.skip'
--- po/POTFILES.skip 1970-01-01 00:00:00 +0000
+++ po/POTFILES.skip 2013-02-19 17:19:01 +0000
@@ -0,0 +1,7 @@
1src/daemon.c
2src/scope.c
3src/remote-scope.c
4src/ubuntu-video-search.c
5tests/unit/test-ubuntu-video-search.c
6tests/unit/ubuntu-video-search.c
7tests/unit/remote-scope.c
08
=== removed file 'po/unity-lens-video.pot'
--- po/unity-lens-video.pot 2012-09-14 09:12:09 +0000
+++ po/unity-lens-video.pot 1970-01-01 00:00:00 +0000
@@ -1,70 +0,0 @@
1# SOME DESCRIPTIVE TITLE.
2# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3# This file is distributed under the same license as the PACKAGE package.
4# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5#
6#, fuzzy
7msgid ""
8msgstr ""
9"Project-Id-Version: PACKAGE VERSION\n"
10"Report-Msgid-Bugs-To: \n"
11"POT-Creation-Date: 2012-09-14 10:03+0100\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
15"Language: \n"
16"MIME-Version: 1.0\n"
17"Content-Type: text/plain; charset=CHARSET\n"
18"Content-Transfer-Encoding: 8bit\n"
19
20#: ../src/unity-lens-video:49 ../src/unity-lens-video:56
21msgid "My Videos"
22msgstr ""
23
24#: ../src/unity-lens-video:50
25msgid "Online"
26msgstr ""
27
28#: ../src/unity-lens-video:51 ../video.lens.in.h:1
29msgid "Videos"
30msgstr ""
31
32#: ../src/unity-lens-video:52
33msgid "Recently Viewed"
34msgstr ""
35
36#: ../src/unity-lens-video:53
37msgid "More suggestions"
38msgstr ""
39
40#: ../src/unity-lens-video:54
41msgid "Search Videos"
42msgstr ""
43
44#: ../src/unity-lens-video:55
45msgid "Sources"
46msgstr ""
47
48#: ../src/unity-lens-video:173
49msgid "Format"
50msgstr ""
51
52#: ../src/unity-lens-video:174
53msgid "Dimensions"
54msgstr ""
55
56#: ../src/unity-lens-video:175
57msgid "Size"
58msgstr ""
59
60#: ../src/unity-lens-video:178
61msgid "Show in Folder"
62msgstr ""
63
64#: ../src/unity-lens-video:181
65msgid "Play"
66msgstr ""
67
68#: ../video.lens.in.h:2
69msgid "Search local videos"
70msgstr ""
710
=== removed file 'setup.cfg'
--- setup.cfg 2012-02-22 10:48:53 +0000
+++ setup.cfg 1970-01-01 00:00:00 +0000
@@ -1,10 +0,0 @@
1[build]
2i18n=True
3
4[build_i18n]
5domain=unity-lens-video
6desktop_files=[ ("share/unity/lenses/video",
7 ("video.lens.in",)
8 )
9 ]
10
110
=== removed file 'setup.py'
--- setup.py 2012-10-16 12:12:46 +0000
+++ setup.py 1970-01-01 00:00:00 +0000
@@ -1,18 +0,0 @@
1#!/usr/bin/env python
2#
3from distutils.core import setup
4from DistUtilsExtra.command import *
5
6setup(name="unity-lens-video",
7 version="0.3.14",
8 author="David Calle",
9 author_email="davidc@framli.eu",
10 url="http://launchpad.net/~davidc3",
11 license="GNU General Public License (GPL)",
12 data_files=[
13 ('lib/unity-lens-video', ['src/unity-lens-video']),
14 ('share/dbus-1/services', ['unity-lens-video.service']),
15 ('share/applications', ['unity-lens-video.desktop']),
16 ('share/pixmaps', ['unity-lens-video.png']),
17 ], cmdclass={"build": build_extra.build_extra,
18 "build_i18n": build_i18n.build_i18n,})
190
=== added file 'src/Makefile.am'
--- src/Makefile.am 1970-01-01 00:00:00 +0000
+++ src/Makefile.am 2013-02-19 17:19:01 +0000
@@ -0,0 +1,133 @@
1NULL =
2BUILT_SOURCES =
3CLEANFILES =
4EXTRA_DIST =
5
6DATADIR = $(datadir)
7
8AM_CPPFLAGS = $(COVERAGE_CFLAGS)
9AM_LDFLAGS = $(COVERAGE_LDFLAGS)
10
11pkglibexec_PROGRAMS = \
12 unity-video-lens-daemon \
13 unity-scope-video-remote \
14 $(NULL)
15
16unity_video_lens_daemon_CPPFLAGS = \
17 -DDATADIR=\"$(DATADIR)\" \
18 -DPKGDATADIR=\"$(PKGDATADIR)\" \
19 -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \
20 -DG_LOG_DOMAIN=\"unity-video-lens-daemon\" \
21 $(LENS_DAEMON_CFLAGS) \
22 $(MAINTAINER_CFLAGS) \
23 -I$(srcdir) \
24 $(NULL)
25
26unity_scope_video_remote_CPPFLAGS = \
27 -DDATADIR=\"$(DATADIR)\" \
28 -DPKGDATADIR=\"$(PKGDATADIR)\" \
29 -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \
30 -DG_LOG_DOMAIN=\"unity-scope-video-remote\" \
31 $(LENS_DAEMON_CFLAGS) \
32 $(MAINTAINER_CFLAGS) \
33 -I$(srcdir) \
34 $(NULL)
35
36unity_video_lens_daemon_VALAFLAGS = \
37 -C \
38 --pkg dee-1.0 \
39 --pkg unity \
40 --pkg unity-extras \
41 --pkg gio-2.0 \
42 --pkg gio-unix-2.0 \
43 --pkg glib-2.0 \
44 --pkg zeitgeist-1.0 \
45 --vapidir $(srcdir) \
46 --vapidir $(top_srcdir)/vapi \
47 --target-glib=2.26 \
48 $(MAINTAINER_VALAFLAGS) \
49 $(NULL)
50
51unity_scope_video_remote_VALAFLAGS = \
52 -C \
53 --pkg dee-1.0 \
54 --pkg unity \
55 --pkg unity-extras \
56 --pkg gio-2.0 \
57 --pkg gio-unix-2.0 \
58 --pkg glib-2.0 \
59 --pkg zeitgeist-1.0 \
60 --pkg libsoup-gnome-2.4 \
61 --pkg libsoup-2.4 \
62 --pkg json-glib-1.0 \
63 --vapidir $(srcdir) \
64 --vapidir $(top_srcdir)/vapi \
65 --target-glib=2.26 \
66 $(MAINTAINER_VALAFLAGS) \
67 $(NULL)
68
69
70unity_video_lens_daemon_LDADD = \
71 $(LENS_DAEMON_LIBS) \
72 $(NULL)
73
74unity_scope_video_remote_LDADD = \
75 $(LENS_DAEMON_LIBS) \
76 $(NULL)
77
78unity_video_lens_daemon_VALASOURCES = \
79 blacklist-tracker.vala \
80 daemon.vala \
81 main.vala \
82 locate.vala \
83 scope.vala \
84 config.vala \
85 thumbnailer.vala \
86 utils.vala \
87 video-file.vala \
88 $(NULL)
89
90unity_scope_video_remote_VALASOURCES = \
91 config.vala \
92 remote-scope-globals.vala \
93 remote-video-main.vala \
94 remote-scope.vala \
95 remote-uri.vala \
96 ubuntu-video-search.vala \
97 utils.vala \
98 video-file.vala \
99 $(NULL)
100
101unity_video_lens_daemon_SOURCES = \
102 $(unity_video_lens_daemon_VALASOURCES:.vala=.c) \
103 $(NULL)
104
105unity_scope_video_remote_SOURCES = \
106 $(unity_scope_video_remote_VALASOURCES:.vala=.c) \
107 $(NULL)
108
109BUILT_SOURCES += \
110 unity_video_lens_daemon.vala.stamp \
111 unity_scope_video_remote.vala.stamp \
112 $(NULL)
113
114EXTRA_DIST += \
115 unity_video_lens_daemon.vala.stamp \
116 unity_scope_video_remote.vala.stamp \
117 $(unity_video_lens_daemon_VALASOURCES) \
118 $(unity_scope_video_remote_VALASOURCES) \
119 $(NULL)
120
121unity_video_lens_daemon.vala.stamp: $(unity_video_lens_daemon_VALASOURCES)
122 $(AM_V_GEN) $(VALAC) $(unity_video_lens_daemon_VALAFLAGS) $^
123 @touch $@
124
125unity_scope_video_remote.vala.stamp: $(unity_scope_video_remote_VALASOURCES)
126 $(AM_V_GEN) $(VALAC) $(unity_scope_video_remote_VALAFLAGS) $^
127 @touch $@
128
129CLEANFILES += \
130 *.stamp \
131 $(unity_video_lens_daemon_VALASOURCES:.vala=.c) \
132 $(unity_scope_video_remote_VALASOURCES:.vala=.c) \
133 $(NULL)
0134
=== added file 'src/blacklist-tracker.vala'
--- src/blacklist-tracker.vala 1970-01-01 00:00:00 +0000
+++ src/blacklist-tracker.vala 2013-02-19 17:19:01 +0000
@@ -0,0 +1,128 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
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 version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by Michal Hruby <michal.hruby@canonical.com>
17 *
18 */
19using Zeitgeist;
20using Config;
21using Gee;
22
23namespace Unity.VideoLens
24{
25 /* Libzeitgeist currently doesn't expose any blacklist API, so we need to
26 * use direct dbus calls */
27 [DBus (name = "org.gnome.zeitgeist.Blacklist")]
28 public interface Blacklist : Object
29 {
30 [DBus (signature = "a{s(asaasay)}")]
31 public abstract async Variant get_templates () throws Error;
32 public signal void template_added (string template_id,
33 [DBus (signature = "(asassay)")] Variant template);
34 public signal void template_removed (string template_id,
35 [DBus (signature = "(asassay)")] Variant template);
36 }
37
38 public class BlacklistTracker : Object
39 {
40 private HashTable<string, Event> event_templates;
41 private Blacklist blacklist_proxy;
42
43 private Set<string> blacklisted_uris;
44 private bool cached_uris_dirty;
45
46 construct
47 {
48 event_templates = new HashTable<string, Event> (str_hash, str_equal);
49 blacklisted_uris = new HashSet<string> ();
50
51 Bus.get_proxy<Blacklist> (BusType.SESSION, "org.gnome.zeitgeist.Engine",
52 "/org/gnome/zeitgeist/blacklist", 0, null, (src, res) =>
53 {
54 try
55 {
56 blacklist_proxy = (src as DBusConnection).get_proxy<Blacklist>.end (res);
57 fetch_blacklists ();
58 }
59 catch (GLib.Error err)
60 {
61 warning ("%s", err.message);
62 }
63 });
64 }
65
66 private async void fetch_blacklists ()
67 {
68 blacklist_proxy.template_added.connect (this.template_added);
69 blacklist_proxy.template_removed.connect (this.template_removed);
70 Variant all_templates = yield blacklist_proxy.get_templates ();
71
72 VariantIter iter = new VariantIter (all_templates);
73 string template_id;
74 Variant event_variant;
75 while (iter.next ("{s@(asaasay)}", out template_id, out event_variant))
76 {
77 Event e = new Event.from_variant (event_variant);
78 event_templates[template_id] = e;
79 }
80 cached_uris_dirty = true;
81 }
82
83 private void template_added (string id, Variant template)
84 {
85 Event e = new Event.from_variant (template);
86 event_templates[id] = e;
87 cached_uris_dirty = true;
88 }
89
90 private void template_removed (string id, Variant template)
91 {
92 event_templates.remove (id);
93 cached_uris_dirty = true;
94 }
95
96 private void update_uris ()
97 {
98 var iter = HashTableIter<string, Event> (event_templates);
99 unowned Event e;
100 while (iter.next (null, out e))
101 {
102 if (e.num_subjects () > 0)
103 {
104 unowned Subject s = e.get_subject (0);
105 unowned string uri = s.get_uri ();
106 if (uri == null || uri == "") continue;
107
108 if (uri.has_suffix ("*"))
109 {
110 blacklisted_uris.add (uri.substring (0, uri.length - 1));
111 }
112 }
113 }
114 }
115
116 public unowned Set<string> get_blacklisted_uris ()
117 {
118 if (cached_uris_dirty)
119 {
120 blacklisted_uris.clear ();
121 update_uris ();
122 cached_uris_dirty = false;
123 }
124 return blacklisted_uris;
125 }
126 }
127
128} /* namespace */
0129
=== added file 'src/config.vala.in'
--- src/config.vala.in 1970-01-01 00:00:00 +0000
+++ src/config.vala.in 2013-02-19 17:19:01 +0000
@@ -0,0 +1,16 @@
1namespace Config {
2
3 const string PREFIX = "@prefix@";
4
5 const string DATADIR = "@DATADIR@";
6
7 const string PKGDATADIR = "@DATADIR@/unity";
8
9 const string BINDIR = "@prefix@/bin";
10
11 const string LOCALEDIR = "@DATADIR@/locale";
12
13 const string PACKAGE = "@PACKAGE@";
14
15 const string VERSION = "@VERSION@";
16}
017
=== added file 'src/daemon.vala'
--- src/daemon.vala 1970-01-01 00:00:00 +0000
+++ src/daemon.vala 2013-02-19 17:19:01 +0000
@@ -0,0 +1,79 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
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 version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
17 * based on python code by David Calle <davidc@framli.eu>
18 */
19using Config;
20
21namespace Unity.VideoLens {
22
23 const string ICON_PATH = Config.DATADIR + "/icons/unity-icon-theme/places/svg/";
24
25 public class Daemon : GLib.Object
26 {
27 private Unity.Lens lens;
28
29 construct
30 {
31 lens = new Unity.Lens("/net/launchpad/lens/video", "video");
32 lens.search_in_global = true;
33 lens.search_hint = _("Search videos");
34 lens.sources_display_name = _("Sources");
35 lens.visible = true;
36
37 populate_categories ();
38 populate_filters ();
39
40 var video_scope = new VideoScope ();
41 lens.add_local_scope (video_scope);
42
43 try {
44 lens.export ();
45 } catch (GLib.IOError e) {
46 stdout.printf ("error %s\n", e.message);
47 }
48 }
49
50 private void populate_filters ()
51 {
52 var filters = new GLib.List<Unity.Filter> ();
53
54 /* TODO */
55
56 /* A filter */
57 {
58 }
59
60 /* Another filter */
61 {
62 }
63
64 lens.filters = filters;
65 }
66
67 private void populate_categories ()
68 {
69 var categories = new GLib.List<Unity.Category> ();
70 var icon_dir = File.new_for_path (ICON_PATH);
71
72 categories.append(new Unity.Category (_("My Videos"), new FileIcon (icon_dir.get_child ("group-videos.svg")), Unity.CategoryRenderer.VERTICAL_TILE));
73 categories.append(new Unity.Category (_("Online"), new FileIcon (icon_dir.get_child ("group-internet.svg")), Unity.CategoryRenderer.VERTICAL_TILE));
74 categories.append(new Unity.Category (_("More suggestions"), new FileIcon (icon_dir.get_child ("group-treat-yourself.svg")), Unity.CategoryRenderer.VERTICAL_TILE));
75
76 lens.categories = categories;
77 }
78 }
79}
080
=== added file 'src/locate.vala'
--- src/locate.vala 1970-01-01 00:00:00 +0000
+++ src/locate.vala 2013-02-19 17:19:01 +0000
@@ -0,0 +1,112 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
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 version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
17 *
18 */
19
20namespace Unity.VideoLens
21{
22 public class Locate
23 {
24 /* Filter used by locate handling loop; return value of false means file will be ignored */
25 public delegate bool LocateFilter (string path);
26
27 private static const int MAX_RESULTS = 100;
28 private static const int MAX_LOCATE_RESULTS = 200;
29
30 private string cache_db;
31 private string videos_folder;
32 public string locate_bin { get; set; default = "locate"; }
33 public string updatedb_bin { get; set; default = "updatedb"; }
34
35 public Locate (string cache_dir, string videos_dir)
36 {
37 cache_db = cache_dir + "/videos.db";
38 videos_folder = videos_dir;
39 }
40
41 public Gee.ArrayList<VideoFile?>? run_locate (string search_string, Thumbnailer thumbnailer, LocateFilter? filter = null)
42 {
43 if (Utils.is_regular_file (cache_db))
44 {
45 string stdout;
46 try
47 {
48 string query = locate_query_string (search_string);
49 GLib.Process.spawn_command_line_sync (@"$locate_bin -l $MAX_LOCATE_RESULTS -id $cache_db $query", out stdout);
50 var result_list = parse_locate_results (stdout, 100, thumbnailer, filter);
51 return result_list;
52 }
53 catch (GLib.Error e)
54 {
55 warning ("Failed to run locate: %s", e.message);
56 }
57 }
58 return null;
59 }
60
61 internal string locate_query_string (string search_string)
62 {
63 return videos_folder + "*" + search_string.replace (" ", "*") + "*";
64 }
65
66 public void updatedb ()
67 {
68 try
69 {
70 GLib.Process.spawn_command_line_async (@"$updatedb_bin -o $cache_db -l 0 -U $videos_folder");
71 }
72 catch (GLib.Error e)
73 {
74 warning ("Can't create database, will retry: %s", e.message);
75 }
76 }
77
78 public Gee.ArrayList<VideoFile?> parse_locate_results (string locate_output, int max, Thumbnailer thumbnailer, LocateFilter? filter)
79 {
80 var results_list = locate_output.split ("\n");
81 var res = new Gee.ArrayList<VideoFile?> ();
82 int video_count = 0;
83 foreach (var video in results_list)
84 {
85 if (video_count >= MAX_RESULTS)
86 break;
87
88 try
89 {
90 if (Utils.is_video (video) && (filter == null || filter (video)))
91 {
92 video_count++;
93 var name = Utils.get_name (video);
94 VideoFile video_file = VideoFile ()
95 {
96 title = name,
97 lc_title = name.down (),
98 uri = "file://" + video,
99 icon = thumbnailer.get_icon (video)
100 };
101 res.add (video_file);
102 }
103 }
104 catch (Error e)
105 {
106 // silently ignore
107 }
108 }
109 return res;
110 }
111 }
112}
0\ No newline at end of file113\ No newline at end of file
1114
=== added file 'src/main.vala'
--- src/main.vala 1970-01-01 00:00:00 +0000
+++ src/main.vala 2013-02-19 17:19:01 +0000
@@ -0,0 +1,59 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
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 version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17using GLib;
18using Config;
19
20namespace Unity.VideoLens {
21
22 static Application? app = null;
23 static Daemon? daemon = null;
24
25 public static int main (string[] args)
26 {
27 GLib.Environment.set_prgname ("unity-video-lens");
28
29 /* Sort up locale to get translations but also sorting and
30 * punctuation right */
31 GLib.Intl.textdomain (Config.PACKAGE);
32 GLib.Intl.bindtextdomain (Config.PACKAGE, Config.LOCALEDIR);
33 GLib.Intl.bind_textdomain_codeset (Config.PACKAGE, "UTF-8");
34 GLib.Intl.setlocale(GLib.LocaleCategory.ALL, "");
35
36 try
37 {
38 app = Extras.dbus_own_name ("net.launchpad.lens.video", () =>
39 {
40 daemon = new Daemon ();
41 });
42 }
43 catch (Error e)
44 {
45 warning ("Failed to start video lens daemon: %s\n", e.message);
46 return 1;
47 }
48
49 if (app == null)
50 {
51 warning ("Another instance of the Unity Videos Lens " +
52 "already appears to be running.\nBailing out.\n");
53 return 2;
54 }
55
56 return app.run ();
57 }
58
59} /* namespace */
060
=== added file 'src/remote-scope-globals.vala'
--- src/remote-scope-globals.vala 1970-01-01 00:00:00 +0000
+++ src/remote-scope-globals.vala 2013-02-19 17:19:01 +0000
@@ -0,0 +1,25 @@
1
2/*
3 * Copyright (C) 2012 Canonical Ltd
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation.
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, see <http://www.gnu.org/licenses/>.
16 *
17 * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
18 *
19 */
20
21namespace Unity.VideoLens
22{
23 static int CAT_INDEX_ONLINE = 1;
24 static int CAT_INDEX_MORE = 2;
25}
0\ No newline at end of file26\ No newline at end of file
127
=== added file 'src/remote-scope.vala'
--- src/remote-scope.vala 1970-01-01 00:00:00 +0000
+++ src/remote-scope.vala 2013-02-19 17:19:01 +0000
@@ -0,0 +1,491 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
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 version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
17 * based on python code by David Calle <davidc@framli.eu>
18 *
19 */
20
21namespace Unity.VideoLens
22{
23 public class RemoteVideoScope : Unity.Scope
24 {
25 private static int REFRESH_INTERVAL = 3600; // fetch sources & recommendations once an hour
26 private static int RETRY_INTERVAL = 60; // retry sources/recommendations after a minute
27
28 private Soup.Session session;
29 private PreferencesManager preferences = PreferencesManager.get_default ();
30 Gee.ArrayList<RemoteVideoFile?> recommendations;
31 int64 recommendations_last_update = 0;
32 Zeitgeist.DataSourceRegistry zg_sources;
33 bool use_zeitgeist;
34
35 public RemoteVideoScope ()
36 {
37 Object (dbus_path: "/net/launchpad/scope/remotevideos");
38 }
39
40 protected override void constructed ()
41 {
42 recommendations = new Gee.ArrayList<RemoteVideoFile?> ();
43
44 use_zeitgeist = false;
45 try
46 {
47 zeitgeist_init ();
48 use_zeitgeist = true;
49 }
50 catch (Error e)
51 {
52 warning ("Failed to initialize Zeitgeist, won't store events");
53 }
54
55 session = new Soup.SessionAsync ();
56 session.ssl_use_system_ca_file = true;
57 session.ssl_strict = true;
58 session.user_agent = "Unity Video Lens Remote Scope v" + Config.VERSION;
59 session.add_feature_by_type (typeof (SoupGNOME.ProxyResolverGNOME));
60
61 search_in_global = false;
62 search_changed.connect ((search, search_type, cancellable) =>
63 {
64 update_search_async.begin (search, search_type, cancellable);
65 });
66 filters_changed.connect (on_filters_changed);
67 sources.notify["filtering"].connect (on_filters_changed);
68
69 generate_search_key.connect ((scope, search) =>
70 {
71 return search.search_string.strip ();
72 });
73
74 preferences.notify["remote-content-search"].connect ((obj, pspec) =>
75 {
76 queue_search_changed (SearchType.DEFAULT);
77 });
78
79 activate_uri.connect (on_activate_uri);
80 preview_uri.connect (on_preview_uri);
81
82 query_list_of_sources ();
83
84 // refresh the at least once every 30 minutes
85 GLib.Timeout.add_seconds (REFRESH_INTERVAL/2, () =>
86 {
87 queue_search_changed (SearchType.DEFAULT); return true;
88 });
89
90 try
91 {
92 export ();
93 }
94 catch (Error e)
95 {
96 error ("Failed to export scope: %s", e.message);
97 }
98 }
99
100 /* Query the server for a list of sources that will be used
101 * to build sources filter options and search queries.
102 */
103 private void query_list_of_sources ()
104 {
105 var msg = new Soup.Message ("GET", UbuntuVideoSearch.sources_uri ());
106 session.queue_message (msg, sources_cb);
107 }
108
109 private Gee.ArrayList<RemoteVideoFile?>? handle_search_response (Soup.Message msg, bool is_treat_yourself = false)
110 {
111 if (msg.status_code != 200)
112 {
113 warning ("Unable to get results from the server: %u, %s", msg.status_code, msg.reason_phrase);
114 }
115 else
116 {
117 try
118 {
119 return UbuntuVideoSearch.process_search_results ((string)msg.response_body.data, is_treat_yourself);
120 }
121 catch (Error e)
122 {
123 warning ("Error processing search results: %s", e.message);
124 }
125 }
126 return null;
127 }
128
129 private void sources_cb (Soup.Session session, Soup.Message msg)
130 {
131 uint interval = RETRY_INTERVAL;
132 if (msg.status_code != 200)
133 {
134 warning ("Unable to query the server for a list of sources, %u: %s", msg.status_code, msg.reason_phrase);
135 }
136 else
137 {
138 try
139 {
140 var sources_array = UbuntuVideoSearch.process_sources_results ((string) msg.response_body.data);
141
142 // remove all existing sources
143 foreach (var opt in sources.options)
144 {
145 sources.remove_option (opt.id);
146 }
147
148 // add sources
149 foreach (var src in sources_array)
150 {
151 sources.add_option (src, src, null);
152 }
153 interval = REFRESH_INTERVAL;
154 }
155 catch (Error e)
156 {
157 warning ("Got invalid json from the server");
158 }
159 }
160 GLib.Timeout.add_seconds (interval, () =>
161 {
162 query_list_of_sources (); return false;
163 });
164 }
165
166 private Unity.ActivationResponse on_activate_uri (string rawuri)
167 {
168 var fakeuri = RemoteUri.from_rawuri (rawuri);
169 if (fakeuri != null)
170 {
171 if (use_zeitgeist)
172 zeitgeist_insert_event (fakeuri.uri, fakeuri.title, fakeuri.icon);
173 try
174 {
175 GLib.AppInfo.launch_default_for_uri (fakeuri.uri, null);
176 return new Unity.ActivationResponse (Unity.HandledType.HIDE_DASH);
177 }
178 catch (GLib.Error e)
179 {
180 warning ("Failed to launch default application for '%s': %s", fakeuri.uri, e.message);
181 }
182 }
183 else
184 {
185 warning ("Invalid raw uri: '%s'", rawuri);
186 }
187 return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED);
188 }
189
190 private Unity.ActivationResponse on_play_video (string rawuri)
191 {
192 return on_activate_uri (rawuri);
193 }
194
195 private Unity.Preview? build_preview (RemoteUri uri, RemoteVideoDetails? details)
196 {
197 string title = uri.title;
198 string subtitle = "";
199 string description = "";
200
201 if (details != null)
202 {
203 title = details.title;
204 description = details.description;
205
206 if (details.release_date != null && details.release_date != "")
207 subtitle = details.release_date;
208
209 if (details.duration > 0)
210 {
211 string duration = ngettext ("%d min", "%d mins", details.duration).printf (details.duration);
212 if (subtitle != "")
213 subtitle += ", " + duration;
214 else
215 subtitle = duration;
216 }
217 }
218
219 GLib.Icon thumbnail = new GLib.FileIcon (GLib.File.new_for_uri (details != null ? details.image : uri.icon));
220
221 var real_preview = new Unity.MoviePreview (title, subtitle, description, thumbnail);
222 var play_video = new Unity.PreviewAction ("play", _("Play"), null);
223 play_video.activated.connect (on_play_video);
224 real_preview.add_action (play_video);
225
226 // For now, rating == -1 and num_ratings == 0 hides the rating widget from the preview
227 real_preview.set_rating (-1, 0);
228
229 if (details != null)
230 {
231 //TODO: For details of future source types, factor out common detail key/value pairs
232 if (details.directors.length > 0)
233 real_preview.add_info (new Unity.InfoHint ("directors", ngettext ("Director", "Directors", details.directors.length), null, string.joinv (", ", details.directors)));
234
235 if (details.starring != null && details.starring != "")
236 real_preview.add_info (new Unity.InfoHint ("cast", _("Cast"), null, details.starring));
237
238 if (details.genres != null && details.genres.length > 0)
239 real_preview.add_info (new Unity.InfoHint ("genres", ngettext("Genre", "Genres", details.genres.length), null, string.joinv (", ", details.genres)));
240
241 // TODO: Add Vimeo & YouTube details for v1 of JSON API
242 if (details.uploaded_by != null && details.uploaded_by != "")
243 real_preview.add_info (new Unity.InfoHint ("uploaded-by", _("Uploaded by"), null, details.uploaded_by));
244
245 if (details.date_uploaded != null && details.date_uploaded != "")
246 real_preview.add_info (new Unity.InfoHint ("uploaded-on", _("Uploaded on"), null, details.date_uploaded));
247 }
248
249 return real_preview;
250 }
251
252 private Unity.Preview? on_preview_uri (string rawuri)
253 {
254 var fakeuri = RemoteUri.from_rawuri (rawuri);
255 if (fakeuri != null)
256 {
257 if (fakeuri.details_uri != null && fakeuri.details_uri != "")
258 {
259 var preview = new AsyncPreview ();
260 get_details.begin (fakeuri.details_uri, (obj, res) =>
261 {
262 try
263 {
264 var details = get_details.end (res);
265 preview.preview_ready (build_preview (fakeuri, details));
266 }
267 catch (Error e)
268 {
269 warning ("Failed to fetch video details: %s", e.message);
270 preview.preview_ready (build_preview (fakeuri, null));
271 }
272 });
273 return preview;
274 }
275 else
276 {
277 return build_preview (fakeuri, null);
278 }
279 }
280 else
281 {
282 warning ("Invalid raw uri: '%s'", rawuri);
283 }
284
285 return null;
286 }
287
288 private async RemoteVideoDetails? get_details (string url) throws Error
289 {
290 var msg = new Soup.Message ("GET", url);
291 session.queue_message (msg, (session_, msg_) =>
292 {
293 msg = msg_;
294 get_details.callback ();
295 });
296
297 yield;
298
299 if (msg.status_code != 200)
300 {
301 warning ("Unable to get details from the server: %u, %s", msg.status_code, msg.reason_phrase);
302 return null;
303 }
304 else
305 {
306 var details = UbuntuVideoSearch.process_details_results ((string) msg.response_body.data);
307 return details;
308 }
309 }
310
311 private async void update_search_async (LensSearch search, SearchType search_type, Cancellable? cancellable)
312 {
313 var search_string = search.search_string.strip ();
314 debug ("Remote search string changed to: %s", search_string);
315
316 var model = search.results_model;
317 model.clear ();
318
319 // only perform the request if the user has not disabled
320 // online/commercial suggestions. That will hide the category as well.
321 if (preferences.remote_content_search != Unity.PreferencesManager.RemoteContent.ALL)
322 {
323 search.finished();
324 return;
325 }
326
327 // create a list of activated sources
328 var active_sources = new Gee.ArrayList<string> (null);
329 foreach (var opt in sources.options)
330 {
331 if (source_activated (opt.id))
332 active_sources.add (opt.id);
333 }
334
335 // If all the sources are activated, don't bother passing them as arguments
336 if (active_sources.size == sources.options.length ())
337 {
338 active_sources.clear ();
339 }
340
341 if (search_type == Unity.SearchType.DEFAULT)
342 {
343 if (at_least_one_source_is_on (active_sources))
344 {
345 yield perform_search (search_string, search, active_sources, cancellable);
346 }
347 }
348
349 search.finished ();
350 }
351
352 private bool source_activated (string id)
353 {
354 bool active = sources.get_option (id).active;
355 bool filtering = sources.filtering;
356
357 if ((active && filtering) || (!active && !filtering))
358 return true;
359 return false;
360 }
361
362 /* Return a general activation state of all sources of this scope.
363 * This is needed, because we don't want to show recommends if an option
364 * from another scope is the only one activated
365 */
366 private bool at_least_one_source_is_on (Gee.ArrayList<string> active_sources)
367 {
368 return (sources.filtering && active_sources.size > 0 || !sources.filtering);
369 }
370
371 private void on_filters_changed ()
372 {
373 queue_search_changed (SearchType.DEFAULT);
374 }
375
376 /* Query the server with the search string and the list of sources.
377 */
378 private async void perform_search (string search_string, LensSearch search, Gee.ArrayList<string> active_sources, Cancellable? cancellable)
379 {
380 search.results_model.clear ();
381
382 if ((search_string == null || search_string == "") && (active_sources.size == 0) && (recommendations.size > 0))
383 {
384 var time = new DateTime.now_utc ();
385 if (time.to_unix () - recommendations_last_update < REFRESH_INTERVAL)
386 {
387 debug ("Updating search results with recommendations");
388 update_results_model (search.results_model, recommendations);
389 return;
390 }
391 }
392
393 var url = UbuntuVideoSearch.build_search_uri (search_string, active_sources);
394 debug ("Querying the server: %s", url);
395
396 bool is_treat_yourself = (search_string == null || search_string == "" || active_sources.size == 0);
397 var msg = new Soup.Message ("GET", url);
398
399 session.queue_message (msg, (session_, msg_) =>
400 {
401 msg = msg_;
402 perform_search.callback ();
403 });
404
405 var cancelled = false;
406 ulong cancel_id = 0;
407 if (cancellable != null)
408 {
409 cancel_id = cancellable.connect (() =>
410 {
411 cancelled = true;
412 session.cancel_message (msg, Soup.KnownStatusCode.CANCELLED);
413 });
414 }
415
416 yield;
417
418 if (cancelled)
419 {
420 // we can't disconnect right away, as that would deadlock (cause
421 // cancel_message doesn't return before invoking the callback)
422 Idle.add (perform_search.callback);
423 yield;
424 cancellable.disconnect (cancel_id);
425 throw new IOError.CANCELLED ("Cancelled");
426 }
427
428 if (cancellable != null)
429 {
430 // clean up
431 cancellable.disconnect (cancel_id);
432 }
433
434 var results = handle_search_response (msg, is_treat_yourself);
435 if (results != null)
436 {
437 if (search_string == null || search_string.strip () == "" && active_sources.size == 0)
438 {
439 debug ("Empty search, updating recommendations");
440 var time = new DateTime.now_utc ();
441 recommendations = results;
442 recommendations_last_update = time.to_unix ();
443 }
444 update_results_model (search.results_model, results);
445 }
446 }
447
448 private void update_results_model (Dee.Model model, Gee.ArrayList<RemoteVideoFile?> results)
449 {
450 foreach (var video in results)
451 {
452 if (video.uri.has_prefix ("http"))
453 {
454 var fake_uri = new RemoteUri (video.uri, video.title, video.icon, video.details_uri);
455 var result_icon = video.icon;
456
457 if (video.category == CAT_INDEX_MORE && video.price != null && video.price != "")
458 {
459 var anno_icon = new Unity.AnnotatedIcon (new FileIcon (File.new_for_uri (result_icon)));
460 anno_icon.category = Unity.CategoryType.MOVIE;
461 anno_icon.ribbon = video.price;
462 result_icon = anno_icon.to_string ();
463 }
464
465 model.append (fake_uri.to_rawuri (), result_icon, video.category, "text/html", video.title, video.comment, video.uri);
466 }
467 }
468 }
469
470 private void zeitgeist_init () throws Error
471 {
472 zg_sources = new Zeitgeist.DataSourceRegistry ();
473 var templates = new PtrArray.sized(1);
474 var ev = new Zeitgeist.Event.full (Zeitgeist.ZG_ACCESS_EVENT, Zeitgeist.ZG_USER_ACTIVITY, "lens://unity-lens-video");
475 templates.add ((ev as GLib.Object).ref());
476 var data_source = new Zeitgeist.DataSource.full ("98898", "Unity Video Lens", "", templates);
477 zg_sources.register_data_source (data_source, null);
478 }
479
480 private void zeitgeist_insert_event (string uri, string title, string icon)
481 {
482 var subject = new Zeitgeist.Subject.full (uri, Zeitgeist.NFO_VIDEO, Zeitgeist.NFO_REMOTE_DATA_OBJECT, "", uri, title, icon);
483 var event = new Zeitgeist.Event.full (Zeitgeist.ZG_ACCESS_EVENT, Zeitgeist.ZG_USER_ACTIVITY, "lens://unity-lens-video");
484 event.add_subject (subject);
485
486 var ev_array = new PtrArray.sized(1);
487 ev_array.add ((event as GLib.Object).ref ());
488 Zeitgeist.Log.get_default ().insert_events_from_ptrarray (ev_array, null);
489 }
490 }
491}
0492
=== added file 'src/remote-uri.vala'
--- src/remote-uri.vala 1970-01-01 00:00:00 +0000
+++ src/remote-uri.vala 2013-02-19 17:19:01 +0000
@@ -0,0 +1,52 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
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 version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
17 */
18
19namespace Unity.VideoLens
20{
21 public class RemoteUri
22 {
23 public string uri { get; set; }
24 public string title { get; set; }
25 public string icon { get; set; }
26 public string details_uri { get; set; }
27
28 public RemoteUri (string uri, string title, string icon, string details_uri)
29 {
30 this.uri = uri;
31 this.title = title;
32 this.icon = icon;
33 this.details_uri = details_uri;
34 }
35
36 public static RemoteUri? from_rawuri (string raw_uri)
37 {
38 RemoteUri uri = null;
39 string[] args = raw_uri.split ("lens-meta://", 4);
40 if (args.length == 4)
41 {
42 uri = new RemoteUri (args[0], args[1], args[2], args[3]);
43 }
44 return uri;
45 }
46
47 public string to_rawuri ()
48 {
49 return string.join ("lens-meta://", uri, title, icon, details_uri);
50 }
51 }
52}
0\ No newline at end of file53\ No newline at end of file
154
=== added file 'src/remote-video-main.vala'
--- src/remote-video-main.vala 1970-01-01 00:00:00 +0000
+++ src/remote-video-main.vala 2013-02-19 17:19:01 +0000
@@ -0,0 +1,60 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
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 version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17using GLib;
18using Config;
19
20namespace Unity.VideoLens {
21
22 static const string BUS_NAME = "net.launchpad.scope.RemoteVideos";
23 Unity.Scope scope;
24 static Application? app = null;
25
26 public static int main (string[] args)
27 {
28 GLib.Environment.set_prgname ("unity-remote-video-scope");
29
30 /* Sort up locale to get translations but also sorting and
31 * punctuation right */
32 GLib.Intl.textdomain (Config.PACKAGE);
33 GLib.Intl.bindtextdomain (Config.PACKAGE, Config.LOCALEDIR);
34 GLib.Intl.bind_textdomain_codeset (Config.PACKAGE, "UTF-8");
35 GLib.Intl.setlocale(GLib.LocaleCategory.ALL, "");
36
37 try
38 {
39 app = Extras.dbus_own_name (BUS_NAME, () =>
40 {
41 scope = new RemoteVideoScope ();
42 });
43 }
44 catch (Error e)
45 {
46 warning ("Failed to start video lens daemon: %s\n", e.message);
47 return 1;
48 }
49
50 if (app == null)
51 {
52 warning ("Another instance of the Unity Videos Lens " +
53 "already appears to be running.\nBailing out.\n");
54 return 2;
55 }
56
57 return app.run ();
58 }
59
60} /* namespace */
061
=== added file 'src/scope.vala'
--- src/scope.vala 1970-01-01 00:00:00 +0000
+++ src/scope.vala 2013-02-19 17:19:01 +0000
@@ -0,0 +1,427 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
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 version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
17 * based on python code by David Calle <davidc@framli.eu>
18 *
19 */
20
21namespace Unity.VideoLens
22{
23 public class VideoScope : Unity.Scope
24 {
25 private static const int MAX_ZG_EVENTS = 24;
26 private static const int CAT_INDEX_MY_VIDEOS = 0;
27 private static const int CAT_INDEX_ONLINE = 1;
28 private static const int CAT_INDEX_MORE = 2;
29 private static const int REFRESH_TIMEOUT = 30;
30
31 private static string cache_directory;
32
33 private string videos_folder;
34 private Unity.Extras.PreviewPlayer preview_player;
35 private Thumbnailer thumbnailer;
36 private Locate locate;
37 private BlacklistTracker blacklist_tracker;
38
39 public VideoScope ()
40 {
41 Object (dbus_path: "/net/launchpad/lens/video/main");
42
43 videos_folder = GLib.Environment.get_user_special_dir (GLib.UserDirectory.VIDEOS);
44 cache_directory = GLib.Environment.get_user_cache_dir () + "/unity-lens-video";
45
46 // create cache directory
47 try
48 {
49 var cache_dir = GLib.File.new_for_path (cache_directory);
50 if (! cache_dir.query_exists (null))
51 cache_dir.make_directory (null);
52 }
53 catch (Error e)
54 {
55 error ("Failed to create cache directory: %s", e.message);
56 }
57
58 blacklist_tracker = new BlacklistTracker ();
59 thumbnailer = new Thumbnailer (cache_directory);
60 locate = new Locate (cache_directory, videos_folder);
61
62 search_in_global = true;
63 sources.add_option ("local", _("My Videos"), null);
64 provides_personal_content = true;
65
66 GLib.Timeout.add_seconds (REFRESH_TIMEOUT, refresh_results);
67
68 search_changed.connect ((search, search_type, cancellable) =>
69 {
70 dispatch_search (search, search_type, cancellable);
71 });
72
73 filters_changed.connect (on_filters_changed);
74 sources.notify["filtering"].connect (on_filters_changed);
75 preview_uri.connect ((uri) =>
76 {
77 return generate_preview_for_uri (uri);
78 });
79 }
80
81 private bool refresh_results ()
82 {
83 debug ("Queuing new search because of timeout");
84 queue_search_changed (Unity.SearchType.DEFAULT);
85 return true;
86 }
87
88 private void on_filters_changed ()
89 {
90 queue_search_changed (Unity.SearchType.DEFAULT);
91 }
92
93 private async void dispatch_search (LensSearch search, SearchType search_type, Cancellable cancellable)
94 {
95 var search_string = search.search_string.strip ();
96 var search_status = search;
97 var model = search.results_model;
98 debug ("Search changed to '%s'", search_string);
99
100 if (source_activated ("local"))
101 {
102 if (search_type == Unity.SearchType.GLOBAL)
103 {
104 if (search_string == "")
105 {
106 model.clear ();
107 if (search != null)
108 search_status.finished ();
109 debug ("Global view without search string : hide");
110 }
111 else
112 {
113 update_results_model (search_string, model, "global", cancellable, search_status);
114 }
115 }
116 else
117 {
118 if (search_string == null || search_string == "")
119 {
120 try
121 {
122 zg_call (cancellable, search_status);
123 }
124 catch (GLib.Error e)
125 {
126 warning ("Failed to call zeitgeist: %s", e.message);
127 }
128 }
129 else
130 {
131 update_results_model (search_string, model, "lens", cancellable, search_status);
132 }
133 }
134 }
135 }
136
137 private void update_results_model (string search_string, Dee.Model model, string cat, Cancellable? cancellable, LensSearch search, bool clear_model = true)
138 {
139 var home_folder = GLib.Environment.get_home_dir ();
140
141 if (videos_folder != home_folder)
142 {
143 locate.updatedb ();
144 }
145
146 var result_list = locate.run_locate (search_string, thumbnailer, video_filter);
147 if (result_list != null)
148 {
149 GLib.Idle.add (() =>
150 {
151 result_list.sort ((GLib.CompareFunc?)sort_alpha);
152 add_results (search, model, cat, cancellable, result_list, search_string, clear_model);
153 return false;
154 });
155 }
156 }
157
158 internal void add_results (LensSearch search_status, Dee.Model model, string cat, Cancellable? cancellable, Gee.ArrayList<VideoFile?> result_list, string search, bool clear_model)
159 {
160 if (cancellable != null && !cancellable.is_cancelled ())
161 {
162 if (clear_model)
163 search_status.results_model.clear ();
164
165 foreach (var video in result_list)
166 {
167 results_model.append (video.uri, video.icon, video.category, "text/html", video.title, video.comment, video.uri);
168 }
169
170 if (search_status != null)
171 {
172 debug ("Search finished");
173 search_status.finished ();
174 }
175 }
176 }
177
178 internal static int sort_alpha (VideoFile a, VideoFile b)
179 {
180 return a.lc_title.collate (b.lc_title);
181 }
182
183 private async void zg_call (Cancellable? cancellable, LensSearch search_status) throws Error
184 {
185 bool active = sources.get_option ("local").active;
186 bool filtering = sources.filtering;
187 string uri = active && filtering ? "file:*" : "*";
188
189 var time_range = new Zeitgeist.TimeRange.to_now ();
190 var event_template = new Zeitgeist.Event ();
191 var subject = new Zeitgeist.Subject.full (uri, Zeitgeist.NFO_VIDEO, "", "", "", "", "");
192 event_template.add_subject (subject);
193
194 var templates = new PtrArray.sized (1);
195 templates.add ((event_template as GLib.Object).ref());
196 var results = yield Zeitgeist.Log.get_default ().find_events (time_range, templates, Zeitgeist.StorageState.ANY, MAX_ZG_EVENTS, Zeitgeist.ResultType.MOST_RECENT_SUBJECTS, cancellable);
197 process_zg_events (results, cancellable, search_status);
198 }
199
200 internal bool video_filter (string path)
201 {
202 try
203 {
204 if (Utils.is_video (path) && !Utils.is_hidden (path))
205 {
206 var file = File.new_for_path (path);
207 if (!is_blacklisted(file.get_uri ()))
208 return true;
209 }
210 }
211 catch (Error e)
212 {
213 warning ("Failed to get properties of '%s': %s", path, e.message);
214 }
215 return false;
216 }
217
218 internal void process_zg_events (Zeitgeist.ResultSet events, Cancellable cancellable, LensSearch search_status)
219 {
220 var result_list = new Gee.ArrayList<VideoFile?> ();
221
222 foreach (var event in events)
223 {
224 if (cancellable.is_cancelled ())
225 return;
226
227 var event_uri = event.get_subject (0).get_uri ();
228 if (event_uri.has_prefix ("file://"))
229 {
230 try
231 {
232 // If the file is local, we use the same methods
233 // as other result items.
234 var file = GLib.File.new_for_uri (event_uri);
235 var path = file.get_path ();
236 if (video_filter (path))
237 {
238 var finfo = file.query_info (GLib.FileAttribute.STANDARD_DISPLAY_NAME, GLib.FileQueryInfoFlags.NONE, null);
239 VideoFile video = VideoFile ()
240 {
241 title = finfo.get_display_name (),
242 lc_title = finfo.get_display_name ().down (),
243 comment = "",
244 uri = event_uri,
245 icon = thumbnailer.get_icon (path),
246 category = CAT_INDEX_MY_VIDEOS
247 };
248 result_list.add (video);
249 }
250 }
251 catch (GLib.Error e)
252 {
253 warning ("Failed to access file '%s': %s", event_uri, e.message);
254 }
255 }
256 else if (event_uri.has_prefix ("http"))
257 {
258 // If the file is distant, we take
259 // all we need from Zeitgeist
260 // this one can be any unicode string:
261 VideoFile video = VideoFile ()
262 {
263 title = event.get_subject (0).get_text (),
264 comment = "",
265 uri = event_uri,
266 icon = event.get_subject (0).get_storage (),
267 category = CAT_INDEX_ONLINE
268 };
269 result_list.add (video);
270 }
271 }
272
273 search_status.results_model.clear ();
274 foreach (var video in result_list)
275 {
276 results_model.append (video.uri, video.icon, video.category, "text/html", video.title, video.comment, video.uri);
277 }
278
279 update_results_model ("", search_status.results_model, "lens", cancellable, search_status, false);
280 }
281
282 private Unity.ActivationResponse show_in_folder (string uri)
283 {
284 try
285 {
286 Unity.Extras.show_in_folder (uri);
287 return new Unity.ActivationResponse (Unity.HandledType.HIDE_DASH);
288 }
289 catch (GLib.Error e)
290 {
291 warning ("Failed to show in folder '%s': %s", uri, e.message);
292 }
293 return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED);
294 }
295
296 private Preview? generate_preview_for_uri (string uri)
297 {
298 debug ("Preview uri: %s", uri);
299
300 Unity.Preview preview = null;
301 var model = results_model;
302 var iter = model.get_first_iter ();
303 while (!model.is_last (iter))
304 {
305 if (model.get_string (iter, 0) == uri)
306 {
307 var title = model.get_string (iter, 4);
308 string subtitle = "";
309 int64 file_size = 0;
310 bool local_video = uri.has_prefix ("file://");
311
312 if (local_video)
313 {
314 var file = GLib.File.new_for_uri (uri);
315 try
316 {
317 var finfo = file.query_info (GLib.FileAttribute.TIME_MODIFIED + "," + GLib.FileAttribute.STANDARD_SIZE, GLib.FileQueryInfoFlags.NONE, null);
318 file_size = finfo.get_size ();
319 var tval = finfo.get_modification_time ();
320 var dt = new DateTime.from_timeval_local (tval);
321 subtitle = dt.format ("%x, %X");
322 }
323 catch (Error e)
324 {
325 // empty subtitle
326 }
327 }
328 var desc = model.get_string (iter, 5);
329
330 preview = new Unity.MoviePreview (title, subtitle, desc, null);
331 preview.closed.connect (on_preview_closed);
332 var play_video = new Unity.PreviewAction ("play", _("Play"), null);
333 preview.add_action (play_video);
334 preview.image_source_uri = model.get_string (iter, 1);
335
336 if (local_video)
337 {
338 var show_folder = new Unity.PreviewAction ("show-in-folder", _("Show in Folder"), null);
339 show_folder.activated.connect (show_in_folder);
340 preview.add_action (show_folder);
341 }
342
343 // we may get remote uris from zeitgeist - fetch details for local files only
344 if (local_video)
345 {
346 var async_preview = new Unity.AsyncPreview ();
347 preview.image_source_uri = uri;
348
349 if (preview_player == null)
350 preview_player = new Unity.Extras.PreviewPlayer ();
351
352 preview_player.video_properties.begin (uri, (obj, res) =>
353 {
354 try
355 {
356 var props = preview_player.video_properties.end (res);
357 if ("width" in props && "height" in props && "codec" in props)
358 {
359 var width = props["width"].get_uint32 ();
360 var height = props["height"].get_uint32 ();
361 var codec = props["codec"].get_string ();
362 string dimensions = "%u*%u".printf (width, height);
363 if (width > 0 && height > 0)
364 {
365 var gcd = Utils.gcd (width, height);
366 dimensions += ", %u:%u".printf (width / gcd, height / gcd);
367 }
368
369 preview.add_info (new Unity.InfoHint ("format", _("Format"), null, codec));
370 preview.add_info (new Unity.InfoHint ("dimensions", _("Dimensions"), null, dimensions));
371 preview.add_info (new Unity.InfoHint ("size", _("Size"), null, GLib.format_size (file_size)));
372 }
373 }
374 catch (Error e)
375 {
376 warning ("Couldn't get video details: %s", e.message);
377 }
378 async_preview.preview_ready (preview);
379 });
380 return async_preview;
381 }
382 break;
383 }
384 iter = model.next (iter);
385 }
386
387 if (preview == null)
388 warning ("Couldn't find model row for requested preview uri: %s", uri);
389
390 return preview;
391 }
392
393 private bool is_blacklisted (string uri)
394 {
395 foreach (var blacklisted_uri in blacklist_tracker.get_blacklisted_uris ())
396 {
397 if (uri.has_prefix (blacklisted_uri))
398 return true;
399 }
400 return false;
401 }
402
403 private void on_preview_closed (Unity.Preview preview)
404 {
405 if (preview_player != null)
406 {
407 try
408 {
409 preview_player.close ();
410 }
411 catch (Error e)
412 {
413 warning ("Failed to close preview player: %s", e.message);
414 }
415 }
416 }
417
418 private bool source_activated (string source)
419 {
420 // Return the state of a sources filter option
421 var active = sources.get_option (source).active;
422 var filtering = sources.filtering;
423 return (active && filtering) || (!active && !filtering);
424 }
425
426 }
427}
0428
=== added file 'src/thumbnailer.vala'
--- src/thumbnailer.vala 1970-01-01 00:00:00 +0000
+++ src/thumbnailer.vala 2013-02-19 17:19:01 +0000
@@ -0,0 +1,115 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
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 version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
17 * based on python code by David Calle <davidc@framli.eu>
18 *
19 */
20
21namespace Unity.VideoLens
22{
23 public class Thumbnailer
24 {
25 private static const int MAX_THUMBNAILERS = 3;
26
27 private string videos_folder;
28 private string cache_directory;
29 private Gee.LinkedList<GLib.Pid?> thumbnailer_pids;
30
31 public Thumbnailer (string cache_dir)
32 {
33 cache_directory = cache_dir;
34 videos_folder = GLib.Environment.get_user_special_dir (GLib.UserDirectory.VIDEOS);
35 thumbnailer_pids = new Gee.LinkedList<GLib.Pid?> ();
36 }
37
38 public string get_icon (string video_file)
39 {
40 /* This method checks several locations for a video thumbnail.
41
42 1) <filename>.jpg file in the same folder (not activated for now)
43 2) Nautilus thumbnails
44 3) Cached thumbnails generated by the scope
45
46 If nothing has been found, it tries to generate a thumbnail with Totem,
47 stores and uses it.
48 If the generation fails or is slow, it fallbacks to the standard video
49 icon.
50 */
51 var file = GLib.File.new_for_path (video_file);
52 var video_path = file.get_path ();
53
54 string? icon_path = null;
55
56 // check if nautilus thumbnail is available
57 try
58 {
59 var finfo = file.query_info (GLib.FileAttribute.THUMBNAIL_PATH + "," + GLib.FileAttribute.THUMBNAILING_FAILED, GLib.FileQueryInfoFlags.NONE);
60 if (finfo.get_attribute_boolean (GLib.FileAttribute.THUMBNAILING_FAILED))
61 return "video";
62 else
63 icon_path = finfo.get_attribute_as_string (GLib.FileAttribute.THUMBNAIL_PATH);
64 }
65 catch (GLib.Error e)
66 {
67 // silently ignore
68 }
69
70 if (icon_path == null || icon_path == "")
71 {
72 string check_path = cache_directory + "/" + video_path.replace (videos_folder, "").replace ("/", "_");
73
74 // check for thumbnail generated by this scope
75 if (Utils.is_regular_file (check_path))
76 return check_path;
77
78 create_thumbnail (video_path, check_path);
79
80 // FIXME: this is not reliable, since we spawn thumbnailer async
81 if (Utils.is_regular_file (check_path))
82 return check_path;
83 }
84
85 if (icon_path == null || icon_path == "")
86 icon_path = "video";
87
88 return icon_path;
89 }
90
91 private void thumbnailer_process_watch (Pid pid, int status)
92 {
93 thumbnailer_pids.remove (pid);
94 }
95
96 public void create_thumbnail (string video_path, string thumbnail_path)
97 {
98 if (thumbnailer_pids.size < MAX_THUMBNAILERS)
99 {
100 GLib.Pid pid;
101 string[] args = {"/usr/bin/totem-video-thumbnailer", video_path, thumbnail_path};
102 try
103 {
104 GLib.Process.spawn_async (null, args, null, GLib.SpawnFlags.DO_NOT_REAP_CHILD, null, out pid);
105 GLib.ChildWatch.add (pid, thumbnailer_process_watch);
106 thumbnailer_pids.add (pid);
107 }
108 catch (Error e)
109 {
110 warning ("Failed to spawn thumbailer: %s", e.message);
111 }
112 }
113 }
114 }
115}
0\ No newline at end of file116\ No newline at end of file
1117
=== added file 'src/ubuntu-video-search.vala'
--- src/ubuntu-video-search.vala 1970-01-01 00:00:00 +0000
+++ src/ubuntu-video-search.vala 2013-02-19 17:19:01 +0000
@@ -0,0 +1,229 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
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 version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
17 *
18 */
19
20namespace Unity.VideoLens.UbuntuVideoSearch
21{
22 private static const string SERVER = "http://videosearch.ubuntu.com/v0";
23
24 public static string sources_uri ()
25 {
26 return SERVER + "/sources";
27 }
28
29 public static string recommendations_uri ()
30 {
31 return SERVER + "/search?q=&sources=Amazon";
32 }
33
34 public static string build_search_uri (string query, Gee.ArrayList<string>? sources)
35 {
36 var uri = new StringBuilder ();
37 uri.append (SERVER);
38 uri.append ("/search?q=");
39 uri.append (Uri.escape_string (query, "", false));
40 uri.append ("&split=true");
41 if (sources != null && sources.size>0)
42 {
43 uri.append ("&sources=");
44 uri.append (sources[0]);
45 for (int i=1; i<sources.size; i++)
46 {
47 uri.append (",");
48 uri.append (sources[i]);
49 }
50 }
51 return uri.str;
52 }
53
54 /**
55 * Parses JSON data returned by search requests, returns list of videos.
56 */
57 public Gee.ArrayList<RemoteVideoFile?>? process_search_results (string json_data, bool is_treat_yourself) throws Error
58 {
59 var parser = new Json.Parser ();
60
61 parser.load_from_data (json_data);
62 var root = parser.get_root ();
63
64 if (root.get_node_type () == Json.NodeType.OBJECT)
65 {
66 var records = root.get_object ().get_array_member ("other");
67 var results = process_results_array (records, CAT_INDEX_ONLINE);
68 records = root.get_object ().get_array_member ("treats");
69 results.add_all (process_results_array (records, CAT_INDEX_MORE));
70 return results;
71 }
72 else
73 {
74 var records = root.get_array ();
75 var results = process_results_array (records, is_treat_yourself ? CAT_INDEX_MORE : CAT_INDEX_ONLINE);
76 return results;
77 }
78 }
79
80 /**
81 * Joins elements of JSON array of strings.
82 */
83 internal string join_array (Json.Array array, string separator)
84 {
85 var res = new StringBuilder ();
86 var len = array.get_length ();
87 array.foreach_element ((a, index, node) =>
88 {
89 res.append (node.get_string ());
90 if (index < len - 1)
91 res.append (separator);
92 });
93 return res.str;
94 }
95
96 public string[] json_array_to_str_array (Json.Array array)
97 {
98 string [] res = {};
99 array.foreach_element ((a, index, node) =>
100 {
101 res += node.get_string ();
102 });
103 return res;
104 }
105
106 public RemoteVideoDetails process_details_results (string json_data) throws Error
107 {
108 var parser = new Json.Parser ();
109 parser.load_from_data (json_data);
110 var details = parser.get_root ().get_object ();
111
112 var video = RemoteVideoDetails ();
113 video.title = details.get_string_member ("title");
114 video.image = details.get_string_member ("image");
115 video.description = details.get_string_member ("description");
116 video.source = details.get_string_member ("source");
117
118 if (details.has_member ("release_date"))
119 {
120 // v1 spec states release_date will be YYYY-MM-DD
121 var release_date = GLib.Time ();
122 if (release_date.strptime (details.get_string_member ("release_date"), "%Y-%m-%d") != null)
123 {
124 video.release_date = release_date.format ("%Y");
125 }
126 else
127 {
128 warning ("Failed to parse release_date: '%s'", details.get_string_member ("release_date"));
129 }
130 }
131
132 video.price = details.has_member ("formatted_price") ? details.get_string_member ("formatted_price") : "";
133
134 video.duration = 0;
135 if (details.has_member ("duration"))
136 video.duration = int.parse (details.get_string_member ("duration")) / 60;
137
138 video.directors = {};
139 if (details.has_member ("directors"))
140 {
141 var directors = details.get_array_member ("directors");
142 video.directors = json_array_to_str_array (directors);
143 }
144
145 if (details.has_member ("starring"))
146 {
147 var starring = details.get_array_member ("starring");
148 video.starring = join_array (starring, ", ");
149 }
150
151 video.genres = {};
152 if (details.has_member ("genres"))
153 {
154 var genres = details.get_array_member ("genres");
155 video.genres = json_array_to_str_array (genres);
156 }
157
158 if (details.has_member ("uploaded_by"))
159 video.uploaded_by = details.get_string_member ("uploaded_by");
160
161 if (details.has_member ("date_uploaded"))
162 video.date_uploaded = details.get_string_member ("date_uploaded");
163
164 return video;
165 }
166
167 /**
168 * Parses JSON data returned by 'sources' query, returns list of sources (e.g. "Amazon", "Youtube" etc.)
169 */
170 public Gee.ArrayList<string>? process_sources_results (string json_data) throws Error
171 {
172 var parser = new Json.Parser ();
173
174 parser.load_from_data (json_data);
175 var sources_array = parser.get_root ().get_array ();
176 var results = new Gee.ArrayList<string> (null);
177
178 sources_array.foreach_element ((array, index, node) =>
179 {
180 results.add (node.get_string ());
181 });
182
183 return results;
184 }
185
186 /**
187 * Converts JSON results array to list of RemoteVideoFile records.
188 */
189 internal Gee.ArrayList<RemoteVideoFile?> process_results_array (Json.Array results, int category)
190 {
191 var videos = new Gee.ArrayList<RemoteVideoFile?> ();
192
193 results.foreach_element ((array, index, node) =>
194 {
195 try
196 {
197 var video = RemoteVideoFile ();
198 var rec = node.get_object ();
199 video.uri = rec.get_string_member ("url");
200 video.title = rec.get_string_member ("title");
201 video.icon = rec.get_string_member ("img");
202 video.comment = rec.get_string_member ("source");
203 video.details_uri = (rec.has_member ("details") ? rec.get_string_member ("details") : "");
204 video.category = category;
205 video.price = null;
206
207 if (category == CAT_INDEX_MORE)
208 {
209 string price = null;
210 if (rec.has_member ("formatted_price"))
211 {
212 price = rec.get_string_member ("formatted_price");
213 }
214 if (price == null || price == "free")
215 {
216 price = _("Free");
217 }
218 video.price = price;
219 }
220 videos.add (video);
221 }
222 catch (Error e)
223 {
224 warning ("Malformed result, skipping: %s", e.message);
225 }
226 });
227 return videos;
228 }
229}
0\ No newline at end of file230\ No newline at end of file
1231
=== removed file 'src/unity-lens-video'
--- src/unity-lens-video 2012-10-18 15:05:16 +0000
+++ src/unity-lens-video 1970-01-01 00:00:00 +0000
@@ -1,543 +0,0 @@
1#! /usr/bin/python
2# -*- coding: utf-8 -*-
3
4# Copyright (c) 2011 David Calle <davidc@framli.eu>
5
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15
16# You should have received a copy of the GNU General Public License
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19"""The unity video lens. Indexing videos in ~/Videos and generating thumbnails if needed."""
20
21import gettext
22import locale
23import os
24import sys
25from zeitgeist.client import ZeitgeistClient
26from zeitgeist import client, datamodel
27import time
28import fractions
29import dbus
30
31#pylint: disable=E0611
32from gi.repository import (
33 GLib,
34 GObject,
35 Gio,
36 Unity,
37 Dee
38)
39#pylint: enable=E0611
40
41APP_NAME = "unity-lens-video"
42LOCAL_PATH = "/usr/share/locale/"
43
44gettext.bindtextdomain(APP_NAME, LOCAL_PATH)
45gettext.textdomain(APP_NAME)
46_ = gettext.gettext
47
48# Translatable strings
49CAT_ONCOMPUTER = _("My Videos")
50CAT_ONLINE = _("Online")
51CAT_GLOBAL = _("Videos")
52CAT_RECENT = _("Recently Viewed")
53CAT_MORE_SUGGESTIONS = _("More suggestions")
54HINT = _("Search Videos")
55SOURCES = _("Sources")
56LOCAL_VIDEOS = _("My Videos")
57
58CAT_INDEX_MY_VIDEOS = 0
59CAT_INDEX_ONLINE = 1
60CAT_INDEX_MORE = 2
61
62BUS_NAME = "net.launchpad.lens.video"
63FOLDER = GLib.get_user_special_dir(GLib.USER_DIRECTORY_VIDEOS)
64HOME_FOLDER = GLib.get_home_dir()
65CACHE = "%s/unity-lens-video" % GLib.get_user_cache_dir()
66DB = "videos.db"
67Q = []
68Q_MAX = 3
69try:
70 ZG = ZeitgeistClient()
71except:
72 raise SystemExit(1)
73
74REFRESH_TIMEOUT = 300
75PREVIEW_PLAYER_DBUS_NAME = "com.canonical.Unity.Lens.Music.PreviewPlayer"
76PREVIEW_PLAYER_DBUS_PATH = "/com/canonical/Unity/Lens/Music/PreviewPlayer"
77PREVIEW_PLAYER_DBUS_IFACE = PREVIEW_PLAYER_DBUS_NAME
78
79# pylint: disable=R0903
80class Daemon:
81
82 """Creation of a lens with a local scope."""
83
84 def __init__(self):
85 #Create the lens
86 self._lens = Unity.Lens.new("/net/launchpad/lens/video", "video")
87 self._lens.props.search_hint = HINT
88 self._lens.props.visible = True
89 self._lens.props.search_in_global = True
90 self._lens.props.sources_display_name = SOURCES
91
92 svg_dir = "/usr/share/icons/unity-icon-theme/places/svg/"
93 cats = []
94 cats.append(Unity.Category.new(CAT_ONCOMPUTER,
95 Gio.ThemedIcon.new(svg_dir + "group-videos.svg"),
96 Unity.CategoryRenderer.VERTICAL_TILE))
97 cats.append(Unity.Category.new(CAT_ONLINE,
98 Gio.ThemedIcon.new(svg_dir + "group-internet.svg"),
99 Unity.CategoryRenderer.VERTICAL_TILE))
100 cats.append(Unity.Category.new(CAT_MORE_SUGGESTIONS,
101 Gio.ThemedIcon.new(svg_dir + "group-treat-yourself.svg"),
102 Unity.CategoryRenderer.VERTICAL_TILE))
103 self._lens.props.categories = cats
104
105 filters = []
106 self._lens.props.filters = filters
107
108 self.bus = dbus.SessionBus()
109
110 # Create the scope
111 self._scope = Unity.Scope.new("/net/launchpad/lens/video/main")
112 self._scope.search_in_global = True
113 self._scope.props.sources.add_option('local', LOCAL_VIDEOS, None)
114 self._scope.props.provides_personal_content=True
115 self._scope.connect("search-changed", self.on_search_changed)
116 self._scope.connect("filters-changed",self.on_filtering_changed)
117 self._scope.props.sources.connect("notify::filtering",
118 self.on_filtering_changed)
119 self._scope.connect('preview-uri', self.on_preview_uri)
120 self._lens.add_local_scope(self._scope)
121 self._lens.export()
122
123 GLib.timeout_add_seconds(REFRESH_TIMEOUT, self.refresh_results)
124 self._scope.queue_search_changed(Unity.SearchType.DEFAULT)
125
126 def refresh_results(self, *_):
127 """Update the results on a timeout."""
128 print "Queuing new search because of timeout"
129 self._scope.queue_search_changed(Unity.SearchType.DEFAULT)
130 return True
131
132 def on_filtering_changed(self, *_):
133 """Run another search when a filter change is notified."""
134 self._scope.queue_search_changed(Unity.SearchType.DEFAULT)
135
136 def source_activated(self, source):
137 """Return the state of a sources filter option."""
138 active = self._scope.props.sources.get_option(source).props.active
139 filtering = self._scope.props.sources.props.filtering
140 if (active and filtering) or (not active and not filtering):
141 return True
142 else:
143 return False
144
145 def on_preview_closed(self, preview):
146 try:
147 player = self.bus.get_object (PREVIEW_PLAYER_DBUS_NAME, PREVIEW_PLAYER_DBUS_PATH)
148 dbus.Interface (player, PREVIEW_PLAYER_DBUS_IFACE).Close()
149 except Exception as e:
150 print "Failed to send close signal to preview player:", e
151
152 def on_preview_uri(self, scope, uri):
153 """Preview request handler"""
154 preview = None
155 model = self._scope.props.results_model
156 iter = model.get_first_iter()
157 while not model.is_last(iter):
158 if model.get_string(iter, 0) == uri:
159 title = model.get_string(iter, 4);
160 try:
161 subtitle = time.strftime("%x, %X", time.localtime(os.path.getmtime(GLib.filename_from_uri(uri, None))))
162 except:
163 # Instead of empty, maybe the date/time of the zg event?
164 subtitle = ''
165 desc = model.get_string(iter, 5);
166 preview = Unity.MoviePreview.new(title, subtitle, desc, None)
167 preview.connect('closed', self.on_preview_closed)
168
169 # we may get remote uris from zeitgeist - fetch details for local files only
170 if uri.startswith("file://"):
171 local_video = True
172 preview.props.image_source_uri = uri
173 try:
174 player = self.bus.get_object (PREVIEW_PLAYER_DBUS_NAME, PREVIEW_PLAYER_DBUS_PATH)
175 props = dbus.Interface (player, PREVIEW_PLAYER_DBUS_IFACE).VideoProperties(uri)
176 width = props['width']
177 height = props['height']
178 codec = props['codec']
179 dimensions = str(width) + "*" + str(height)
180 if width > 0 and height > 0:
181 gcd = fractions.gcd(width, height)
182 dimensions += ", " + str(width / gcd) + ":" + str(height / gcd)
183 preview.add_info(Unity.InfoHint.new("format", _("Format"), None, codec))
184 preview.add_info(Unity.InfoHint.new("dimensions", _("Dimensions"), None, dimensions))
185 preview.add_info(Unity.InfoHint.new("size", _("Size"), None, GLib.format_size(os.path.getsize(GLib.filename_from_uri(uri, None)))))
186 except Exception as e:
187 print "Couldn't get video details", e
188 else:
189 local_video = False
190 preview.props.image_source_uri = model.get_string(iter, 1)
191 play_video = Unity.PreviewAction.new("play", _("Play"), None)
192 preview.add_action(play_video)
193 if local_video:
194 show_folder = Unity.PreviewAction.new("show-in-folder", _("Show in Folder"), None)
195 show_folder.connect('activated', self.show_in_folder)
196 preview.add_action(show_folder)
197 break
198 iter = model.next(iter)
199 if preview == None:
200 print "Couldn't find model row for requested preview uri:", uri
201 return preview
202
203 def show_in_folder(self, scope, uri):
204 """ Open folder that contains given video """
205 file = Gio.file_new_for_uri (uri)
206 parent = file.get_parent()
207 if parent == None:
208 parent = Gio.file_new_for_path("/")
209 try:
210 Gio.app_info_launch_default_for_uri(parent.get_uri(), None)
211 except Exception as e:
212 print "Couldn't launch default app for uri", parent.get_uri(), e
213 return Unity.ActivationResponse(handled=Unity.HandledType.NOT_HANDLED)
214 return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH)
215
216 def on_search_changed(self, scope, search, search_type, cancellable):
217 """On a new search, differentiate between lens view
218 and global search before updating the model"""
219 search_status = search
220 search_string = search.props.search_string.strip()
221 print "Search changed to \"%s\"" % search_string
222 model = search.props.results_model
223 if self.source_activated('local'):
224 if search_type is Unity.SearchType.GLOBAL:
225 if search_string == '':
226 model.clear ()
227 if search_status:
228 search_status.finished ()
229 print "Global view without search string : hide"
230
231 else:
232 self.update_results_model(search_string, model, 'global', cancellable, search_status)
233 else:
234 if not search_string:
235 # this will call update_results_model as well
236 self.zg_call(cancellable, search_status)
237 else:
238 self.update_results_model(search_string, model, 'lens', cancellable, search_status)
239 else:
240 model.clear()
241 if search_status:
242 search_status.finished ()
243
244 def update_results_model(self, search, model, cat, cancellable, search_status, clear_model=True):
245 """Check for the existence of the cache folder, create it if needed,
246 and run the search method."""
247 if not Gio.file_new_for_path(CACHE).query_exists(None):
248 Gio.file_new_for_path(CACHE).make_directory(None)
249 if FOLDER != HOME_FOLDER:
250 try:
251 GLib.spawn_async(['/usr/bin/updatedb', '-o', CACHE+'/'+DB,
252 '-l', '0', '-U', FOLDER])
253 except GLib.GError:
254 print "Can't create the database, will retry."
255
256 if self.is_file(CACHE+'/'+DB):
257 try:
258 results = GLib.spawn_sync(None,
259 ['/usr/bin/locate',
260 '-id', CACHE+'/'+DB,
261 FOLDER+'*'+search.replace (" ","*")+'*' ],
262 None, 0, None, None)
263 except GLib.GError:
264 results = None
265 else:
266 # spawn_sync returns bool, stdout, stderr, exit_status
267 if results[3] == 0:
268 results = results[1]
269 else:
270 results = None
271 else:
272 results = None
273 result_list = []
274 blacklist = self.get_blacklist ()
275 if results:
276 video_counter = 0
277 print len(results.split('\n'))
278 for video in results.split('\n'):
279 if video_counter < 100:
280 if self.is_video(video) and not self.is_hidden(video, blacklist):
281 video_counter+= 1
282 title = self.get_name(video)
283 comment = ''
284 uri = 'file://%s' % video
285 icon = self.get_icon(video)
286 if title:
287 item = []
288 item.append(title)
289 item.append(comment)
290 item.append(uri)
291 item.append(icon)
292 result_list.append(item)
293 result_list = self.sort_alpha(result_list)
294
295 GLib.idle_add(self.add_results, search_status, model, cat, cancellable, result_list, search, clear_model)
296
297
298 def add_results (self, search_status=None, model=None,
299 cat=None, cancellable=None, result_list=[], search=None,
300 clear_model=None):
301 result_sets = []
302
303 if cancellable and not cancellable.is_cancelled():
304 if cat == 'global':
305 # Create only one result set for the Global search
306 result_sets.append({'category':CAT_INDEX_MY_VIDEOS, 'results':result_list})
307 else:
308 result_sets.append({'category':CAT_INDEX_MY_VIDEOS, 'results':result_list})
309 if clear_model: model.clear()
310 for result_set in result_sets:
311 cat = result_set['category']
312 for i in result_set['results']:
313 title = str(i[0])
314 comment = str(i[1])
315 uri = str(i[2])
316 dnd_uri = str(i[2])
317 icon_hint = str(i[3])
318 model.append(uri, icon_hint, cat, "text/html",
319 title, comment, dnd_uri)
320 if search_status:
321 print "Search finished"
322 search_status.finished()
323 else:
324 print "Search cancelled"
325 return False
326
327 def is_file(self, uri):
328 """Check if the file is an actual file"""
329 g_file = Gio.file_new_for_path(uri)
330 if g_file.query_exists(None):
331 file_type = g_file.query_file_type(Gio.FileQueryInfoFlags.NONE,
332 None)
333 if file_type is Gio.FileType.REGULAR:
334 return True
335
336 def get_blacklist(self):
337 """Get zeitgeist blacklist"""
338 bt_list = []
339 try:
340 iface = client.ZeitgeistDBusInterface()
341 except:
342 print "Unable to connect to Zeitgeist, won't handle blacklists."
343 iface = None
344 if iface:
345 blacklist = iface.get_extension("Blacklist", "blacklist")
346 bt_list = blacklist.GetTemplates ()
347 return bt_list
348
349 def is_hidden(self, uri, blacklist):
350 """Check if the file is hidden"""
351 g_file = Gio.file_new_for_path(uri)
352 hidden = g_file.query_info(
353 Gio.FILE_ATTRIBUTE_STANDARD_IS_HIDDEN,
354 Gio.FileQueryInfoFlags.NONE,
355 None).get_attribute_boolean('standard::is-hidden')
356 blacklisted = False
357 for bt in blacklist:
358 bt = bt.replace('dir-/', '/').encode("utf-8")
359 if uri.startswith(bt):
360 blacklisted = True
361 if hidden or uri.find('/.') > -1 or blacklisted:
362 return True
363
364 def sort_alpha(self, results):
365 """Sort results in several ways, depending on the category"""
366 results.sort(key=lambda x: x[0].lower(), reverse=False)
367 return results
368
369 def is_video(self, uri):
370 """Check if the file is a video"""
371 if self.is_file(uri):
372 g_file = Gio.file_new_for_path(uri)
373 content_type = g_file.query_info('standard::content-type',
374 Gio.FileQueryInfoFlags.NONE,
375 None).get_content_type()
376 if 'video' in content_type:
377 return True
378
379 def get_name(self, uri):
380 """Get the display name of the file"""
381 g_file = Gio.file_new_for_path(uri)
382 name = g_file.query_info(
383 Gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
384 Gio.FileQueryInfoFlags.NONE,
385 None)
386 display_name = name.get_attribute_as_string(
387 Gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME)
388 return display_name
389
390 def get_icon(self, uri):
391 """This method checks several locations for a video thumbnail.
392
393 1) <filename>.jpg file in the same folder (not activated for now)
394 1) Nautilus thumbnails
395 2) Cached thumbnails generated by the scope
396
397 If nothing has been found, it tries to generate a thumbnail with Totem,
398 stores and uses it.
399 If the generation fails or is slow, it fallbacks to the standard video
400 icon.
401 """
402 icon_path = None
403 g_file = Gio.file_new_for_path(uri)
404 video_path = g_file.get_path()
405 thumb_name = video_path.replace(FOLDER, '').replace('/', '_')
406 icon_check = '%s/thumb_%s.png' % (CACHE, thumb_name)
407 # if not icon_path:
408 # print 'Check for local cover'
409 # local_cover = uri.replace('.%s' % uri.split('.')[-1], '.jpg')
410 # if self.is_file(local_cover):
411 # icon_path = local_cover
412 if not icon_path:
413 icon = g_file.query_info(
414 ','.join((Gio.FILE_ATTRIBUTE_THUMBNAIL_PATH,
415 Gio.FILE_ATTRIBUTE_THUMBNAILING_FAILED)),
416 Gio.FileQueryInfoFlags.NONE,
417 None)
418 if icon.get_attribute_boolean(Gio.FILE_ATTRIBUTE_THUMBNAILING_FAILED):
419 icon_path = 'video'
420 else:
421 icon_path = icon.get_attribute_as_string(
422 Gio.FILE_ATTRIBUTE_THUMBNAIL_PATH)
423 if not icon_path:
424 if self.is_file(icon_check):
425 icon_path = icon_check
426 if not icon_path:
427 # Check if processes can be removed from the thumbnailing queue
428 for process in Q:
429 if not Gio.file_new_for_path(
430 "/proc/"+str(process)).query_exists(None):
431 Q.remove(process)
432 if len(Q) < Q_MAX:
433 try:
434 p = GLib.spawn_async(['/usr/bin/totem-video-thumbnailer',
435 video_path, icon_check])
436 Q.append(p[0])
437 if self.is_file(icon_check):
438 icon_path = icon_check
439 except:
440 print "Warning : the file may have been removed."
441 if not icon_path:
442 icon_path = 'video'
443 return icon_path
444
445 def zg_call (self, cancellable, search_status):
446 active = self._scope.props.sources.get_option("local").props.active
447 filtering = self._scope.props.sources.props.filtering
448 if active and filtering:
449 uri = "file:*"
450 else:
451 uri = "*"
452 time_range = datamodel.TimeRange.until_now ()
453 max_amount_results = 24
454 event_template = datamodel.Event()
455 interpretation = datamodel.Interpretation.VIDEO
456 event_template.append_subject(
457 datamodel.Subject.new_for_values(uri=uri,interpretation=interpretation))
458 def wrap(callback):
459 def wrapped(events):
460 callback(events, cancellable, search_status)
461
462 return wrapped
463
464 ZG.find_events_for_templates(
465 [event_template, ],
466 wrap(self.progress_zg_events),
467 timerange = time_range,
468 storage_state = datamodel.StorageState.Any,
469 num_events = max_amount_results,
470 result_type = datamodel.ResultType.MostRecentSubjects
471 )
472
473 def progress_zg_events(self, events, cancellable, search_status):
474 if cancellable.is_cancelled(): return
475 blacklist = self.get_blacklist ()
476 result_list = []
477 for event in events:
478 item = []
479 uri = event.get_subjects()[0].uri
480 if uri.startswith('file://'):
481 # If the file is local, we use the same methods
482 # as other result items.
483 g_file = Gio.file_new_for_uri(uri)
484 path = g_file.get_path()
485 if self.is_video(path) and not self.is_hidden(path, blacklist):
486 item.append(self.get_name(path))
487 item.append('')
488 item.append(uri.encode("utf-8"))
489 item.append(self.get_icon(path))
490 item.append(CAT_INDEX_MY_VIDEOS)
491 result_list.append(item)
492 elif uri.startswith('http'):
493 # If the file is distant, we take
494 # all we need from Zeitgeist
495 # this one can be any unicode string:
496 item.append(event.get_subjects()[0].text.encode("utf-8"))
497 item.append('')
498 # these two *should* be ascii, but it can't hurt to be safe
499 item.append(event.get_subjects()[0].uri.encode("utf-8"))
500 item.append(event.get_subjects()[0].storage.encode("utf-8"))
501 item.append(CAT_INDEX_ONLINE)
502 result_list.append(item)
503 search_status.props.results_model.clear ()
504 for i in result_list:
505 title = str(i[0])
506 comment = str(i[1])
507 uri = str(i[2])
508 dnd_uri = str(i[2])
509 icon_hint = str(i[3])
510 category = i[4]
511 self._scope.props.results_model.append(uri, icon_hint,
512 category, "text/html", title, comment, dnd_uri)
513
514 self.update_results_model("", search_status.props.results_model, 'lens', cancellable, search_status, False)
515
516# pylint: enable=R0903
517
518def main():
519 """Connect to the session bus, exit if there is a running instance."""
520 try:
521 session_bus_connection = Gio.bus_get_sync(Gio.BusType.SESSION, None)
522 session_bus = Gio.DBusProxy.new_sync(session_bus_connection, 0, None,
523 'org.freedesktop.DBus',
524 '/org/freedesktop/DBus',
525 'org.freedesktop.DBus', None)
526 result = session_bus.call_sync('RequestName',
527 GLib.Variant("(su)", (BUS_NAME, 0x4)),
528 0, -1, None)
529
530 # Unpack variant response with signature "(u)". 1 means we got it.
531 result = result.unpack()[0]
532
533 if result != 1:
534 print >> sys.stderr, "Failed to own name %s. Bailing out." % BUS_NAME
535 raise SystemExit(1)
536 except:
537 raise SystemExit(1)
538
539 daemon = Daemon()
540 GObject.MainLoop().run()
541
542if __name__ == "__main__":
543 main()
5440
=== added file 'src/utils.vala'
--- src/utils.vala 1970-01-01 00:00:00 +0000
+++ src/utils.vala 2013-02-19 17:19:01 +0000
@@ -0,0 +1,77 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
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 version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
17 * based on python code by David Calle <davidc@framli.eu>
18 */
19
20namespace Unity.Utils
21{
22 public bool is_regular_file (string path)
23 {
24 var file = GLib.File.new_for_path (path);
25 if (file.query_exists (null))
26 return file.query_file_type (GLib.FileQueryInfoFlags.NONE, null) == GLib.FileType.REGULAR;
27 return false;
28 }
29
30 public bool is_video (string path) throws Error
31 {
32 var file = GLib.File.new_for_path (path);
33 if (file.query_exists (null))
34 {
35 if (file.query_file_type (GLib.FileQueryInfoFlags.NONE, null) == GLib.FileType.REGULAR)
36 {
37 var content_type = file.query_info ("standard::content-type", GLib.FileQueryInfoFlags.NONE, null).get_content_type ();
38 return content_type.contains ("video");
39 }
40 }
41 return false;
42 }
43
44 private bool is_hidden (string path) throws Error
45 {
46 var file = GLib.File.new_for_path (path);
47 return file.query_info (GLib.FileAttribute.STANDARD_IS_HIDDEN, GLib.FileQueryInfoFlags.NONE, null).get_is_hidden ();
48 }
49
50 public string get_name (string path) throws Error
51 {
52 var file = GLib.File.new_for_path (path);
53 var finfo = file.query_info (GLib.FileAttribute.STANDARD_DISPLAY_NAME, GLib.FileQueryInfoFlags.NONE, null);
54 return finfo.get_attribute_as_string (GLib.FileAttribute.STANDARD_DISPLAY_NAME);
55 }
56
57 public uint gcd (uint a, uint b)
58 requires (a > 0 && b > 0)
59 ensures (result > 0)
60 {
61 for (;;)
62 {
63 if (a > b)
64 {
65 a = a % b;
66 if (a == 0)
67 return b;
68 }
69 else
70 {
71 b = b % a;
72 if (b == 0)
73 return a;
74 }
75 }
76 }
77}
0\ No newline at end of file78\ No newline at end of file
179
=== added file 'src/video-file.vala'
--- src/video-file.vala 1970-01-01 00:00:00 +0000
+++ src/video-file.vala 2013-02-19 17:19:01 +0000
@@ -0,0 +1,59 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
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 version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
17 *
18 */
19
20namespace Unity.VideoLens
21{
22 public struct VideoFile
23 {
24 string title;
25 string lc_title;
26 string comment;
27 string uri;
28 string icon;
29 int category;
30 }
31
32 public struct RemoteVideoFile
33 {
34 string title;
35 string comment;
36 string uri;
37 string icon;
38 string details_uri;
39 string price;
40 int category;
41 }
42
43public struct RemoteVideoDetails
44 {
45 string title;
46 string description;
47 string uri;
48 string image;
49 string source;
50 string release_date;
51 int duration;
52 string[] directors;
53 string starring;
54 string[] genres;
55 string uploaded_by;
56 string date_uploaded;
57 string price;
58 }
59}
0\ No newline at end of file60\ No newline at end of file
161
=== added directory 'tests/unit'
=== added file 'tests/unit/Makefile.am'
--- tests/unit/Makefile.am 1970-01-01 00:00:00 +0000
+++ tests/unit/Makefile.am 2013-02-19 17:19:01 +0000
@@ -0,0 +1,120 @@
1include $(top_srcdir)/Makefile.decl
2
3check_PROGRAMS = \
4 test-locate \
5 test-utils \
6 test-ubuntu-video-search \
7 $(NULL)
8
9TEST_PROGS += $(check_PROGRAMS)
10
11AM_VALAFLAGS = \
12 --pkg dee-1.0 \
13 --pkg unity \
14 --pkg unity-extras \
15 --pkg gio-2.0 \
16 --pkg gio-unix-2.0 \
17 --pkg json-glib-1.0 \
18 --pkg glib-2.0 \
19 --pkg zeitgeist-1.0 \
20 --pkg libsoup-gnome-2.4 \
21 --pkg libsoup-2.4 \
22 --vapidir $(srcdir) \
23 --vapidir $(top_srcdir)/vapi \
24 --target-glib=2.26 \
25 $(MAINTAINER_VALAFLAGS) \
26 $(NULL)
27
28LDADD = $(LENS_DAEMON_LIBS) \
29 $(test_libs)
30
31AM_CPPFLAGS = \
32 $(LENS_DAEMON_CFLAGS) \
33 -I$(srcdir) \
34 -I$(top_srcdir)/src \
35 -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \
36 $(COVERAGE_CFLAGS) \
37 $(NULL)
38
39AM_LDFLAGS = $(COVERAGE_LDFLAGS)
40
41if !ENABLE_C_WARNINGS
42 AM_CPPFLAGS += -w
43endif
44
45test_locate_VALASOURCES = \
46 test-locate.vala \
47 thumbnailer-mock.vala \
48 config-tests.vala \
49 $(top_srcdir)/src/locate.vala \
50 $(top_srcdir)/src/utils.vala \
51 $(top_srcdir)/src/video-file.vala \
52 $(NULL)
53
54test_utils_VALASOURCES = \
55 test-utils.vala \
56 config-tests.vala \
57 $(top_srcdir)/src/utils.vala \
58 $(NULL)
59
60test_ubuntu_video_search_VALASOURCES = \
61 test-ubuntu-video-search.vala \
62 config-tests.vala \
63 $(top_srcdir)/src/remote-scope-globals.vala \
64 $(top_srcdir)/src/ubuntu-video-search.vala \
65 $(top_srcdir)/src/video-file.vala \
66 $(top_srcdir)/src/remote-scope.vala \
67 $(top_srcdir)/src/remote-uri.vala \
68 $(top_srcdir)/src/utils.vala \
69 $(NULL)
70
71nodist_test_locate_SOURCES = $(test_locate_VALASOURCES:.vala=.c)
72
73nodist_test_utils_SOURCES = $(test_utils_VALASOURCES:.vala=.c)
74
75nodist_test_ubuntu_video_search_SOURCES = $(test_ubuntu_video_search_VALASOURCES:.vala=.c)
76
77CLEANFILES = *.stamp \
78 *.c \
79 $(NULL)
80
81EXTRA_DIST = \
82 $(test_locate_VALASOURCES) \
83 $(test_utils_VALASOURCES) \
84 $(test_ubuntu_video_search_VALASOURCES) \
85 data/videosearch_input1.txt \
86 data/videosearch_input2.txt \
87 data/videosearch_details1.txt \
88 data/video1.avi \
89 data/video2.avi \
90 data/video3.avi \
91 data/video1.mpg \
92 data/video2.mpg \
93 data/video3.mpg \
94 $(NULL)
95
96BUILT_SOURCES = \
97 test-locate.vala.stamp \
98 test-utils.vala.stamp \
99 test-ubuntu-video-search.vala.stamp \
100 $(NULL)
101
102test-locate.vala.stamp: $(test_locate_VALASOURCES)
103 $(AM_V_GEN)$(VALAC) -C $(AM_VALAFLAGS) $(VALAFLAGS) $^
104 @touch $@
105
106test-utils.vala.stamp: $(test_utils_VALASOURCES)
107 $(AM_V_GEN)$(VALAC) -C $(AM_VALAFLAGS) $(VALAFLAGS) $^
108 @touch $@
109
110test-ubuntu-video-search.vala.stamp: $(test_ubuntu_video_search_VALASOURCES)
111 $(AM_V_GEN)$(VALAC) -C $(AM_VALAFLAGS) $(VALAFLAGS) $^
112 @touch $@
113
114# START HEADLESS TESTS
115if ENABLE_HEADLESS_TESTS
116test-headless:
117 $(XVFB) make test-nonrecursive; \
118 sleep 1;
119endif
120# END HEADLESS TESTS
0121
=== added file 'tests/unit/config-tests.vala.in'
--- tests/unit/config-tests.vala.in 1970-01-01 00:00:00 +0000
+++ tests/unit/config-tests.vala.in 2013-02-19 17:19:01 +0000
@@ -0,0 +1,6 @@
1namespace Config {
2 const string TESTDATADIR = "@abs_top_srcdir@/tests/unit/data";
3 const string LOCALEDIR = "@DATADIR@/locale";
4 const string PACKAGE = "@PACKAGE@";
5 const string VERSION = "@VERSION@";
6}
07
=== added directory 'tests/unit/data'
=== added file 'tests/unit/data/video1.avi'
=== added file 'tests/unit/data/video1.mpg'
=== added file 'tests/unit/data/video2.avi'
=== added file 'tests/unit/data/video2.mpg'
=== added file 'tests/unit/data/video3.avi'
=== added file 'tests/unit/data/video3.mpg'
=== added file 'tests/unit/data/videosearch_details1.txt'
--- tests/unit/data/videosearch_details1.txt 1970-01-01 00:00:00 +0000
+++ tests/unit/data/videosearch_details1.txt 2013-02-19 17:19:01 +0000
@@ -0,0 +1,26 @@
1{
2 "browser_url": "http://browserurl1",
3 "genres": [
4 "genre1",
5 "genre2"
6 ],
7 "description": "a movie",
8 "title": "title1",
9 "price": 1,
10 "source": "source1",
11 "directors": [
12 "director1",
13 "director2"
14 ],
15 "details": "http://detailsurl1",
16 "starring": [
17 "star1",
18 "star2"
19 ],
20 "uploaded_by": "uploader1",
21 "duration": "5477",
22 "release_date": "2010-04-01T07:00:00.000Z",
23 "image": "http://image1",
24 "date_uploaded": "2012-06-06T21:55:12.000Z",
25 "formatted_price": "$1"
26}
027
=== added file 'tests/unit/data/videosearch_input1.txt'
--- tests/unit/data/videosearch_input1.txt 1970-01-01 00:00:00 +0000
+++ tests/unit/data/videosearch_input1.txt 2013-02-19 17:19:01 +0000
@@ -0,0 +1,44 @@
1{
2 "other": [
3 {
4 "url": "http://url0",
5 "source": "source0",
6 "img": "http://image0",
7 "title": "title0"
8 },
9 {
10 "url": "http://url1",
11 "source": "source1",
12 "details": "http://details1",
13 "img": "http://image1",
14 "title": "title1"
15 }
16 ],
17 "treats": [
18 {
19 "img": "http://image2",
20 "title": "title2",
21 "url": "http://url2",
22 "price": 1,
23 "source": "source2",
24 "details": "http://details2",
25 "formatted_price": "1 USD"
26 },
27 {
28 "img": "http://image3",
29 "title": "title3",
30 "url": "http://url3",
31 "price": 0,
32 "source": "source3",
33 "details": "http://details3",
34 "formatted_price": "free"
35 },
36 {
37 "img": "http://image4",
38 "title": "title4",
39 "url": "http://url4",
40 "source": "source4",
41 "details": "http://details4"
42 }
43 ]
44}
045
=== added file 'tests/unit/data/videosearch_input2.txt'
--- tests/unit/data/videosearch_input2.txt 1970-01-01 00:00:00 +0000
+++ tests/unit/data/videosearch_input2.txt 2013-02-19 17:19:01 +0000
@@ -0,0 +1,17 @@
1[
2 {
3 "url": "http://url0",
4 "source": "source0",
5 "img": "http://image0",
6 "title": "title0"
7 },
8 {
9 "url": "http://url1",
10 "source": "source1",
11 "details": "http://details1",
12 "img": "http://image1",
13 "title": "title1",
14 "price": 1,
15 "formatted_price": "1 USD"
16 }
17]
018
=== added file 'tests/unit/test-locate.vala'
--- tests/unit/test-locate.vala 1970-01-01 00:00:00 +0000
+++ tests/unit/test-locate.vala 2013-02-19 17:19:01 +0000
@@ -0,0 +1,57 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
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 version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
17 */
18
19namespace Unity.VideoLens
20{
21 private Thumbnailer thumbnailer;
22 private Locate locate;
23 private string video_dir;
24 private string user_videos_folder;
25
26 public static int main (string[] args)
27 {
28 video_dir = Config.TESTDATADIR;
29 user_videos_folder = GLib.Environment.get_user_special_dir (GLib.UserDirectory.VIDEOS);
30
31 locate = new Locate ("cache", video_dir);
32 thumbnailer = new Thumbnailer ();
33
34 Test.init (ref args);
35 Test.add_data_func ("/Locate/ParseLocateResults", test_parse);
36 Test.add_data_func ("/Locate/LocateQueryString", test_query_string);
37 Test.run ();
38 return 0;
39 }
40
41 internal static void test_parse ()
42 {
43 string input = @"$video_dir/video1.avi\n$video_dir/video2.avi\n$video_dir/video1.mpg\n$video_dir/video2.mpg\n";
44 var results = locate.parse_locate_results (input, 1000, thumbnailer, (path) => { return true; });
45 assert (results.size == 4);
46
47 /* Filter out all results */
48 results = locate.parse_locate_results (input, 1000, thumbnailer, (path) => { return false; });
49 assert (results.size == 0);
50 }
51
52 internal static void test_query_string ()
53 {
54 assert (locate.locate_query_string ("foo") == video_dir + "*foo*");
55 assert (locate.locate_query_string ("foo bar") == video_dir + "*foo*bar*");
56 }
57}
0\ No newline at end of file58\ No newline at end of file
159
=== added file 'tests/unit/test-ubuntu-video-search.vala'
--- tests/unit/test-ubuntu-video-search.vala 1970-01-01 00:00:00 +0000
+++ tests/unit/test-ubuntu-video-search.vala 2013-02-19 17:19:01 +0000
@@ -0,0 +1,329 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
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 version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
17 *
18 */
19
20namespace Unity.VideoLens
21{
22 public static int main (string[] args)
23 {
24 /* Sort up locale to get translations but also sorting and
25 * punctuation right */
26 GLib.Intl.textdomain (Config.PACKAGE);
27 GLib.Intl.bindtextdomain (Config.PACKAGE, Config.LOCALEDIR);
28 GLib.Intl.bind_textdomain_codeset (Config.PACKAGE, "UTF-8");
29 GLib.Intl.setlocale(GLib.LocaleCategory.ALL, "");
30
31 Test.init (ref args);
32 Test.add_data_func ("/UbuntuVideoSearch/ProcessSourcesResults", test_process_sources_results);
33 Test.add_data_func ("/UbuntuVideoSearch/ProcessInvalidSourcesResults", test_process_invalid_sources_results);
34 Test.add_data_func ("/UbuntuVideoSearch/BuildSearchUri", test_build_search_uri);
35 Test.add_data_func ("/UbuntuVideoSearch/ProcessInvalidSearchResultsJson", test_invalid_search_results_json);
36 Test.add_data_func ("/UbuntuVideoSearch/ProcessSearchResults", test_process_search_results);
37 Test.add_data_func ("/UbuntuVideoSearch/ProcessSearchResultsTreatYourself", test_process_search_no_split_ty_category);
38 Test.add_data_func ("/UbuntuVideoSearch/ProcessSearchResultsOnline", test_process_search_no_split_online_category);
39 Test.add_data_func ("/UbuntuVideoSearch/JoinArray", test_join_array);
40 Test.add_data_func ("/UbuntuVideoSearch/ProcessInvalidDetailsResults", test_process_invalid_details_results);
41 Test.add_data_func ("/UbuntuVideoSearch/ProcessDetailsResults", test_process_details_results);
42
43 Test.run ();
44
45 return 0;
46 }
47
48 internal static void test_process_sources_results ()
49 {
50 assert (UbuntuVideoSearch.process_sources_results ("[]").size == 0);
51
52 var sources = UbuntuVideoSearch.process_sources_results ("[\"Foo\", \"Bar\"]");
53 assert (sources.size == 2);
54 assert (sources[0] == "Foo");
55 assert (sources[1] == "Bar");
56 }
57
58 internal static void test_process_invalid_sources_results ()
59 {
60 bool got_excp = false;
61 try
62 {
63 UbuntuVideoSearch.process_sources_results (";");
64 }
65 catch (Error e)
66 {
67 got_excp = true;
68 }
69 assert (got_excp == true);
70 }
71
72 internal static void test_build_search_uri ()
73 {
74 // test null sources
75 assert (UbuntuVideoSearch.build_search_uri ("foo", null) == "http://videosearch.ubuntu.com/v0/search?q=foo&split=true");
76
77 // test empty sources list
78 var sources = new Gee.ArrayList<string> (null);
79 assert (UbuntuVideoSearch.build_search_uri ("foo", sources) == "http://videosearch.ubuntu.com/v0/search?q=foo&split=true");
80
81 // test non-empty sources and uri escaping
82 sources.add ("Amazon");
83 sources.add ("BBC");
84 assert (UbuntuVideoSearch.build_search_uri ("foo!", sources) == "http://videosearch.ubuntu.com/v0/search?q=foo%21&split=true&sources=Amazon,BBC");
85 }
86
87 internal static void test_invalid_search_results_json ()
88 {
89 // ignore warnings
90 Test.log_set_fatal_handler (() => { return false; });
91
92 bool got_excp = false;
93 try
94 {
95 UbuntuVideoSearch.process_search_results (",", false);
96 }
97 catch (Error e)
98 {
99 got_excp = true;
100 }
101 assert (got_excp == true);
102 }
103
104 /**
105 * Test search results of search query with 'split=true' flag, resulting in search results
106 * being placed in CAT_INDEX_MORE (if from 'treats' results group) and CAT_INDEX_ONLINE category (if from 'other' group).
107 */
108 internal static void test_process_search_results ()
109 {
110 string datadir = Config.TESTDATADIR;
111 uint8[] contents;
112 assert (File.new_for_path (@"$datadir/videosearch_input1.txt").load_contents (null, out contents, null) == true);
113
114 var results = UbuntuVideoSearch.process_search_results ((string)contents, false);
115 assert (results.size == 5);
116
117 bool got_video[5] = {false, false, false, false, false};
118
119 // verify that all expected records are there
120 foreach (var res in results)
121 {
122 if (res.title == "title0")
123 {
124 got_video[0] = true;
125 assert (res.comment == "source0");
126 assert (res.uri == "http://url0");
127 assert (res.icon == "http://image0");
128 assert (res.details_uri == "");
129 assert (res.price == null);
130 assert (res.category == CAT_INDEX_ONLINE);
131 }
132 else if (res.title == "title1")
133 {
134 got_video[1] = true;
135 assert (res.comment == "source1");
136 assert (res.uri == "http://url1");
137 assert (res.icon == "http://image1");
138 assert (res.details_uri == "http://details1");
139 assert (res.price == null);
140 assert (res.category == CAT_INDEX_ONLINE);
141 }
142 else if (res.title == "title2")
143 {
144 got_video[2] = true;
145 assert (res.comment == "source2");
146 assert (res.uri == "http://url2");
147 assert (res.icon == "http://image2");
148 assert (res.details_uri == "http://details2");
149 assert (res.price == "1 USD");
150 assert (res.category == CAT_INDEX_MORE);
151 }
152 else if (res.title == "title3")
153 {
154 got_video[3] = true;
155 assert (res.comment == "source3");
156 assert (res.uri == "http://url3");
157 assert (res.icon == "http://image3");
158 assert (res.details_uri == "http://details3");
159 assert (res.price == _("Free"));
160 assert (res.category == CAT_INDEX_MORE);
161 }
162 else if (res.title == "title4")
163 {
164 got_video[4] = true;
165 assert (res.comment == "source4");
166 assert (res.uri == "http://url4");
167 assert (res.icon == "http://image4");
168 assert (res.details_uri == "http://details4");
169 assert (res.price == _("Free")); // null price of video4 turned into 'free' for 'Treat yourself category'
170 assert (res.category == CAT_INDEX_MORE);
171 }
172 else
173 {
174 assert (1 == 0); // this shouldn't happen
175 }
176 }
177
178 assert (got_video[0] == true);
179 assert (got_video[1] == true);
180 assert (got_video[2] == true);
181 assert (got_video[3] == true);
182 assert (got_video[4] == true);
183 }
184
185 /**
186 * Test search results of search query with no 'split' option and is_treat_yourself=true, resulting in all search results
187 * being placed in CAT_INDEX_MORE category.
188 */
189 internal static void test_process_search_no_split_ty_category ()
190 {
191 string datadir = Config.TESTDATADIR;
192 uint8[] contents;
193 assert (File.new_for_path (@"$datadir/videosearch_input2.txt").load_contents (null, out contents, null) == true);
194
195 var results = UbuntuVideoSearch.process_search_results ((string)contents, /* is_treat_yourself */ true);
196 assert (results.size == 2);
197
198 bool got_video[2] = {false, false};
199
200 // verify that all expected records are there
201 foreach (var res in results)
202 {
203 if (res.title == "title0")
204 {
205 got_video[0] = true;
206 assert (res.comment == "source0");
207 assert (res.uri == "http://url0");
208 assert (res.icon == "http://image0");
209 assert (res.details_uri == "");
210 assert (res.price == _("Free"));
211 assert (res.category == CAT_INDEX_MORE);
212 }
213 else if (res.title == "title1")
214 {
215 got_video[1] = true;
216 assert (res.comment == "source1");
217 assert (res.uri == "http://url1");
218 assert (res.icon == "http://image1");
219 assert (res.details_uri == "http://details1");
220 assert (res.price == "1 USD");
221 assert (res.category == CAT_INDEX_MORE);
222 }
223 else
224 {
225 assert (1 == 0); // this shouldn't happen
226 }
227 }
228
229 assert (got_video[0] == true);
230 assert (got_video[1] == true);
231 }
232
233 /**
234 * Test search results of search query with no 'split' option and is_treat_yourself=false, resulting in all search results
235 * being placed in CAT_INDEX_ONLINE
236 */
237 internal static void test_process_search_no_split_online_category ()
238 {
239 string datadir = Config.TESTDATADIR;
240 uint8[] contents;
241 assert (File.new_for_path (@"$datadir/videosearch_input2.txt").load_contents (null, out contents, null) == true);
242
243 var results = UbuntuVideoSearch.process_search_results ((string)contents, /* is_treat_yourself */ false);
244 assert (results.size == 2);
245
246 bool got_video[2] = {false, false};
247
248 // verify that all expected records are there
249 foreach (var res in results)
250 {
251 if (res.title == "title0")
252 {
253 got_video[0] = true;
254 assert (res.comment == "source0");
255 assert (res.uri == "http://url0");
256 assert (res.icon == "http://image0");
257 assert (res.details_uri == "");
258 assert (res.price == null);
259 assert (res.category == CAT_INDEX_ONLINE);
260 }
261 else if (res.title == "title1")
262 {
263 got_video[1] = true;
264 assert (res.comment == "source1");
265 assert (res.uri == "http://url1");
266 assert (res.icon == "http://image1");
267 assert (res.details_uri == "http://details1");
268 assert (res.price == null);
269 assert (res.category == CAT_INDEX_ONLINE);
270 }
271 else
272 {
273 assert (1 == 0); // this shouldn't happen
274 }
275 }
276
277 assert (got_video[0] == true);
278 assert (got_video[1] == true);
279 }
280
281 internal static void test_join_array ()
282 {
283 var array = new Json.Array ();
284 array.add_string_element ("abc");
285 array.add_string_element ("def");
286
287 assert (UbuntuVideoSearch.join_array (array, ", ") == "abc, def");
288 }
289
290 internal static void test_process_invalid_details_results ()
291 {
292 bool got_excp = false;
293 try
294 {
295 UbuntuVideoSearch.process_details_results (",");
296 }
297 catch (Error e)
298 {
299 got_excp = true;
300 }
301 assert (got_excp == true);
302 }
303
304 internal static void test_process_details_results ()
305 {
306 string datadir = Config.TESTDATADIR;
307 uint8[] contents;
308 assert (File.new_for_path (@"$datadir/videosearch_details1.txt").load_contents (null, out contents, null) == true);
309
310 var video = UbuntuVideoSearch.process_details_results ((string)contents);
311
312 assert (video.title == "title1");
313 assert (video.description == "a movie");
314 assert (video.image == "http://image1");
315 assert (video.directors.length == 2);
316 assert (video.directors[0] == "director1");
317 assert (video.directors[1] == "director2");
318 assert (video.duration == 91);
319 assert (video.genres.length == 2);
320 assert (video.genres[0] == "genre1");
321 assert (video.genres[1] == "genre2");
322 assert (video.starring == "star1, star2");
323 assert (video.source == "source1");
324 assert (video.release_date == "2010");
325 assert (video.uploaded_by == "uploader1");
326 assert (video.price == "$1");
327 assert (video.date_uploaded == "2012-06-06T21:55:12.000Z");
328 }
329}
0330
=== added file 'tests/unit/test-utils.vala'
--- tests/unit/test-utils.vala 1970-01-01 00:00:00 +0000
+++ tests/unit/test-utils.vala 2013-02-19 17:19:01 +0000
@@ -0,0 +1,60 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
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 version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
17 */
18
19namespace Unity.VideoLens
20{
21 public static int main (string[] args)
22 {
23 Test.init (ref args);
24 Test.add_data_func ("/Utils/Gcd", test_gcd);
25 Test.add_data_func ("/Utils/IsRegularFile", test_is_regular_file);
26 Test.add_data_func ("/Utils/GetName", test_get_name);
27 Test.add_data_func ("/Utils/IsVideo", test_is_video);
28 Test.run ();
29 return 0;
30 }
31
32 internal static void test_gcd ()
33 {
34 assert (Utils.gcd (1, 1) == 1);
35 assert (Utils.gcd (2, 1) == 1);
36 assert (Utils.gcd (10, 2) == 2);
37 assert (Utils.gcd (2, 10) == 2);
38 assert (Utils.gcd (20, 15) == 5);
39 }
40
41 internal static void test_is_regular_file ()
42 {
43 assert (Utils.is_regular_file ("/etc/passwd") == true);
44 assert (Utils.is_regular_file ("/dev/null") == false);
45 assert (Utils.is_regular_file ("/non-existing-file") == false);
46 }
47
48 internal static void test_get_name ()
49 {
50 assert (Utils.get_name ("/etc/passwd") == "passwd");
51 }
52
53 internal static void test_is_video ()
54 {
55 var video_dir = Config.TESTDATADIR;
56
57 assert (Utils.is_video ("/etc/passwd") == false);
58 assert (Utils.is_video (@"$video_dir/video1.avi") == true);
59 }
60}
0\ No newline at end of file61\ No newline at end of file
162
=== added file 'tests/unit/thumbnailer-mock.vala'
--- tests/unit/thumbnailer-mock.vala 1970-01-01 00:00:00 +0000
+++ tests/unit/thumbnailer-mock.vala 2013-02-19 17:19:01 +0000
@@ -0,0 +1,29 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
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 version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
17 *
18 */
19
20namespace Unity.VideoLens
21{
22 public class Thumbnailer
23 {
24 public string get_icon (string video_file)
25 {
26 return "mock-icon";
27 }
28 }
29}
0\ No newline at end of file30\ No newline at end of file
131
=== added directory 'vapi'
=== added file 'vapi/Makefile.am'
--- vapi/Makefile.am 1970-01-01 00:00:00 +0000
+++ vapi/Makefile.am 2013-02-19 17:19:01 +0000
@@ -0,0 +1,5 @@
1NULL =
2BUILT_SOURCES =
3CLEANFILES =
4EXTRA_DIST = libsoup-gnome-2.4.vapi
5
06
=== added file 'vapi/libsoup-gnome-2.4.vapi'
--- vapi/libsoup-gnome-2.4.vapi 1970-01-01 00:00:00 +0000
+++ vapi/libsoup-gnome-2.4.vapi 2013-02-19 17:19:01 +0000
@@ -0,0 +1,8 @@
1[CCode (cprefix = "Soup", gir_namespace = "SoupGNOME", gir_version = "2.4", lower_case_cprefix = "soup_")]
2namespace SoupGNOME {
3 [CCode (cheader_filename = "libsoup/soup-gnome.h", type_id = "soup_proxy_resolver_gnome_get_type ()")]
4 public class ProxyResolverGNOME : Soup.ProxyResolverDefault, Soup.ProxyURIResolver, Soup.SessionFeature {
5 [CCode (has_construct_function = false)]
6 protected ProxyResolverGNOME ();
7 }
8}

Subscribers

People subscribed via source and target branches