Merge lp:~stolowski/unity-lens-video/vala-rewrite into lp:unity-lens-video
- vala-rewrite
- Merge into trunk
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 |
Related bugs: |
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.
Paweł Stołowski (stolowski) wrote : | # |
Implemented all (hopefully) suggested corrections. As discussed on IRC, zeitgeist and thumbnail handling should stay as is for now.
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.
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.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
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
1 | === added file 'AUTHORS' |
2 | === added file 'ChangeLog' |
3 | === added file 'Makefile.am' |
4 | --- Makefile.am 1970-01-01 00:00:00 +0000 |
5 | +++ Makefile.am 2013-02-19 17:19:01 +0000 |
6 | @@ -0,0 +1,5 @@ |
7 | +SUBDIRS = src po tests/unit vapi data |
8 | + |
9 | +DISTCHECK_CONFIGURE_FLAGS = --enable-localinstall |
10 | + |
11 | +include $(top_srcdir)/Makefile.am.coverage |
12 | |
13 | === added file 'Makefile.am.coverage' |
14 | --- Makefile.am.coverage 1970-01-01 00:00:00 +0000 |
15 | +++ Makefile.am.coverage 2013-02-19 17:19:01 +0000 |
16 | @@ -0,0 +1,48 @@ |
17 | + |
18 | +# Coverage targets |
19 | + |
20 | +.PHONY: clean-gcno clean-gcda \ |
21 | + coverage-html generate-coverage-html clean-coverage-html \ |
22 | + coverage-gcovr generate-coverage-gcovr clean-coverage-gcovr |
23 | + |
24 | +clean-local: clean-gcno clean-coverage-html clean-coverage-gcovr |
25 | + |
26 | +if HAVE_GCOV |
27 | + |
28 | +clean-gcno: |
29 | + @echo Removing old coverage instrumentation |
30 | + -find -name '*.gcno' -print | xargs -r rm |
31 | + |
32 | +clean-gcda: |
33 | + @echo Removing old coverage results |
34 | + -find -name '*.gcda' -print | xargs -r rm |
35 | + |
36 | +coverage-html: clean-gcda |
37 | + -$(MAKE) $(AM_MAKEFLAGS) -k check |
38 | + $(MAKE) $(AM_MAKEFLAGS) generate-coverage-html |
39 | + |
40 | +generate-coverage-html: |
41 | + @echo Collecting coverage data |
42 | + $(LCOV) --directory $(top_builddir) --capture --output-file coverage.info --no-checksum --compat-libtool |
43 | + LANG=C $(GENHTML) --prefix $(top_builddir) --output-directory coveragereport --title "Code Coverage" --legend --show-details coverage.info |
44 | + |
45 | +clean-coverage-html: clean-gcda |
46 | + -$(LCOV) --directory $(top_builddir) -z |
47 | + -rm -rf coverage.info coveragereport |
48 | + |
49 | +if HAVE_GCOVR |
50 | + |
51 | +coverage-gcovr: clean-gcda |
52 | + -$(MAKE) $(AM_MAKEFLAGS) -k check |
53 | + $(MAKE) $(AM_MAKEFLAGS) generate-coverage-gcovr |
54 | + |
55 | +generate-coverage-gcovr: |
56 | + @echo Generating coverage GCOVR report |
57 | + $(GCOVR) -x -r $(top_builddir) -o $(top_builddir)/coverage.xml |
58 | + |
59 | +clean-coverage-gcovr: clean-gcda |
60 | + -rm -rf $(top_builddir)/coverage.xml |
61 | + |
62 | +endif # HAVE_GCOVR |
63 | + |
64 | +endif # HAVE_GCOV |
65 | |
66 | === added file 'Makefile.decl' |
67 | --- Makefile.decl 1970-01-01 00:00:00 +0000 |
68 | +++ Makefile.decl 2013-02-19 17:19:01 +0000 |
69 | @@ -0,0 +1,75 @@ |
70 | +# GLIB - Library of useful C routines |
71 | +# |
72 | +# This file is copied almost verbatim from the GLib-2.0 distribution |
73 | +# |
74 | + |
75 | +GTESTER = gtester |
76 | +GTESTER_REPORT = gtester-report |
77 | + |
78 | +# initialize variables for unconditional += appending |
79 | +EXTRA_DIST = |
80 | +TEST_PROGS = |
81 | + |
82 | +### testing rules |
83 | + |
84 | +# test: run all tests in cwd and subdirs |
85 | +test: test-nonrecursive |
86 | + @ for subdir in $(SUBDIRS) . ; do \ |
87 | + test "$$subdir" = "." -o "$$subdir" = "po" || \ |
88 | + ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \ |
89 | + done |
90 | + |
91 | +# test-nonrecursive: run tests only in cwd |
92 | +test-nonrecursive: ${TEST_PROGS} |
93 | + @test -z "${TEST_PROGS}" || G_DEBUG=gc-friendly MALLOC_CHECK_=2 MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256)) ${GTESTER} --verbose ${TEST_PROGS} |
94 | + |
95 | +# test-report: run tests in subdirs and generate report |
96 | +# perf-report: run tests in subdirs with -m perf and generate report |
97 | +# full-report: like test-report: with -m perf and -m slow |
98 | +test-report perf-report full-report: ${TEST_PROGS} |
99 | + @test -z "${TEST_PROGS}" || { \ |
100 | + case $@ in \ |
101 | + test-report) test_options="-k";; \ |
102 | + perf-report) test_options="-k -m=perf";; \ |
103 | + full-report) test_options="-k -m=perf -m=slow";; \ |
104 | + esac ; \ |
105 | + if test -z "$$GTESTER_LOGDIR" ; then \ |
106 | + ${GTESTER} --verbose $$test_options -o test-report.xml ${TEST_PROGS} ; \ |
107 | + elif test -n "${TEST_PROGS}" ; then \ |
108 | + ${GTESTER} --verbose $$test_options -o `mktemp "$$GTESTER_LOGDIR/log-XXXXXX"` ${TEST_PROGS} ; \ |
109 | + fi ; \ |
110 | + } |
111 | + @ ignore_logdir=true ; \ |
112 | + if test -z "$$GTESTER_LOGDIR" ; then \ |
113 | + GTESTER_LOGDIR=`mktemp -d "\`pwd\`/.testlogs-XXXXXX"`; export GTESTER_LOGDIR ; \ |
114 | + ignore_logdir=false ; \ |
115 | + fi ; \ |
116 | + REVISION=$(VERSION) ; \ |
117 | + for subdir in $(SUBDIRS) . ; do \ |
118 | + test "$$subdir" = "." -o "$$subdir" = "po" || \ |
119 | + ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \ |
120 | + done ; \ |
121 | + $$ignore_logdir || { \ |
122 | + echo '<?xml version="1.0"?>' > $@.xml ; \ |
123 | + echo '<report-collection>' >> $@.xml ; \ |
124 | + echo '<info>' >> $@.xml ; \ |
125 | + echo ' <package>$(PACKAGE)</package>' >> $@.xml ; \ |
126 | + echo ' <version>$(VERSION)</version>' >> $@.xml ; \ |
127 | + echo " <revision>$$REVISION</revision>" >> $@.xml ; \ |
128 | + echo '</info>' >> $@.xml ; \ |
129 | + for lf in `ls -L "$$GTESTER_LOGDIR"/.` ; do \ |
130 | + sed '1,1s/^<?xml\b[^>?]*?>//' <"$$GTESTER_LOGDIR"/"$$lf" >> $@.xml ; \ |
131 | + done ; \ |
132 | + echo >> $@.xml ; \ |
133 | + echo '</report-collection>' >> $@.xml ; \ |
134 | + rm -rf "$$GTESTER_LOGDIR"/ ; \ |
135 | + ${GTESTER_REPORT} --version 2>/dev/null 1>&2 ; test "$$?" != 0 || ${GTESTER_REPORT} $@.xml >$@.html ; \ |
136 | + } |
137 | +.PHONY: test test-report perf-report full-report test-nonrecursive |
138 | + |
139 | +# run tests in cwd as part of make check |
140 | +if ENABLE_HEADLESS_TESTS |
141 | +check-local: test-headless |
142 | +else |
143 | +check-local: test-nonrecursive |
144 | +endif |
145 | |
146 | === added file 'NEWS' |
147 | === added file 'acinclude.m4' |
148 | --- acinclude.m4 1970-01-01 00:00:00 +0000 |
149 | +++ acinclude.m4 2013-02-19 17:19:01 +0000 |
150 | @@ -0,0 +1,40 @@ |
151 | +dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR) |
152 | +dnl |
153 | +dnl example |
154 | +dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir) |
155 | +dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local |
156 | + |
157 | +AC_DEFUN([AS_AC_EXPAND], |
158 | +[ |
159 | + EXP_VAR=[$1] |
160 | + FROM_VAR=[$2] |
161 | + |
162 | + dnl first expand prefix and exec_prefix if necessary |
163 | + prefix_save=$prefix |
164 | + exec_prefix_save=$exec_prefix |
165 | + |
166 | + dnl if no prefix given, then use /usr/local, the default prefix |
167 | + if test "x$prefix" = "xNONE"; then |
168 | + prefix=$ac_default_prefix |
169 | + fi |
170 | + dnl if no exec_prefix given, then use prefix |
171 | + if test "x$exec_prefix" = "xNONE"; then |
172 | + exec_prefix=$prefix |
173 | + fi |
174 | + |
175 | + full_var="$FROM_VAR" |
176 | + dnl loop until it doesn't change anymore |
177 | + while true; do |
178 | + new_full_var="`eval echo $full_var`" |
179 | + if test "x$new_full_var"="x$full_var"; then break; fi |
180 | + full_var=$new_full_var |
181 | + done |
182 | + |
183 | + dnl clean up |
184 | + full_var=$new_full_var |
185 | + AC_SUBST([$1], "$full_var") |
186 | + |
187 | + dnl restore prefix and exec_prefix |
188 | + prefix=$prefix_save |
189 | + exec_prefix=$exec_prefix_save |
190 | +]) |
191 | |
192 | === added file 'autogen.sh' |
193 | --- autogen.sh 1970-01-01 00:00:00 +0000 |
194 | +++ autogen.sh 2013-02-19 17:19:01 +0000 |
195 | @@ -0,0 +1,13 @@ |
196 | +#!/bin/sh |
197 | + |
198 | +srcdir=`dirname $0` |
199 | + |
200 | +PKG_NAME="unity-lens-video" |
201 | + |
202 | +which gnome-autogen.sh || { |
203 | + echo "You need gnome-common from GNOME SVN" |
204 | + exit 1 |
205 | +} |
206 | + |
207 | +USE_GNOME2_MACROS=1 \ |
208 | +. gnome-autogen.sh "$@" |
209 | |
210 | === added file 'configure.ac' |
211 | --- configure.ac 1970-01-01 00:00:00 +0000 |
212 | +++ configure.ac 2013-02-19 17:19:01 +0000 |
213 | @@ -0,0 +1,208 @@ |
214 | +AC_INIT(unity-lens-video, 6.8.0, https://launchpad.net/unity-lens-video) |
215 | +AC_COPYRIGHT([Copyright 2012 Canonical]) |
216 | + |
217 | +AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION) |
218 | + |
219 | +##################################################### |
220 | +# Silent build rules |
221 | +##################################################### |
222 | +m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) |
223 | + |
224 | +AC_PREREQ(2.59) |
225 | + |
226 | +AC_CONFIG_HEADERS([config.h]) |
227 | + |
228 | +##################################################### |
229 | +# Init the other things we depend on |
230 | +##################################################### |
231 | +AM_MAINTAINER_MODE |
232 | +AM_PROG_VALAC([0.16.0]) |
233 | +AS_IF([test -z "$VALAC"], [AC_MSG_ERROR(["No valac compiler found."])]) |
234 | +AC_PROG_CC |
235 | +AM_PROG_CC_C_O |
236 | +AC_HEADER_STDC |
237 | + |
238 | +LT_INIT |
239 | +AC_CONFIG_MACRO_DIR([m4]) |
240 | + |
241 | +############################################# |
242 | +# Gettext |
243 | +############################################# |
244 | +GETTEXT_PACKAGE="$PACKAGE" |
245 | +AC_SUBST(GETTEXT_PACKAGE) |
246 | +#AC_SUBST([CONFIG_STATUS_DEPENDENCIES],['$(top_srcdir)/po/LINGUAS']) |
247 | +AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [gettext domain]) |
248 | +AM_GLIB_GNU_GETTEXT |
249 | + |
250 | +# AM_GNOME_GETTEXT above substs $DATADIRNAME |
251 | +# this is the directory where the *.{mo,gmo} files are installed |
252 | +localedir='${prefix}/${DATADIRNAME}/locale' |
253 | +AC_SUBST(localedir) |
254 | + |
255 | +IT_PROG_INTLTOOL([0.40.0]) |
256 | + |
257 | +AC_DEFINE_UNQUOTED(LOCALE_DIR, "${PREFIX}/${DATADIRNAME}/locale",[Locale directory]) |
258 | +AC_DEFINE_UNQUOTED(DATADIR, "${PREFIX}/${DATADIRNAME}",[Data directory]) |
259 | +AC_DEFINE_UNQUOTED(PREFIXDIR, "${PREFIX}",[Prefix directory]) |
260 | + |
261 | +###################################################### |
262 | +# intltool rule for generating translated .lens file |
263 | +###################################################### |
264 | +INTLTOOL_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 $< [$]@' |
265 | +AC_SUBST(INTLTOOL_LENS_RULE) |
266 | + |
267 | +########################### |
268 | +# gcov coverage reporting |
269 | +########################### |
270 | +m4_include([m4/gcov.m4]) |
271 | +AC_TDD_GCOV |
272 | +AM_CONDITIONAL([HAVE_GCOV], [test "x$ac_cv_check_gcov" = xyes]) |
273 | +AM_CONDITIONAL([HAVE_LCOV], [test "x$ac_cv_check_lcov" = xyes]) |
274 | +AM_CONDITIONAL([HAVE_GCOVR], [test "x$ac_cv_check_gcovr" = xyes]) |
275 | +AC_SUBST(COVERAGE_CFLAGS) |
276 | +AC_SUBST(COVERAGE_LDFLAGS) |
277 | + |
278 | +##################################################### |
279 | +# Check for module and library dependancies |
280 | +##################################################### |
281 | +GLIB_REQUIRED=2.27 |
282 | +PKG_CHECK_MODULES(LENS_DAEMON, |
283 | + glib-2.0 >= $GLIB_REQUIRED |
284 | + gobject-2.0 >= $GLIB_REQUIRED |
285 | + gio-2.0 >= $GLIB_REQUIRED |
286 | + gio-unix-2.0 >= $GLIB_REQUIRED |
287 | + dee-1.0 >= 1.0.7 |
288 | + gee-1.0 |
289 | + libsoup-gnome-2.4 |
290 | + json-glib-1.0 |
291 | + zeitgeist-1.0 >= 0.3.8 |
292 | + unity >= 6.90.2 |
293 | + unity-extras >= 6.90.2 |
294 | + ) |
295 | + |
296 | +AC_SUBST(LENS_DAEMON_CFLAGS) |
297 | +AC_SUBST(LENS_DAEMON_LIBS) |
298 | + |
299 | +#################################################################### |
300 | +# C compiler warnings |
301 | +#################################################################### |
302 | +AC_ARG_ENABLE([c-warnings], |
303 | + AC_HELP_STRING([--enable-c-warnings=@<:@no/yes@:>@], [show warnings from the C compiler @<:@default=no@:>@]),, |
304 | + [enable_c_warnings=no]) |
305 | + |
306 | +if test "x$enable_c_warnings" = "xyes"; then |
307 | + AC_DEFINE(ENABLE_C_WARNINGS, 1, [show warnings from the C compiler]) |
308 | +fi |
309 | + |
310 | +AM_CONDITIONAL(ENABLE_C_WARNINGS, test "$enable_c_warnings" = "yes") |
311 | + |
312 | +##################################################### |
313 | +# local install for distcheck and stand-alone running |
314 | +##################################################### |
315 | +with_localinstall="no" |
316 | +AC_ARG_ENABLE(localinstall, |
317 | + AS_HELP_STRING([--enable-localinstall], |
318 | + [Install all of the files locally instead of in system directories (for distcheck)]), |
319 | + with_localinstall=$enableval, |
320 | + with_localinstall=no) |
321 | + |
322 | +AM_CONDITIONAL([HAVE_LOCALINSTALL], [test "x$with_localinstall" = "xyes"]) |
323 | + |
324 | +#################################################################### |
325 | +# Headless tests |
326 | +#################################################################### |
327 | +AC_ARG_ENABLE([headless-tests], |
328 | + AS_HELP_STRING([--enable-headless-tests=@<:@no/yes@:>@],[enable headless test suite (requires Xvfb) @<:@default=no@:>@]),, |
329 | + [enable_headless_tests=no]) |
330 | + |
331 | +AM_CONDITIONAL([ENABLE_HEADLESS_TESTS],[test "x$enable_headless_tests" != "xno"]) |
332 | + |
333 | +if test "x$enable_headless_tests" = "xyes"; then |
334 | + AC_PATH_PROG([XVFB],[xvfb-run]) |
335 | +fi |
336 | + |
337 | +##################################################### |
338 | +# local install for distcheck and stand-alone running |
339 | +##################################################### |
340 | +with_localinstall="no" |
341 | +AC_ARG_ENABLE(localinstall, |
342 | + AS_HELP_STRING([--enable-localinstall], |
343 | + [Install all of the files locally instead of in system directories (for distcheck)]), |
344 | + with_localinstall=$enableval, |
345 | + with_localinstall=no) |
346 | + |
347 | +AM_CONDITIONAL([HAVE_LOCALINSTALL], [test "x$with_localinstall" = "xyes"]) |
348 | + |
349 | +##################################################### |
350 | +# Expand variables needed for config.vala |
351 | +##################################################### |
352 | +AS_AC_EXPAND(PREFIX, $prefix) |
353 | +AC_SUBST(PREFIX) |
354 | + |
355 | +AS_AC_EXPAND(DATADIR, $datarootdir) |
356 | +AC_SUBST(DATADIR) |
357 | + |
358 | +##################################################### |
359 | +# Look for dbus service dir |
360 | +##################################################### |
361 | +if test "x$with_localinstall" = "xyes"; then |
362 | + DBUSSERVICEDIR="${datadir}/dbus-1/services/" |
363 | +else |
364 | + DBUSSERVICEDIR=`$PKG_CONFIG --variable=session_bus_services_dir dbus-1` |
365 | +fi |
366 | +AC_SUBST(DBUSSERVICEDIR) |
367 | + |
368 | +##################################################### |
369 | +# Look for correct Lenses dir |
370 | +##################################################### |
371 | +if test "x$with_localinstall" = "xyes"; then |
372 | + LENSESDIR="${datadir}/unity/lenses" |
373 | +else |
374 | + LENSESDIR=`$PKG_CONFIG --variable=lensesdir unity` |
375 | +fi |
376 | +AC_SUBST(LENSESDIR) |
377 | + |
378 | +############################################# |
379 | +# GSettings macros |
380 | +############################################# |
381 | + |
382 | +GLIB_GSETTINGS |
383 | + |
384 | +##################################################### |
385 | +# Create the Makefiles |
386 | +##################################################### |
387 | +AC_CONFIG_FILES([ |
388 | + Makefile |
389 | + data/Makefile |
390 | + data/video.lens.in |
391 | + src/Makefile |
392 | + po/Makefile.in |
393 | + src/config.vala |
394 | + tests/unit/config-tests.vala |
395 | + tests/unit/Makefile |
396 | + vapi/Makefile |
397 | +]) |
398 | +AC_OUTPUT |
399 | + |
400 | +##################################################### |
401 | +# Output the results |
402 | +##################################################### |
403 | +AC_MSG_NOTICE([ |
404 | + |
405 | + Unity Video Lens Daemon $VERSION |
406 | + ---------------------------------- |
407 | + |
408 | + Prefix : ${prefix} |
409 | + |
410 | + Local install : ${with_localinstall} |
411 | + |
412 | + Extra CFlags : ${CPPFLAGS} $MAINTAINER_CFLAGS |
413 | + Extra ValaFlags : ${CPPFLAGS} $MAINTAINER_VALAFLAGS |
414 | + |
415 | + Lenses Directory: ${LENSESDIR} |
416 | + |
417 | + Testing |
418 | + Headless tests : ${enable_headless_tests} |
419 | + Coverage reporting : ${use_gcov} |
420 | + |
421 | +]) |
422 | |
423 | === added directory 'data' |
424 | === added file 'data/Makefile.am' |
425 | --- data/Makefile.am 1970-01-01 00:00:00 +0000 |
426 | +++ data/Makefile.am 2013-02-19 17:19:01 +0000 |
427 | @@ -0,0 +1,27 @@ |
428 | +dbus_servicesdir = $(DBUSSERVICEDIR) |
429 | +service_in_files = \ |
430 | + unity-scope-video-remote.service.in \ |
431 | + unity-lens-video.service.in \ |
432 | + $(NULL) |
433 | + |
434 | +dbus_services_DATA = $(service_in_files:.service.in=.service) |
435 | + |
436 | +%.service: %.service.in |
437 | + $(AM_V_GEN)sed -e "s|\@pkglibexecdir\@|$(pkglibexecdir)|" $< > $@ |
438 | + |
439 | +lens_in_files = video.lens.in video-remote.scope |
440 | +lensdir = $(LENSESDIR)/video |
441 | +lens_DATA = $(lens_in_files:.lens.in=.lens) |
442 | + |
443 | +@INTLTOOL_LENS_RULE@ |
444 | + |
445 | +EXTRA_DIST = \ |
446 | + $(service_in_files) \ |
447 | + $(lens_in_files) \ |
448 | + $(NULL) |
449 | + |
450 | +CLEANFILES = \ |
451 | + unity-scope-video-remote.service \ |
452 | + unity-lens-video.service \ |
453 | + video.lens \ |
454 | + $(NULL) |
455 | |
456 | === renamed file 'unity-lens-video.desktop' => 'data/unity-lens-video.desktop' |
457 | === renamed file 'unity-lens-video.png' => 'data/unity-lens-video.png' |
458 | === renamed file 'unity-lens-video.service' => 'data/unity-lens-video.service.in' |
459 | --- unity-lens-video.service 2012-02-02 19:43:07 +0000 |
460 | +++ data/unity-lens-video.service.in 2013-02-19 17:19:01 +0000 |
461 | @@ -1,3 +1,3 @@ |
462 | [D-BUS Service] |
463 | Name=net.launchpad.lens.video |
464 | -Exec=/usr/lib/unity-lens-video/unity-lens-video |
465 | +Exec=@pkglibexecdir@/unity-video-lens-daemon |
466 | |
467 | === added file 'data/unity-scope-video-remote.service.in' |
468 | --- data/unity-scope-video-remote.service.in 1970-01-01 00:00:00 +0000 |
469 | +++ data/unity-scope-video-remote.service.in 2013-02-19 17:19:01 +0000 |
470 | @@ -0,0 +1,3 @@ |
471 | +[D-BUS Service] |
472 | +Name=net.launchpad.scope.RemoteVideos |
473 | +Exec=@pkglibexecdir@/unity-scope-video-remote |
474 | |
475 | === added file 'data/video-remote.scope' |
476 | --- data/video-remote.scope 1970-01-01 00:00:00 +0000 |
477 | +++ data/video-remote.scope 2013-02-19 17:19:01 +0000 |
478 | @@ -0,0 +1,3 @@ |
479 | +[Scope] |
480 | +DBusName=net.launchpad.scope.RemoteVideos |
481 | +DBusPath=/net/launchpad/scope/remotevideos |
482 | |
483 | === renamed file 'video.lens.in' => 'data/video.lens.in.in' |
484 | === modified file 'debian/changelog' |
485 | --- debian/changelog 2012-12-05 09:29:20 +0000 |
486 | +++ debian/changelog 2013-02-19 17:19:01 +0000 |
487 | @@ -1,3 +1,14 @@ |
488 | +unity-lens-video (0.3.14daily12.12.05-0ubuntu2) UNRELEASED; urgency=low |
489 | + |
490 | + * debian/control: |
491 | + - Update Build-Depends for Vala rewrite |
492 | + - Add unity-scope-video-remote to this source package |
493 | + * debian/rules: |
494 | + - Use dh-autoreconf |
495 | + - Enable xvfb tests |
496 | + |
497 | + -- Michael Terry <mterry@ubuntu.com> Tue, 19 Feb 2013 17:10:20 +0000 |
498 | + |
499 | unity-lens-video (0.3.14daily12.12.05-0ubuntu1) raring; urgency=low |
500 | |
501 | [ Michael Terry ] |
502 | |
503 | === modified file 'debian/control' |
504 | --- debian/control 2012-11-08 04:06:55 +0000 |
505 | +++ debian/control 2013-02-19 17:19:01 +0000 |
506 | @@ -2,28 +2,41 @@ |
507 | Section: gnome |
508 | Priority: optional |
509 | Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
510 | -XSBC-Original-Maintainer: David Calle <davidc@framli.eu> |
511 | -Build-Depends: debhelper (>= 9), |
512 | - python, |
513 | - python-distutils-extra, |
514 | +Build-Depends: debhelper (>= 9), |
515 | + dh-autoreconf, |
516 | dh-translations, |
517 | -Standards-Version: 3.9.3 |
518 | -Homepage: https://launchpad.net/unity-lens-videos |
519 | + gnome-common, |
520 | + libdee-dev (>= 1.0.7), |
521 | + libgee-dev, |
522 | + libglib2.0-dev (>= 2.27), |
523 | + libjson-glib-dev, |
524 | + libsoup-gnome2.4-dev, |
525 | + libunity-dev (>= 6.90.2), |
526 | + libzeitgeist-dev (>= 0.3.8), |
527 | + valac-0.18, |
528 | + xvfb, |
529 | +Standards-Version: 3.9.4 |
530 | +Homepage: https://launchpad.net/unity-lens-video |
531 | # If you aren't a member of ~unity-team but need to upload packaging changes, |
532 | # just go ahead. ~unity-team will notice and sync up the code again. |
533 | -Vcs-Bzr: https://code.launchpad.net/~unity-team/unity-lens-videos/trunk |
534 | +Vcs-Bzr: https://code.launchpad.net/~unity-team/unity-lens-video/trunk |
535 | |
536 | Package: unity-lens-video |
537 | -Architecture: all |
538 | +Architecture: any |
539 | Depends: ${misc:Depends}, |
540 | - ${python:Depends}, |
541 | - gir1.2-unity-5.0, |
542 | - gir1.2-dee-1.0, |
543 | - gir1.2-glib-2.0, |
544 | - python-zeitgeist, |
545 | + ${shlibs:Depends}, |
546 | unity-lens-music (>= 6.6.0), |
547 | Recommends: unity-scope-video-remote |
548 | Breaks: unity (<< 6.0.0), |
549 | Description: Unity Video lens |
550 | A plugin to search videos in the Dash. |
551 | |
552 | +Package: unity-scope-video-remote |
553 | +Architecture: any |
554 | +Depends: ${misc:Depends}, |
555 | + ${shlibs:Depends}, |
556 | + gvfs-bin, |
557 | + unity-lens-video, |
558 | +Enhances: unity-lens-video |
559 | +Description: Remote videos engine |
560 | + This scope adds a remote videos search engine to the Video lens. |
561 | |
562 | === modified file 'debian/copyright' |
563 | --- debian/copyright 2012-02-14 14:21:28 +0000 |
564 | +++ debian/copyright 2013-02-19 17:19:01 +0000 |
565 | @@ -1,15 +1,13 @@ |
566 | -Format: http://dep.debian.net/deps/dep5 |
567 | -Source: https://launchpad.net/unity-lens-videos |
568 | -Upstream-Name: unity-lens-video |
569 | -Upstream-Contact: David Calle <davidc@framli.eu> |
570 | +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ |
571 | +Source: https://launchpad.net/unity-lens-video |
572 | +Upstream-Name: Unity Video Lens |
573 | |
574 | Files: * |
575 | -Copyright: 2011 David Calle <davidc@framli.eu> |
576 | -License: GPL-3.0+ |
577 | +Copyright: 2011-2013 Canonical Ltd |
578 | +License: GPL-3 |
579 | This program is free software: you can redistribute it and/or modify |
580 | it under the terms of the GNU General Public License as published by |
581 | - the Free Software Foundation, either version 3 of the License, or |
582 | - (at your option) any later version. |
583 | + the Free Software Foundation, version 3 of the License. |
584 | . |
585 | This program is distributed in the hope that it will be useful, |
586 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
587 | |
588 | === modified file 'debian/rules' |
589 | --- debian/rules 2012-11-08 04:06:55 +0000 |
590 | +++ debian/rules 2013-02-19 17:19:01 +0000 |
591 | @@ -1,9 +1,14 @@ |
592 | #!/usr/bin/make -f |
593 | # -*- makefile -*- |
594 | |
595 | - |
596 | %: |
597 | - dh $@ --with python2,translations |
598 | + dh $@ --with autoreconf,translations |
599 | + |
600 | +override_dh_autoreconf: |
601 | + NOCONFIGURE=1 dh_autoreconf ./autogen.sh |
602 | + |
603 | +override_dh_auto_configure: |
604 | + dh_auto_configure -- --enable-headless-tests |
605 | |
606 | override_dh_install: |
607 | dh_install --fail-missing |
608 | |
609 | === added file 'debian/unity-lens-video.install' |
610 | --- debian/unity-lens-video.install 1970-01-01 00:00:00 +0000 |
611 | +++ debian/unity-lens-video.install 2013-02-19 17:19:01 +0000 |
612 | @@ -0,0 +1,3 @@ |
613 | +/usr/lib/*/unity-lens-video/unity-video-lens-daemon |
614 | +/usr/share/dbus-1/services/unity-lens-video.service |
615 | +/usr/share/unity/lenses/video/video.lens |
616 | |
617 | === added file 'debian/unity-scope-video-remote.install' |
618 | --- debian/unity-scope-video-remote.install 1970-01-01 00:00:00 +0000 |
619 | +++ debian/unity-scope-video-remote.install 2013-02-19 17:19:01 +0000 |
620 | @@ -0,0 +1,3 @@ |
621 | +/usr/lib/*/unity-lens-video/unity-scope-video-remote |
622 | +/usr/share/dbus-1/services/unity-scope-video-remote.service |
623 | +/usr/share/unity/lenses/video/video-remote.scope |
624 | |
625 | === modified file 'debian/watch' |
626 | --- debian/watch 2012-03-21 21:41:31 +0000 |
627 | +++ debian/watch 2013-02-19 17:19:01 +0000 |
628 | @@ -1,2 +1,2 @@ |
629 | version=3 |
630 | -http://launchpad.net/unity-lens-videos/+download .*/unity-lens-video-([0-9.]+)\.tar\.gz |
631 | +http://launchpad.net/unity-lens-video/+download .*/unity-lens-video-([0-9.]+)\.tar\.gz |
632 | |
633 | === added directory 'm4' |
634 | === added file 'm4/gcov.m4' |
635 | --- m4/gcov.m4 1970-01-01 00:00:00 +0000 |
636 | +++ m4/gcov.m4 2013-02-19 17:19:01 +0000 |
637 | @@ -0,0 +1,86 @@ |
638 | +# Checks for existence of coverage tools: |
639 | +# * gcov |
640 | +# * lcov |
641 | +# * genhtml |
642 | +# * gcovr |
643 | +# |
644 | +# Sets ac_cv_check_gcov to yes if tooling is present |
645 | +# and reports the executables to the variables LCOV, GCOVR and GENHTML. |
646 | +AC_DEFUN([AC_TDD_GCOV], |
647 | +[ |
648 | + AC_ARG_ENABLE(gcov, |
649 | + AS_HELP_STRING([--enable-gcov], |
650 | + [enable coverage testing with gcov]), |
651 | + [use_gcov=$enableval], [use_gcov=no]) |
652 | + |
653 | + if test "x$use_gcov" = "xyes"; then |
654 | + # we need gcc: |
655 | + if test "$GCC" != "yes"; then |
656 | + AC_MSG_ERROR([GCC is required for --enable-gcov]) |
657 | + fi |
658 | + |
659 | + # Check if ccache is being used |
660 | + AC_CHECK_PROG(SHTOOL, shtool, shtool) |
661 | + case `$SHTOOL path $CC` in |
662 | + *ccache*[)] gcc_ccache=yes;; |
663 | + *[)] gcc_ccache=no;; |
664 | + esac |
665 | + |
666 | + if test "$gcc_ccache" = "yes" && (test -z "$CCACHE_DISABLE" || test "$CCACHE_DISABLE" != "1"); then |
667 | + AC_MSG_ERROR([ccache must be disabled when --enable-gcov option is used. You can disable ccache by setting environment variable CCACHE_DISABLE=1.]) |
668 | + fi |
669 | + |
670 | + lcov_version_list="1.6 1.7 1.8 1.9" |
671 | + AC_CHECK_PROG(LCOV, lcov, lcov) |
672 | + AC_CHECK_PROG(GENHTML, genhtml, genhtml) |
673 | + |
674 | + if test "$LCOV"; then |
675 | + AC_CACHE_CHECK([for lcov version], glib_cv_lcov_version, [ |
676 | + glib_cv_lcov_version=invalid |
677 | + lcov_version=`$LCOV -v 2>/dev/null | $SED -e 's/^.* //'` |
678 | + for lcov_check_version in $lcov_version_list; do |
679 | + if test "$lcov_version" = "$lcov_check_version"; then |
680 | + glib_cv_lcov_version="$lcov_check_version (ok)" |
681 | + fi |
682 | + done |
683 | + ]) |
684 | + else |
685 | + lcov_msg="To enable code coverage reporting you must have one of the following lcov versions installed: $lcov_version_list" |
686 | + AC_MSG_ERROR([$lcov_msg]) |
687 | + fi |
688 | + |
689 | + case $glib_cv_lcov_version in |
690 | + ""|invalid[)] |
691 | + lcov_msg="You must have one of the following versions of lcov: $lcov_version_list (found: $lcov_version)." |
692 | + AC_MSG_ERROR([$lcov_msg]) |
693 | + LCOV="exit 0;" |
694 | + ;; |
695 | + esac |
696 | + |
697 | + if test -z "$GENHTML"; then |
698 | + AC_MSG_ERROR([Could not find genhtml from the lcov package]) |
699 | + fi |
700 | + |
701 | + ac_cv_check_gcov=yes |
702 | + ac_cv_check_lcov=yes |
703 | + |
704 | + # Remove all optimization flags from CFLAGS |
705 | + changequote({,}) |
706 | + CFLAGS=`echo "$CFLAGS" | $SED -e 's/-O[0-9]*//g'` |
707 | + changequote([,]) |
708 | + |
709 | + # Add the special gcc flags |
710 | + COVERAGE_CFLAGS="-O0 -fprofile-arcs -ftest-coverage" |
711 | + COVERAGE_CXXFLAGS="-O0 -fprofile-arcs -ftest-coverage" |
712 | + COVERAGE_LDFLAGS="-lgcov" |
713 | + |
714 | + # Check availability of gcovr |
715 | + AC_CHECK_PROG(GCOVR, gcovr, gcovr) |
716 | + if test -z "$GCOVR"; then |
717 | + ac_cv_check_gcovr=no |
718 | + else |
719 | + ac_cv_check_gcovr=yes |
720 | + fi |
721 | + |
722 | +fi |
723 | +]) # AC_TDD_GCOV |
724 | |
725 | === modified file 'po/POTFILES.in' |
726 | --- po/POTFILES.in 2012-02-22 10:47:03 +0000 |
727 | +++ po/POTFILES.in 2013-02-19 17:19:01 +0000 |
728 | @@ -1,3 +1,6 @@ |
729 | [encoding: UTF-8] |
730 | -src/unity-lens-video |
731 | -[type: gettext/ini]video.lens.in |
732 | +src/main.vala |
733 | +src/remote-video-main.vala |
734 | +src/ubuntu-video-search.vala |
735 | +tests/unit/test-ubuntu-video-search.vala |
736 | +[type: gettext/ini]data/video.lens.in |
737 | |
738 | === added file 'po/POTFILES.skip' |
739 | --- po/POTFILES.skip 1970-01-01 00:00:00 +0000 |
740 | +++ po/POTFILES.skip 2013-02-19 17:19:01 +0000 |
741 | @@ -0,0 +1,7 @@ |
742 | +src/daemon.c |
743 | +src/scope.c |
744 | +src/remote-scope.c |
745 | +src/ubuntu-video-search.c |
746 | +tests/unit/test-ubuntu-video-search.c |
747 | +tests/unit/ubuntu-video-search.c |
748 | +tests/unit/remote-scope.c |
749 | |
750 | === removed file 'po/unity-lens-video.pot' |
751 | --- po/unity-lens-video.pot 2012-09-14 09:12:09 +0000 |
752 | +++ po/unity-lens-video.pot 1970-01-01 00:00:00 +0000 |
753 | @@ -1,70 +0,0 @@ |
754 | -# SOME DESCRIPTIVE TITLE. |
755 | -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER |
756 | -# This file is distributed under the same license as the PACKAGE package. |
757 | -# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. |
758 | -# |
759 | -#, fuzzy |
760 | -msgid "" |
761 | -msgstr "" |
762 | -"Project-Id-Version: PACKAGE VERSION\n" |
763 | -"Report-Msgid-Bugs-To: \n" |
764 | -"POT-Creation-Date: 2012-09-14 10:03+0100\n" |
765 | -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
766 | -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
767 | -"Language-Team: LANGUAGE <LL@li.org>\n" |
768 | -"Language: \n" |
769 | -"MIME-Version: 1.0\n" |
770 | -"Content-Type: text/plain; charset=CHARSET\n" |
771 | -"Content-Transfer-Encoding: 8bit\n" |
772 | - |
773 | -#: ../src/unity-lens-video:49 ../src/unity-lens-video:56 |
774 | -msgid "My Videos" |
775 | -msgstr "" |
776 | - |
777 | -#: ../src/unity-lens-video:50 |
778 | -msgid "Online" |
779 | -msgstr "" |
780 | - |
781 | -#: ../src/unity-lens-video:51 ../video.lens.in.h:1 |
782 | -msgid "Videos" |
783 | -msgstr "" |
784 | - |
785 | -#: ../src/unity-lens-video:52 |
786 | -msgid "Recently Viewed" |
787 | -msgstr "" |
788 | - |
789 | -#: ../src/unity-lens-video:53 |
790 | -msgid "More suggestions" |
791 | -msgstr "" |
792 | - |
793 | -#: ../src/unity-lens-video:54 |
794 | -msgid "Search Videos" |
795 | -msgstr "" |
796 | - |
797 | -#: ../src/unity-lens-video:55 |
798 | -msgid "Sources" |
799 | -msgstr "" |
800 | - |
801 | -#: ../src/unity-lens-video:173 |
802 | -msgid "Format" |
803 | -msgstr "" |
804 | - |
805 | -#: ../src/unity-lens-video:174 |
806 | -msgid "Dimensions" |
807 | -msgstr "" |
808 | - |
809 | -#: ../src/unity-lens-video:175 |
810 | -msgid "Size" |
811 | -msgstr "" |
812 | - |
813 | -#: ../src/unity-lens-video:178 |
814 | -msgid "Show in Folder" |
815 | -msgstr "" |
816 | - |
817 | -#: ../src/unity-lens-video:181 |
818 | -msgid "Play" |
819 | -msgstr "" |
820 | - |
821 | -#: ../video.lens.in.h:2 |
822 | -msgid "Search local videos" |
823 | -msgstr "" |
824 | |
825 | === removed file 'setup.cfg' |
826 | --- setup.cfg 2012-02-22 10:48:53 +0000 |
827 | +++ setup.cfg 1970-01-01 00:00:00 +0000 |
828 | @@ -1,10 +0,0 @@ |
829 | -[build] |
830 | -i18n=True |
831 | - |
832 | -[build_i18n] |
833 | -domain=unity-lens-video |
834 | -desktop_files=[ ("share/unity/lenses/video", |
835 | - ("video.lens.in",) |
836 | - ) |
837 | - ] |
838 | - |
839 | |
840 | === removed file 'setup.py' |
841 | --- setup.py 2012-10-16 12:12:46 +0000 |
842 | +++ setup.py 1970-01-01 00:00:00 +0000 |
843 | @@ -1,18 +0,0 @@ |
844 | -#!/usr/bin/env python |
845 | -# |
846 | -from distutils.core import setup |
847 | -from DistUtilsExtra.command import * |
848 | - |
849 | -setup(name="unity-lens-video", |
850 | - version="0.3.14", |
851 | - author="David Calle", |
852 | - author_email="davidc@framli.eu", |
853 | - url="http://launchpad.net/~davidc3", |
854 | - license="GNU General Public License (GPL)", |
855 | - data_files=[ |
856 | - ('lib/unity-lens-video', ['src/unity-lens-video']), |
857 | - ('share/dbus-1/services', ['unity-lens-video.service']), |
858 | - ('share/applications', ['unity-lens-video.desktop']), |
859 | - ('share/pixmaps', ['unity-lens-video.png']), |
860 | - ], cmdclass={"build": build_extra.build_extra, |
861 | - "build_i18n": build_i18n.build_i18n,}) |
862 | |
863 | === added file 'src/Makefile.am' |
864 | --- src/Makefile.am 1970-01-01 00:00:00 +0000 |
865 | +++ src/Makefile.am 2013-02-19 17:19:01 +0000 |
866 | @@ -0,0 +1,133 @@ |
867 | +NULL = |
868 | +BUILT_SOURCES = |
869 | +CLEANFILES = |
870 | +EXTRA_DIST = |
871 | + |
872 | +DATADIR = $(datadir) |
873 | + |
874 | +AM_CPPFLAGS = $(COVERAGE_CFLAGS) |
875 | +AM_LDFLAGS = $(COVERAGE_LDFLAGS) |
876 | + |
877 | +pkglibexec_PROGRAMS = \ |
878 | + unity-video-lens-daemon \ |
879 | + unity-scope-video-remote \ |
880 | + $(NULL) |
881 | + |
882 | +unity_video_lens_daemon_CPPFLAGS = \ |
883 | + -DDATADIR=\"$(DATADIR)\" \ |
884 | + -DPKGDATADIR=\"$(PKGDATADIR)\" \ |
885 | + -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \ |
886 | + -DG_LOG_DOMAIN=\"unity-video-lens-daemon\" \ |
887 | + $(LENS_DAEMON_CFLAGS) \ |
888 | + $(MAINTAINER_CFLAGS) \ |
889 | + -I$(srcdir) \ |
890 | + $(NULL) |
891 | + |
892 | +unity_scope_video_remote_CPPFLAGS = \ |
893 | + -DDATADIR=\"$(DATADIR)\" \ |
894 | + -DPKGDATADIR=\"$(PKGDATADIR)\" \ |
895 | + -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \ |
896 | + -DG_LOG_DOMAIN=\"unity-scope-video-remote\" \ |
897 | + $(LENS_DAEMON_CFLAGS) \ |
898 | + $(MAINTAINER_CFLAGS) \ |
899 | + -I$(srcdir) \ |
900 | + $(NULL) |
901 | + |
902 | +unity_video_lens_daemon_VALAFLAGS = \ |
903 | + -C \ |
904 | + --pkg dee-1.0 \ |
905 | + --pkg unity \ |
906 | + --pkg unity-extras \ |
907 | + --pkg gio-2.0 \ |
908 | + --pkg gio-unix-2.0 \ |
909 | + --pkg glib-2.0 \ |
910 | + --pkg zeitgeist-1.0 \ |
911 | + --vapidir $(srcdir) \ |
912 | + --vapidir $(top_srcdir)/vapi \ |
913 | + --target-glib=2.26 \ |
914 | + $(MAINTAINER_VALAFLAGS) \ |
915 | + $(NULL) |
916 | + |
917 | +unity_scope_video_remote_VALAFLAGS = \ |
918 | + -C \ |
919 | + --pkg dee-1.0 \ |
920 | + --pkg unity \ |
921 | + --pkg unity-extras \ |
922 | + --pkg gio-2.0 \ |
923 | + --pkg gio-unix-2.0 \ |
924 | + --pkg glib-2.0 \ |
925 | + --pkg zeitgeist-1.0 \ |
926 | + --pkg libsoup-gnome-2.4 \ |
927 | + --pkg libsoup-2.4 \ |
928 | + --pkg json-glib-1.0 \ |
929 | + --vapidir $(srcdir) \ |
930 | + --vapidir $(top_srcdir)/vapi \ |
931 | + --target-glib=2.26 \ |
932 | + $(MAINTAINER_VALAFLAGS) \ |
933 | + $(NULL) |
934 | + |
935 | + |
936 | +unity_video_lens_daemon_LDADD = \ |
937 | + $(LENS_DAEMON_LIBS) \ |
938 | + $(NULL) |
939 | + |
940 | +unity_scope_video_remote_LDADD = \ |
941 | + $(LENS_DAEMON_LIBS) \ |
942 | + $(NULL) |
943 | + |
944 | +unity_video_lens_daemon_VALASOURCES = \ |
945 | + blacklist-tracker.vala \ |
946 | + daemon.vala \ |
947 | + main.vala \ |
948 | + locate.vala \ |
949 | + scope.vala \ |
950 | + config.vala \ |
951 | + thumbnailer.vala \ |
952 | + utils.vala \ |
953 | + video-file.vala \ |
954 | + $(NULL) |
955 | + |
956 | +unity_scope_video_remote_VALASOURCES = \ |
957 | + config.vala \ |
958 | + remote-scope-globals.vala \ |
959 | + remote-video-main.vala \ |
960 | + remote-scope.vala \ |
961 | + remote-uri.vala \ |
962 | + ubuntu-video-search.vala \ |
963 | + utils.vala \ |
964 | + video-file.vala \ |
965 | + $(NULL) |
966 | + |
967 | +unity_video_lens_daemon_SOURCES = \ |
968 | + $(unity_video_lens_daemon_VALASOURCES:.vala=.c) \ |
969 | + $(NULL) |
970 | + |
971 | +unity_scope_video_remote_SOURCES = \ |
972 | + $(unity_scope_video_remote_VALASOURCES:.vala=.c) \ |
973 | + $(NULL) |
974 | + |
975 | +BUILT_SOURCES += \ |
976 | + unity_video_lens_daemon.vala.stamp \ |
977 | + unity_scope_video_remote.vala.stamp \ |
978 | + $(NULL) |
979 | + |
980 | +EXTRA_DIST += \ |
981 | + unity_video_lens_daemon.vala.stamp \ |
982 | + unity_scope_video_remote.vala.stamp \ |
983 | + $(unity_video_lens_daemon_VALASOURCES) \ |
984 | + $(unity_scope_video_remote_VALASOURCES) \ |
985 | + $(NULL) |
986 | + |
987 | +unity_video_lens_daemon.vala.stamp: $(unity_video_lens_daemon_VALASOURCES) |
988 | + $(AM_V_GEN) $(VALAC) $(unity_video_lens_daemon_VALAFLAGS) $^ |
989 | + @touch $@ |
990 | + |
991 | +unity_scope_video_remote.vala.stamp: $(unity_scope_video_remote_VALASOURCES) |
992 | + $(AM_V_GEN) $(VALAC) $(unity_scope_video_remote_VALAFLAGS) $^ |
993 | + @touch $@ |
994 | + |
995 | +CLEANFILES += \ |
996 | + *.stamp \ |
997 | + $(unity_video_lens_daemon_VALASOURCES:.vala=.c) \ |
998 | + $(unity_scope_video_remote_VALASOURCES:.vala=.c) \ |
999 | + $(NULL) |
1000 | |
1001 | === added file 'src/blacklist-tracker.vala' |
1002 | --- src/blacklist-tracker.vala 1970-01-01 00:00:00 +0000 |
1003 | +++ src/blacklist-tracker.vala 2013-02-19 17:19:01 +0000 |
1004 | @@ -0,0 +1,128 @@ |
1005 | +/* |
1006 | + * Copyright (C) 2012 Canonical Ltd |
1007 | + * |
1008 | + * This program is free software: you can redistribute it and/or modify |
1009 | + * it under the terms of the GNU General Public License version 3 as |
1010 | + * published by the Free Software Foundation. |
1011 | + * |
1012 | + * This program is distributed in the hope that it will be useful, |
1013 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1014 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1015 | + * GNU General Public License for more details. |
1016 | + * |
1017 | + * You should have received a copy of the GNU General Public License |
1018 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1019 | + * |
1020 | + * Authored by Michal Hruby <michal.hruby@canonical.com> |
1021 | + * |
1022 | + */ |
1023 | +using Zeitgeist; |
1024 | +using Config; |
1025 | +using Gee; |
1026 | + |
1027 | +namespace Unity.VideoLens |
1028 | +{ |
1029 | + /* Libzeitgeist currently doesn't expose any blacklist API, so we need to |
1030 | + * use direct dbus calls */ |
1031 | + [DBus (name = "org.gnome.zeitgeist.Blacklist")] |
1032 | + public interface Blacklist : Object |
1033 | + { |
1034 | + [DBus (signature = "a{s(asaasay)}")] |
1035 | + public abstract async Variant get_templates () throws Error; |
1036 | + public signal void template_added (string template_id, |
1037 | + [DBus (signature = "(asassay)")] Variant template); |
1038 | + public signal void template_removed (string template_id, |
1039 | + [DBus (signature = "(asassay)")] Variant template); |
1040 | + } |
1041 | + |
1042 | + public class BlacklistTracker : Object |
1043 | + { |
1044 | + private HashTable<string, Event> event_templates; |
1045 | + private Blacklist blacklist_proxy; |
1046 | + |
1047 | + private Set<string> blacklisted_uris; |
1048 | + private bool cached_uris_dirty; |
1049 | + |
1050 | + construct |
1051 | + { |
1052 | + event_templates = new HashTable<string, Event> (str_hash, str_equal); |
1053 | + blacklisted_uris = new HashSet<string> (); |
1054 | + |
1055 | + Bus.get_proxy<Blacklist> (BusType.SESSION, "org.gnome.zeitgeist.Engine", |
1056 | + "/org/gnome/zeitgeist/blacklist", 0, null, (src, res) => |
1057 | + { |
1058 | + try |
1059 | + { |
1060 | + blacklist_proxy = (src as DBusConnection).get_proxy<Blacklist>.end (res); |
1061 | + fetch_blacklists (); |
1062 | + } |
1063 | + catch (GLib.Error err) |
1064 | + { |
1065 | + warning ("%s", err.message); |
1066 | + } |
1067 | + }); |
1068 | + } |
1069 | + |
1070 | + private async void fetch_blacklists () |
1071 | + { |
1072 | + blacklist_proxy.template_added.connect (this.template_added); |
1073 | + blacklist_proxy.template_removed.connect (this.template_removed); |
1074 | + Variant all_templates = yield blacklist_proxy.get_templates (); |
1075 | + |
1076 | + VariantIter iter = new VariantIter (all_templates); |
1077 | + string template_id; |
1078 | + Variant event_variant; |
1079 | + while (iter.next ("{s@(asaasay)}", out template_id, out event_variant)) |
1080 | + { |
1081 | + Event e = new Event.from_variant (event_variant); |
1082 | + event_templates[template_id] = e; |
1083 | + } |
1084 | + cached_uris_dirty = true; |
1085 | + } |
1086 | + |
1087 | + private void template_added (string id, Variant template) |
1088 | + { |
1089 | + Event e = new Event.from_variant (template); |
1090 | + event_templates[id] = e; |
1091 | + cached_uris_dirty = true; |
1092 | + } |
1093 | + |
1094 | + private void template_removed (string id, Variant template) |
1095 | + { |
1096 | + event_templates.remove (id); |
1097 | + cached_uris_dirty = true; |
1098 | + } |
1099 | + |
1100 | + private void update_uris () |
1101 | + { |
1102 | + var iter = HashTableIter<string, Event> (event_templates); |
1103 | + unowned Event e; |
1104 | + while (iter.next (null, out e)) |
1105 | + { |
1106 | + if (e.num_subjects () > 0) |
1107 | + { |
1108 | + unowned Subject s = e.get_subject (0); |
1109 | + unowned string uri = s.get_uri (); |
1110 | + if (uri == null || uri == "") continue; |
1111 | + |
1112 | + if (uri.has_suffix ("*")) |
1113 | + { |
1114 | + blacklisted_uris.add (uri.substring (0, uri.length - 1)); |
1115 | + } |
1116 | + } |
1117 | + } |
1118 | + } |
1119 | + |
1120 | + public unowned Set<string> get_blacklisted_uris () |
1121 | + { |
1122 | + if (cached_uris_dirty) |
1123 | + { |
1124 | + blacklisted_uris.clear (); |
1125 | + update_uris (); |
1126 | + cached_uris_dirty = false; |
1127 | + } |
1128 | + return blacklisted_uris; |
1129 | + } |
1130 | + } |
1131 | + |
1132 | +} /* namespace */ |
1133 | |
1134 | === added file 'src/config.vala.in' |
1135 | --- src/config.vala.in 1970-01-01 00:00:00 +0000 |
1136 | +++ src/config.vala.in 2013-02-19 17:19:01 +0000 |
1137 | @@ -0,0 +1,16 @@ |
1138 | +namespace Config { |
1139 | + |
1140 | + const string PREFIX = "@prefix@"; |
1141 | + |
1142 | + const string DATADIR = "@DATADIR@"; |
1143 | + |
1144 | + const string PKGDATADIR = "@DATADIR@/unity"; |
1145 | + |
1146 | + const string BINDIR = "@prefix@/bin"; |
1147 | + |
1148 | + const string LOCALEDIR = "@DATADIR@/locale"; |
1149 | + |
1150 | + const string PACKAGE = "@PACKAGE@"; |
1151 | + |
1152 | + const string VERSION = "@VERSION@"; |
1153 | +} |
1154 | |
1155 | === added file 'src/daemon.vala' |
1156 | --- src/daemon.vala 1970-01-01 00:00:00 +0000 |
1157 | +++ src/daemon.vala 2013-02-19 17:19:01 +0000 |
1158 | @@ -0,0 +1,79 @@ |
1159 | +/* |
1160 | + * Copyright (C) 2012 Canonical Ltd |
1161 | + * |
1162 | + * This program is free software: you can redistribute it and/or modify |
1163 | + * it under the terms of the GNU General Public License version 3 as |
1164 | + * published by the Free Software Foundation. |
1165 | + * |
1166 | + * This program is distributed in the hope that it will be useful, |
1167 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1168 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1169 | + * GNU General Public License for more details. |
1170 | + * |
1171 | + * You should have received a copy of the GNU General Public License |
1172 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1173 | + * |
1174 | + * Authored by Pawel Stolowski <pawel.stolowski@canonical.com> |
1175 | + * based on python code by David Calle <davidc@framli.eu> |
1176 | + */ |
1177 | +using Config; |
1178 | + |
1179 | +namespace Unity.VideoLens { |
1180 | + |
1181 | + const string ICON_PATH = Config.DATADIR + "/icons/unity-icon-theme/places/svg/"; |
1182 | + |
1183 | + public class Daemon : GLib.Object |
1184 | + { |
1185 | + private Unity.Lens lens; |
1186 | + |
1187 | + construct |
1188 | + { |
1189 | + lens = new Unity.Lens("/net/launchpad/lens/video", "video"); |
1190 | + lens.search_in_global = true; |
1191 | + lens.search_hint = _("Search videos"); |
1192 | + lens.sources_display_name = _("Sources"); |
1193 | + lens.visible = true; |
1194 | + |
1195 | + populate_categories (); |
1196 | + populate_filters (); |
1197 | + |
1198 | + var video_scope = new VideoScope (); |
1199 | + lens.add_local_scope (video_scope); |
1200 | + |
1201 | + try { |
1202 | + lens.export (); |
1203 | + } catch (GLib.IOError e) { |
1204 | + stdout.printf ("error %s\n", e.message); |
1205 | + } |
1206 | + } |
1207 | + |
1208 | + private void populate_filters () |
1209 | + { |
1210 | + var filters = new GLib.List<Unity.Filter> (); |
1211 | + |
1212 | + /* TODO */ |
1213 | + |
1214 | + /* A filter */ |
1215 | + { |
1216 | + } |
1217 | + |
1218 | + /* Another filter */ |
1219 | + { |
1220 | + } |
1221 | + |
1222 | + lens.filters = filters; |
1223 | + } |
1224 | + |
1225 | + private void populate_categories () |
1226 | + { |
1227 | + var categories = new GLib.List<Unity.Category> (); |
1228 | + var icon_dir = File.new_for_path (ICON_PATH); |
1229 | + |
1230 | + categories.append(new Unity.Category (_("My Videos"), new FileIcon (icon_dir.get_child ("group-videos.svg")), Unity.CategoryRenderer.VERTICAL_TILE)); |
1231 | + categories.append(new Unity.Category (_("Online"), new FileIcon (icon_dir.get_child ("group-internet.svg")), Unity.CategoryRenderer.VERTICAL_TILE)); |
1232 | + categories.append(new Unity.Category (_("More suggestions"), new FileIcon (icon_dir.get_child ("group-treat-yourself.svg")), Unity.CategoryRenderer.VERTICAL_TILE)); |
1233 | + |
1234 | + lens.categories = categories; |
1235 | + } |
1236 | + } |
1237 | +} |
1238 | |
1239 | === added file 'src/locate.vala' |
1240 | --- src/locate.vala 1970-01-01 00:00:00 +0000 |
1241 | +++ src/locate.vala 2013-02-19 17:19:01 +0000 |
1242 | @@ -0,0 +1,112 @@ |
1243 | +/* |
1244 | + * Copyright (C) 2012 Canonical Ltd |
1245 | + * |
1246 | + * This program is free software: you can redistribute it and/or modify |
1247 | + * it under the terms of the GNU General Public License version 3 as |
1248 | + * published by the Free Software Foundation. |
1249 | + * |
1250 | + * This program is distributed in the hope that it will be useful, |
1251 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1252 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1253 | + * GNU General Public License for more details. |
1254 | + * |
1255 | + * You should have received a copy of the GNU General Public License |
1256 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1257 | + * |
1258 | + * Authored by Pawel Stolowski <pawel.stolowski@canonical.com> |
1259 | + * |
1260 | + */ |
1261 | + |
1262 | +namespace Unity.VideoLens |
1263 | +{ |
1264 | + public class Locate |
1265 | + { |
1266 | + /* Filter used by locate handling loop; return value of false means file will be ignored */ |
1267 | + public delegate bool LocateFilter (string path); |
1268 | + |
1269 | + private static const int MAX_RESULTS = 100; |
1270 | + private static const int MAX_LOCATE_RESULTS = 200; |
1271 | + |
1272 | + private string cache_db; |
1273 | + private string videos_folder; |
1274 | + public string locate_bin { get; set; default = "locate"; } |
1275 | + public string updatedb_bin { get; set; default = "updatedb"; } |
1276 | + |
1277 | + public Locate (string cache_dir, string videos_dir) |
1278 | + { |
1279 | + cache_db = cache_dir + "/videos.db"; |
1280 | + videos_folder = videos_dir; |
1281 | + } |
1282 | + |
1283 | + public Gee.ArrayList<VideoFile?>? run_locate (string search_string, Thumbnailer thumbnailer, LocateFilter? filter = null) |
1284 | + { |
1285 | + if (Utils.is_regular_file (cache_db)) |
1286 | + { |
1287 | + string stdout; |
1288 | + try |
1289 | + { |
1290 | + string query = locate_query_string (search_string); |
1291 | + GLib.Process.spawn_command_line_sync (@"$locate_bin -l $MAX_LOCATE_RESULTS -id $cache_db $query", out stdout); |
1292 | + var result_list = parse_locate_results (stdout, 100, thumbnailer, filter); |
1293 | + return result_list; |
1294 | + } |
1295 | + catch (GLib.Error e) |
1296 | + { |
1297 | + warning ("Failed to run locate: %s", e.message); |
1298 | + } |
1299 | + } |
1300 | + return null; |
1301 | + } |
1302 | + |
1303 | + internal string locate_query_string (string search_string) |
1304 | + { |
1305 | + return videos_folder + "*" + search_string.replace (" ", "*") + "*"; |
1306 | + } |
1307 | + |
1308 | + public void updatedb () |
1309 | + { |
1310 | + try |
1311 | + { |
1312 | + GLib.Process.spawn_command_line_async (@"$updatedb_bin -o $cache_db -l 0 -U $videos_folder"); |
1313 | + } |
1314 | + catch (GLib.Error e) |
1315 | + { |
1316 | + warning ("Can't create database, will retry: %s", e.message); |
1317 | + } |
1318 | + } |
1319 | + |
1320 | + public Gee.ArrayList<VideoFile?> parse_locate_results (string locate_output, int max, Thumbnailer thumbnailer, LocateFilter? filter) |
1321 | + { |
1322 | + var results_list = locate_output.split ("\n"); |
1323 | + var res = new Gee.ArrayList<VideoFile?> (); |
1324 | + int video_count = 0; |
1325 | + foreach (var video in results_list) |
1326 | + { |
1327 | + if (video_count >= MAX_RESULTS) |
1328 | + break; |
1329 | + |
1330 | + try |
1331 | + { |
1332 | + if (Utils.is_video (video) && (filter == null || filter (video))) |
1333 | + { |
1334 | + video_count++; |
1335 | + var name = Utils.get_name (video); |
1336 | + VideoFile video_file = VideoFile () |
1337 | + { |
1338 | + title = name, |
1339 | + lc_title = name.down (), |
1340 | + uri = "file://" + video, |
1341 | + icon = thumbnailer.get_icon (video) |
1342 | + }; |
1343 | + res.add (video_file); |
1344 | + } |
1345 | + } |
1346 | + catch (Error e) |
1347 | + { |
1348 | + // silently ignore |
1349 | + } |
1350 | + } |
1351 | + return res; |
1352 | + } |
1353 | + } |
1354 | +} |
1355 | \ No newline at end of file |
1356 | |
1357 | === added file 'src/main.vala' |
1358 | --- src/main.vala 1970-01-01 00:00:00 +0000 |
1359 | +++ src/main.vala 2013-02-19 17:19:01 +0000 |
1360 | @@ -0,0 +1,59 @@ |
1361 | +/* |
1362 | + * Copyright (C) 2012 Canonical Ltd |
1363 | + * |
1364 | + * This program is free software: you can redistribute it and/or modify |
1365 | + * it under the terms of the GNU General Public License version 3 as |
1366 | + * published by the Free Software Foundation. |
1367 | + * |
1368 | + * This program is distributed in the hope that it will be useful, |
1369 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1370 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1371 | + * GNU General Public License for more details. |
1372 | + * |
1373 | + * You should have received a copy of the GNU General Public License |
1374 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1375 | + */ |
1376 | + |
1377 | +using GLib; |
1378 | +using Config; |
1379 | + |
1380 | +namespace Unity.VideoLens { |
1381 | + |
1382 | + static Application? app = null; |
1383 | + static Daemon? daemon = null; |
1384 | + |
1385 | + public static int main (string[] args) |
1386 | + { |
1387 | + GLib.Environment.set_prgname ("unity-video-lens"); |
1388 | + |
1389 | + /* Sort up locale to get translations but also sorting and |
1390 | + * punctuation right */ |
1391 | + GLib.Intl.textdomain (Config.PACKAGE); |
1392 | + GLib.Intl.bindtextdomain (Config.PACKAGE, Config.LOCALEDIR); |
1393 | + GLib.Intl.bind_textdomain_codeset (Config.PACKAGE, "UTF-8"); |
1394 | + GLib.Intl.setlocale(GLib.LocaleCategory.ALL, ""); |
1395 | + |
1396 | + try |
1397 | + { |
1398 | + app = Extras.dbus_own_name ("net.launchpad.lens.video", () => |
1399 | + { |
1400 | + daemon = new Daemon (); |
1401 | + }); |
1402 | + } |
1403 | + catch (Error e) |
1404 | + { |
1405 | + warning ("Failed to start video lens daemon: %s\n", e.message); |
1406 | + return 1; |
1407 | + } |
1408 | + |
1409 | + if (app == null) |
1410 | + { |
1411 | + warning ("Another instance of the Unity Videos Lens " + |
1412 | + "already appears to be running.\nBailing out.\n"); |
1413 | + return 2; |
1414 | + } |
1415 | + |
1416 | + return app.run (); |
1417 | + } |
1418 | + |
1419 | +} /* namespace */ |
1420 | |
1421 | === added file 'src/remote-scope-globals.vala' |
1422 | --- src/remote-scope-globals.vala 1970-01-01 00:00:00 +0000 |
1423 | +++ src/remote-scope-globals.vala 2013-02-19 17:19:01 +0000 |
1424 | @@ -0,0 +1,25 @@ |
1425 | + |
1426 | +/* |
1427 | + * Copyright (C) 2012 Canonical Ltd |
1428 | + * |
1429 | + * This program is free software: you can redistribute it and/or modify |
1430 | + * it under the terms of the GNU General Public License version 3 as |
1431 | + * published by the Free Software Foundation. |
1432 | + * |
1433 | + * This program is distributed in the hope that it will be useful, |
1434 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1435 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1436 | + * GNU General Public License for more details. |
1437 | + * |
1438 | + * You should have received a copy of the GNU General Public License |
1439 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1440 | + * |
1441 | + * Authored by Pawel Stolowski <pawel.stolowski@canonical.com> |
1442 | + * |
1443 | + */ |
1444 | + |
1445 | +namespace Unity.VideoLens |
1446 | +{ |
1447 | + static int CAT_INDEX_ONLINE = 1; |
1448 | + static int CAT_INDEX_MORE = 2; |
1449 | +} |
1450 | \ No newline at end of file |
1451 | |
1452 | === added file 'src/remote-scope.vala' |
1453 | --- src/remote-scope.vala 1970-01-01 00:00:00 +0000 |
1454 | +++ src/remote-scope.vala 2013-02-19 17:19:01 +0000 |
1455 | @@ -0,0 +1,491 @@ |
1456 | +/* |
1457 | + * Copyright (C) 2012 Canonical Ltd |
1458 | + * |
1459 | + * This program is free software: you can redistribute it and/or modify |
1460 | + * it under the terms of the GNU General Public License version 3 as |
1461 | + * published by the Free Software Foundation. |
1462 | + * |
1463 | + * This program is distributed in the hope that it will be useful, |
1464 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1465 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1466 | + * GNU General Public License for more details. |
1467 | + * |
1468 | + * You should have received a copy of the GNU General Public License |
1469 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1470 | + * |
1471 | + * Authored by Pawel Stolowski <pawel.stolowski@canonical.com> |
1472 | + * based on python code by David Calle <davidc@framli.eu> |
1473 | + * |
1474 | + */ |
1475 | + |
1476 | +namespace Unity.VideoLens |
1477 | +{ |
1478 | + public class RemoteVideoScope : Unity.Scope |
1479 | + { |
1480 | + private static int REFRESH_INTERVAL = 3600; // fetch sources & recommendations once an hour |
1481 | + private static int RETRY_INTERVAL = 60; // retry sources/recommendations after a minute |
1482 | + |
1483 | + private Soup.Session session; |
1484 | + private PreferencesManager preferences = PreferencesManager.get_default (); |
1485 | + Gee.ArrayList<RemoteVideoFile?> recommendations; |
1486 | + int64 recommendations_last_update = 0; |
1487 | + Zeitgeist.DataSourceRegistry zg_sources; |
1488 | + bool use_zeitgeist; |
1489 | + |
1490 | + public RemoteVideoScope () |
1491 | + { |
1492 | + Object (dbus_path: "/net/launchpad/scope/remotevideos"); |
1493 | + } |
1494 | + |
1495 | + protected override void constructed () |
1496 | + { |
1497 | + recommendations = new Gee.ArrayList<RemoteVideoFile?> (); |
1498 | + |
1499 | + use_zeitgeist = false; |
1500 | + try |
1501 | + { |
1502 | + zeitgeist_init (); |
1503 | + use_zeitgeist = true; |
1504 | + } |
1505 | + catch (Error e) |
1506 | + { |
1507 | + warning ("Failed to initialize Zeitgeist, won't store events"); |
1508 | + } |
1509 | + |
1510 | + session = new Soup.SessionAsync (); |
1511 | + session.ssl_use_system_ca_file = true; |
1512 | + session.ssl_strict = true; |
1513 | + session.user_agent = "Unity Video Lens Remote Scope v" + Config.VERSION; |
1514 | + session.add_feature_by_type (typeof (SoupGNOME.ProxyResolverGNOME)); |
1515 | + |
1516 | + search_in_global = false; |
1517 | + search_changed.connect ((search, search_type, cancellable) => |
1518 | + { |
1519 | + update_search_async.begin (search, search_type, cancellable); |
1520 | + }); |
1521 | + filters_changed.connect (on_filters_changed); |
1522 | + sources.notify["filtering"].connect (on_filters_changed); |
1523 | + |
1524 | + generate_search_key.connect ((scope, search) => |
1525 | + { |
1526 | + return search.search_string.strip (); |
1527 | + }); |
1528 | + |
1529 | + preferences.notify["remote-content-search"].connect ((obj, pspec) => |
1530 | + { |
1531 | + queue_search_changed (SearchType.DEFAULT); |
1532 | + }); |
1533 | + |
1534 | + activate_uri.connect (on_activate_uri); |
1535 | + preview_uri.connect (on_preview_uri); |
1536 | + |
1537 | + query_list_of_sources (); |
1538 | + |
1539 | + // refresh the at least once every 30 minutes |
1540 | + GLib.Timeout.add_seconds (REFRESH_INTERVAL/2, () => |
1541 | + { |
1542 | + queue_search_changed (SearchType.DEFAULT); return true; |
1543 | + }); |
1544 | + |
1545 | + try |
1546 | + { |
1547 | + export (); |
1548 | + } |
1549 | + catch (Error e) |
1550 | + { |
1551 | + error ("Failed to export scope: %s", e.message); |
1552 | + } |
1553 | + } |
1554 | + |
1555 | + /* Query the server for a list of sources that will be used |
1556 | + * to build sources filter options and search queries. |
1557 | + */ |
1558 | + private void query_list_of_sources () |
1559 | + { |
1560 | + var msg = new Soup.Message ("GET", UbuntuVideoSearch.sources_uri ()); |
1561 | + session.queue_message (msg, sources_cb); |
1562 | + } |
1563 | + |
1564 | + private Gee.ArrayList<RemoteVideoFile?>? handle_search_response (Soup.Message msg, bool is_treat_yourself = false) |
1565 | + { |
1566 | + if (msg.status_code != 200) |
1567 | + { |
1568 | + warning ("Unable to get results from the server: %u, %s", msg.status_code, msg.reason_phrase); |
1569 | + } |
1570 | + else |
1571 | + { |
1572 | + try |
1573 | + { |
1574 | + return UbuntuVideoSearch.process_search_results ((string)msg.response_body.data, is_treat_yourself); |
1575 | + } |
1576 | + catch (Error e) |
1577 | + { |
1578 | + warning ("Error processing search results: %s", e.message); |
1579 | + } |
1580 | + } |
1581 | + return null; |
1582 | + } |
1583 | + |
1584 | + private void sources_cb (Soup.Session session, Soup.Message msg) |
1585 | + { |
1586 | + uint interval = RETRY_INTERVAL; |
1587 | + if (msg.status_code != 200) |
1588 | + { |
1589 | + warning ("Unable to query the server for a list of sources, %u: %s", msg.status_code, msg.reason_phrase); |
1590 | + } |
1591 | + else |
1592 | + { |
1593 | + try |
1594 | + { |
1595 | + var sources_array = UbuntuVideoSearch.process_sources_results ((string) msg.response_body.data); |
1596 | + |
1597 | + // remove all existing sources |
1598 | + foreach (var opt in sources.options) |
1599 | + { |
1600 | + sources.remove_option (opt.id); |
1601 | + } |
1602 | + |
1603 | + // add sources |
1604 | + foreach (var src in sources_array) |
1605 | + { |
1606 | + sources.add_option (src, src, null); |
1607 | + } |
1608 | + interval = REFRESH_INTERVAL; |
1609 | + } |
1610 | + catch (Error e) |
1611 | + { |
1612 | + warning ("Got invalid json from the server"); |
1613 | + } |
1614 | + } |
1615 | + GLib.Timeout.add_seconds (interval, () => |
1616 | + { |
1617 | + query_list_of_sources (); return false; |
1618 | + }); |
1619 | + } |
1620 | + |
1621 | + private Unity.ActivationResponse on_activate_uri (string rawuri) |
1622 | + { |
1623 | + var fakeuri = RemoteUri.from_rawuri (rawuri); |
1624 | + if (fakeuri != null) |
1625 | + { |
1626 | + if (use_zeitgeist) |
1627 | + zeitgeist_insert_event (fakeuri.uri, fakeuri.title, fakeuri.icon); |
1628 | + try |
1629 | + { |
1630 | + GLib.AppInfo.launch_default_for_uri (fakeuri.uri, null); |
1631 | + return new Unity.ActivationResponse (Unity.HandledType.HIDE_DASH); |
1632 | + } |
1633 | + catch (GLib.Error e) |
1634 | + { |
1635 | + warning ("Failed to launch default application for '%s': %s", fakeuri.uri, e.message); |
1636 | + } |
1637 | + } |
1638 | + else |
1639 | + { |
1640 | + warning ("Invalid raw uri: '%s'", rawuri); |
1641 | + } |
1642 | + return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED); |
1643 | + } |
1644 | + |
1645 | + private Unity.ActivationResponse on_play_video (string rawuri) |
1646 | + { |
1647 | + return on_activate_uri (rawuri); |
1648 | + } |
1649 | + |
1650 | + private Unity.Preview? build_preview (RemoteUri uri, RemoteVideoDetails? details) |
1651 | + { |
1652 | + string title = uri.title; |
1653 | + string subtitle = ""; |
1654 | + string description = ""; |
1655 | + |
1656 | + if (details != null) |
1657 | + { |
1658 | + title = details.title; |
1659 | + description = details.description; |
1660 | + |
1661 | + if (details.release_date != null && details.release_date != "") |
1662 | + subtitle = details.release_date; |
1663 | + |
1664 | + if (details.duration > 0) |
1665 | + { |
1666 | + string duration = ngettext ("%d min", "%d mins", details.duration).printf (details.duration); |
1667 | + if (subtitle != "") |
1668 | + subtitle += ", " + duration; |
1669 | + else |
1670 | + subtitle = duration; |
1671 | + } |
1672 | + } |
1673 | + |
1674 | + GLib.Icon thumbnail = new GLib.FileIcon (GLib.File.new_for_uri (details != null ? details.image : uri.icon)); |
1675 | + |
1676 | + var real_preview = new Unity.MoviePreview (title, subtitle, description, thumbnail); |
1677 | + var play_video = new Unity.PreviewAction ("play", _("Play"), null); |
1678 | + play_video.activated.connect (on_play_video); |
1679 | + real_preview.add_action (play_video); |
1680 | + |
1681 | + // For now, rating == -1 and num_ratings == 0 hides the rating widget from the preview |
1682 | + real_preview.set_rating (-1, 0); |
1683 | + |
1684 | + if (details != null) |
1685 | + { |
1686 | + //TODO: For details of future source types, factor out common detail key/value pairs |
1687 | + if (details.directors.length > 0) |
1688 | + real_preview.add_info (new Unity.InfoHint ("directors", ngettext ("Director", "Directors", details.directors.length), null, string.joinv (", ", details.directors))); |
1689 | + |
1690 | + if (details.starring != null && details.starring != "") |
1691 | + real_preview.add_info (new Unity.InfoHint ("cast", _("Cast"), null, details.starring)); |
1692 | + |
1693 | + if (details.genres != null && details.genres.length > 0) |
1694 | + real_preview.add_info (new Unity.InfoHint ("genres", ngettext("Genre", "Genres", details.genres.length), null, string.joinv (", ", details.genres))); |
1695 | + |
1696 | + // TODO: Add Vimeo & YouTube details for v1 of JSON API |
1697 | + if (details.uploaded_by != null && details.uploaded_by != "") |
1698 | + real_preview.add_info (new Unity.InfoHint ("uploaded-by", _("Uploaded by"), null, details.uploaded_by)); |
1699 | + |
1700 | + if (details.date_uploaded != null && details.date_uploaded != "") |
1701 | + real_preview.add_info (new Unity.InfoHint ("uploaded-on", _("Uploaded on"), null, details.date_uploaded)); |
1702 | + } |
1703 | + |
1704 | + return real_preview; |
1705 | + } |
1706 | + |
1707 | + private Unity.Preview? on_preview_uri (string rawuri) |
1708 | + { |
1709 | + var fakeuri = RemoteUri.from_rawuri (rawuri); |
1710 | + if (fakeuri != null) |
1711 | + { |
1712 | + if (fakeuri.details_uri != null && fakeuri.details_uri != "") |
1713 | + { |
1714 | + var preview = new AsyncPreview (); |
1715 | + get_details.begin (fakeuri.details_uri, (obj, res) => |
1716 | + { |
1717 | + try |
1718 | + { |
1719 | + var details = get_details.end (res); |
1720 | + preview.preview_ready (build_preview (fakeuri, details)); |
1721 | + } |
1722 | + catch (Error e) |
1723 | + { |
1724 | + warning ("Failed to fetch video details: %s", e.message); |
1725 | + preview.preview_ready (build_preview (fakeuri, null)); |
1726 | + } |
1727 | + }); |
1728 | + return preview; |
1729 | + } |
1730 | + else |
1731 | + { |
1732 | + return build_preview (fakeuri, null); |
1733 | + } |
1734 | + } |
1735 | + else |
1736 | + { |
1737 | + warning ("Invalid raw uri: '%s'", rawuri); |
1738 | + } |
1739 | + |
1740 | + return null; |
1741 | + } |
1742 | + |
1743 | + private async RemoteVideoDetails? get_details (string url) throws Error |
1744 | + { |
1745 | + var msg = new Soup.Message ("GET", url); |
1746 | + session.queue_message (msg, (session_, msg_) => |
1747 | + { |
1748 | + msg = msg_; |
1749 | + get_details.callback (); |
1750 | + }); |
1751 | + |
1752 | + yield; |
1753 | + |
1754 | + if (msg.status_code != 200) |
1755 | + { |
1756 | + warning ("Unable to get details from the server: %u, %s", msg.status_code, msg.reason_phrase); |
1757 | + return null; |
1758 | + } |
1759 | + else |
1760 | + { |
1761 | + var details = UbuntuVideoSearch.process_details_results ((string) msg.response_body.data); |
1762 | + return details; |
1763 | + } |
1764 | + } |
1765 | + |
1766 | + private async void update_search_async (LensSearch search, SearchType search_type, Cancellable? cancellable) |
1767 | + { |
1768 | + var search_string = search.search_string.strip (); |
1769 | + debug ("Remote search string changed to: %s", search_string); |
1770 | + |
1771 | + var model = search.results_model; |
1772 | + model.clear (); |
1773 | + |
1774 | + // only perform the request if the user has not disabled |
1775 | + // online/commercial suggestions. That will hide the category as well. |
1776 | + if (preferences.remote_content_search != Unity.PreferencesManager.RemoteContent.ALL) |
1777 | + { |
1778 | + search.finished(); |
1779 | + return; |
1780 | + } |
1781 | + |
1782 | + // create a list of activated sources |
1783 | + var active_sources = new Gee.ArrayList<string> (null); |
1784 | + foreach (var opt in sources.options) |
1785 | + { |
1786 | + if (source_activated (opt.id)) |
1787 | + active_sources.add (opt.id); |
1788 | + } |
1789 | + |
1790 | + // If all the sources are activated, don't bother passing them as arguments |
1791 | + if (active_sources.size == sources.options.length ()) |
1792 | + { |
1793 | + active_sources.clear (); |
1794 | + } |
1795 | + |
1796 | + if (search_type == Unity.SearchType.DEFAULT) |
1797 | + { |
1798 | + if (at_least_one_source_is_on (active_sources)) |
1799 | + { |
1800 | + yield perform_search (search_string, search, active_sources, cancellable); |
1801 | + } |
1802 | + } |
1803 | + |
1804 | + search.finished (); |
1805 | + } |
1806 | + |
1807 | + private bool source_activated (string id) |
1808 | + { |
1809 | + bool active = sources.get_option (id).active; |
1810 | + bool filtering = sources.filtering; |
1811 | + |
1812 | + if ((active && filtering) || (!active && !filtering)) |
1813 | + return true; |
1814 | + return false; |
1815 | + } |
1816 | + |
1817 | + /* Return a general activation state of all sources of this scope. |
1818 | + * This is needed, because we don't want to show recommends if an option |
1819 | + * from another scope is the only one activated |
1820 | + */ |
1821 | + private bool at_least_one_source_is_on (Gee.ArrayList<string> active_sources) |
1822 | + { |
1823 | + return (sources.filtering && active_sources.size > 0 || !sources.filtering); |
1824 | + } |
1825 | + |
1826 | + private void on_filters_changed () |
1827 | + { |
1828 | + queue_search_changed (SearchType.DEFAULT); |
1829 | + } |
1830 | + |
1831 | + /* Query the server with the search string and the list of sources. |
1832 | + */ |
1833 | + private async void perform_search (string search_string, LensSearch search, Gee.ArrayList<string> active_sources, Cancellable? cancellable) |
1834 | + { |
1835 | + search.results_model.clear (); |
1836 | + |
1837 | + if ((search_string == null || search_string == "") && (active_sources.size == 0) && (recommendations.size > 0)) |
1838 | + { |
1839 | + var time = new DateTime.now_utc (); |
1840 | + if (time.to_unix () - recommendations_last_update < REFRESH_INTERVAL) |
1841 | + { |
1842 | + debug ("Updating search results with recommendations"); |
1843 | + update_results_model (search.results_model, recommendations); |
1844 | + return; |
1845 | + } |
1846 | + } |
1847 | + |
1848 | + var url = UbuntuVideoSearch.build_search_uri (search_string, active_sources); |
1849 | + debug ("Querying the server: %s", url); |
1850 | + |
1851 | + bool is_treat_yourself = (search_string == null || search_string == "" || active_sources.size == 0); |
1852 | + var msg = new Soup.Message ("GET", url); |
1853 | + |
1854 | + session.queue_message (msg, (session_, msg_) => |
1855 | + { |
1856 | + msg = msg_; |
1857 | + perform_search.callback (); |
1858 | + }); |
1859 | + |
1860 | + var cancelled = false; |
1861 | + ulong cancel_id = 0; |
1862 | + if (cancellable != null) |
1863 | + { |
1864 | + cancel_id = cancellable.connect (() => |
1865 | + { |
1866 | + cancelled = true; |
1867 | + session.cancel_message (msg, Soup.KnownStatusCode.CANCELLED); |
1868 | + }); |
1869 | + } |
1870 | + |
1871 | + yield; |
1872 | + |
1873 | + if (cancelled) |
1874 | + { |
1875 | + // we can't disconnect right away, as that would deadlock (cause |
1876 | + // cancel_message doesn't return before invoking the callback) |
1877 | + Idle.add (perform_search.callback); |
1878 | + yield; |
1879 | + cancellable.disconnect (cancel_id); |
1880 | + throw new IOError.CANCELLED ("Cancelled"); |
1881 | + } |
1882 | + |
1883 | + if (cancellable != null) |
1884 | + { |
1885 | + // clean up |
1886 | + cancellable.disconnect (cancel_id); |
1887 | + } |
1888 | + |
1889 | + var results = handle_search_response (msg, is_treat_yourself); |
1890 | + if (results != null) |
1891 | + { |
1892 | + if (search_string == null || search_string.strip () == "" && active_sources.size == 0) |
1893 | + { |
1894 | + debug ("Empty search, updating recommendations"); |
1895 | + var time = new DateTime.now_utc (); |
1896 | + recommendations = results; |
1897 | + recommendations_last_update = time.to_unix (); |
1898 | + } |
1899 | + update_results_model (search.results_model, results); |
1900 | + } |
1901 | + } |
1902 | + |
1903 | + private void update_results_model (Dee.Model model, Gee.ArrayList<RemoteVideoFile?> results) |
1904 | + { |
1905 | + foreach (var video in results) |
1906 | + { |
1907 | + if (video.uri.has_prefix ("http")) |
1908 | + { |
1909 | + var fake_uri = new RemoteUri (video.uri, video.title, video.icon, video.details_uri); |
1910 | + var result_icon = video.icon; |
1911 | + |
1912 | + if (video.category == CAT_INDEX_MORE && video.price != null && video.price != "") |
1913 | + { |
1914 | + var anno_icon = new Unity.AnnotatedIcon (new FileIcon (File.new_for_uri (result_icon))); |
1915 | + anno_icon.category = Unity.CategoryType.MOVIE; |
1916 | + anno_icon.ribbon = video.price; |
1917 | + result_icon = anno_icon.to_string (); |
1918 | + } |
1919 | + |
1920 | + model.append (fake_uri.to_rawuri (), result_icon, video.category, "text/html", video.title, video.comment, video.uri); |
1921 | + } |
1922 | + } |
1923 | + } |
1924 | + |
1925 | + private void zeitgeist_init () throws Error |
1926 | + { |
1927 | + zg_sources = new Zeitgeist.DataSourceRegistry (); |
1928 | + var templates = new PtrArray.sized(1); |
1929 | + var ev = new Zeitgeist.Event.full (Zeitgeist.ZG_ACCESS_EVENT, Zeitgeist.ZG_USER_ACTIVITY, "lens://unity-lens-video"); |
1930 | + templates.add ((ev as GLib.Object).ref()); |
1931 | + var data_source = new Zeitgeist.DataSource.full ("98898", "Unity Video Lens", "", templates); |
1932 | + zg_sources.register_data_source (data_source, null); |
1933 | + } |
1934 | + |
1935 | + private void zeitgeist_insert_event (string uri, string title, string icon) |
1936 | + { |
1937 | + var subject = new Zeitgeist.Subject.full (uri, Zeitgeist.NFO_VIDEO, Zeitgeist.NFO_REMOTE_DATA_OBJECT, "", uri, title, icon); |
1938 | + var event = new Zeitgeist.Event.full (Zeitgeist.ZG_ACCESS_EVENT, Zeitgeist.ZG_USER_ACTIVITY, "lens://unity-lens-video"); |
1939 | + event.add_subject (subject); |
1940 | + |
1941 | + var ev_array = new PtrArray.sized(1); |
1942 | + ev_array.add ((event as GLib.Object).ref ()); |
1943 | + Zeitgeist.Log.get_default ().insert_events_from_ptrarray (ev_array, null); |
1944 | + } |
1945 | + } |
1946 | +} |
1947 | |
1948 | === added file 'src/remote-uri.vala' |
1949 | --- src/remote-uri.vala 1970-01-01 00:00:00 +0000 |
1950 | +++ src/remote-uri.vala 2013-02-19 17:19:01 +0000 |
1951 | @@ -0,0 +1,52 @@ |
1952 | +/* |
1953 | + * Copyright (C) 2012 Canonical Ltd |
1954 | + * |
1955 | + * This program is free software: you can redistribute it and/or modify |
1956 | + * it under the terms of the GNU General Public License version 3 as |
1957 | + * published by the Free Software Foundation. |
1958 | + * |
1959 | + * This program is distributed in the hope that it will be useful, |
1960 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1961 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1962 | + * GNU General Public License for more details. |
1963 | + * |
1964 | + * You should have received a copy of the GNU General Public License |
1965 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1966 | + * |
1967 | + * Authored by Pawel Stolowski <pawel.stolowski@canonical.com> |
1968 | + */ |
1969 | + |
1970 | +namespace Unity.VideoLens |
1971 | +{ |
1972 | + public class RemoteUri |
1973 | + { |
1974 | + public string uri { get; set; } |
1975 | + public string title { get; set; } |
1976 | + public string icon { get; set; } |
1977 | + public string details_uri { get; set; } |
1978 | + |
1979 | + public RemoteUri (string uri, string title, string icon, string details_uri) |
1980 | + { |
1981 | + this.uri = uri; |
1982 | + this.title = title; |
1983 | + this.icon = icon; |
1984 | + this.details_uri = details_uri; |
1985 | + } |
1986 | + |
1987 | + public static RemoteUri? from_rawuri (string raw_uri) |
1988 | + { |
1989 | + RemoteUri uri = null; |
1990 | + string[] args = raw_uri.split ("lens-meta://", 4); |
1991 | + if (args.length == 4) |
1992 | + { |
1993 | + uri = new RemoteUri (args[0], args[1], args[2], args[3]); |
1994 | + } |
1995 | + return uri; |
1996 | + } |
1997 | + |
1998 | + public string to_rawuri () |
1999 | + { |
2000 | + return string.join ("lens-meta://", uri, title, icon, details_uri); |
2001 | + } |
2002 | + } |
2003 | +} |
2004 | \ No newline at end of file |
2005 | |
2006 | === added file 'src/remote-video-main.vala' |
2007 | --- src/remote-video-main.vala 1970-01-01 00:00:00 +0000 |
2008 | +++ src/remote-video-main.vala 2013-02-19 17:19:01 +0000 |
2009 | @@ -0,0 +1,60 @@ |
2010 | +/* |
2011 | + * Copyright (C) 2012 Canonical Ltd |
2012 | + * |
2013 | + * This program is free software: you can redistribute it and/or modify |
2014 | + * it under the terms of the GNU General Public License version 3 as |
2015 | + * published by the Free Software Foundation. |
2016 | + * |
2017 | + * This program is distributed in the hope that it will be useful, |
2018 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2019 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2020 | + * GNU General Public License for more details. |
2021 | + * |
2022 | + * You should have received a copy of the GNU General Public License |
2023 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2024 | + */ |
2025 | + |
2026 | +using GLib; |
2027 | +using Config; |
2028 | + |
2029 | +namespace Unity.VideoLens { |
2030 | + |
2031 | + static const string BUS_NAME = "net.launchpad.scope.RemoteVideos"; |
2032 | + Unity.Scope scope; |
2033 | + static Application? app = null; |
2034 | + |
2035 | + public static int main (string[] args) |
2036 | + { |
2037 | + GLib.Environment.set_prgname ("unity-remote-video-scope"); |
2038 | + |
2039 | + /* Sort up locale to get translations but also sorting and |
2040 | + * punctuation right */ |
2041 | + GLib.Intl.textdomain (Config.PACKAGE); |
2042 | + GLib.Intl.bindtextdomain (Config.PACKAGE, Config.LOCALEDIR); |
2043 | + GLib.Intl.bind_textdomain_codeset (Config.PACKAGE, "UTF-8"); |
2044 | + GLib.Intl.setlocale(GLib.LocaleCategory.ALL, ""); |
2045 | + |
2046 | + try |
2047 | + { |
2048 | + app = Extras.dbus_own_name (BUS_NAME, () => |
2049 | + { |
2050 | + scope = new RemoteVideoScope (); |
2051 | + }); |
2052 | + } |
2053 | + catch (Error e) |
2054 | + { |
2055 | + warning ("Failed to start video lens daemon: %s\n", e.message); |
2056 | + return 1; |
2057 | + } |
2058 | + |
2059 | + if (app == null) |
2060 | + { |
2061 | + warning ("Another instance of the Unity Videos Lens " + |
2062 | + "already appears to be running.\nBailing out.\n"); |
2063 | + return 2; |
2064 | + } |
2065 | + |
2066 | + return app.run (); |
2067 | + } |
2068 | + |
2069 | +} /* namespace */ |
2070 | |
2071 | === added file 'src/scope.vala' |
2072 | --- src/scope.vala 1970-01-01 00:00:00 +0000 |
2073 | +++ src/scope.vala 2013-02-19 17:19:01 +0000 |
2074 | @@ -0,0 +1,427 @@ |
2075 | +/* |
2076 | + * Copyright (C) 2012 Canonical Ltd |
2077 | + * |
2078 | + * This program is free software: you can redistribute it and/or modify |
2079 | + * it under the terms of the GNU General Public License version 3 as |
2080 | + * published by the Free Software Foundation. |
2081 | + * |
2082 | + * This program is distributed in the hope that it will be useful, |
2083 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2084 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2085 | + * GNU General Public License for more details. |
2086 | + * |
2087 | + * You should have received a copy of the GNU General Public License |
2088 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2089 | + * |
2090 | + * Authored by Pawel Stolowski <pawel.stolowski@canonical.com> |
2091 | + * based on python code by David Calle <davidc@framli.eu> |
2092 | + * |
2093 | + */ |
2094 | + |
2095 | +namespace Unity.VideoLens |
2096 | +{ |
2097 | + public class VideoScope : Unity.Scope |
2098 | + { |
2099 | + private static const int MAX_ZG_EVENTS = 24; |
2100 | + private static const int CAT_INDEX_MY_VIDEOS = 0; |
2101 | + private static const int CAT_INDEX_ONLINE = 1; |
2102 | + private static const int CAT_INDEX_MORE = 2; |
2103 | + private static const int REFRESH_TIMEOUT = 30; |
2104 | + |
2105 | + private static string cache_directory; |
2106 | + |
2107 | + private string videos_folder; |
2108 | + private Unity.Extras.PreviewPlayer preview_player; |
2109 | + private Thumbnailer thumbnailer; |
2110 | + private Locate locate; |
2111 | + private BlacklistTracker blacklist_tracker; |
2112 | + |
2113 | + public VideoScope () |
2114 | + { |
2115 | + Object (dbus_path: "/net/launchpad/lens/video/main"); |
2116 | + |
2117 | + videos_folder = GLib.Environment.get_user_special_dir (GLib.UserDirectory.VIDEOS); |
2118 | + cache_directory = GLib.Environment.get_user_cache_dir () + "/unity-lens-video"; |
2119 | + |
2120 | + // create cache directory |
2121 | + try |
2122 | + { |
2123 | + var cache_dir = GLib.File.new_for_path (cache_directory); |
2124 | + if (! cache_dir.query_exists (null)) |
2125 | + cache_dir.make_directory (null); |
2126 | + } |
2127 | + catch (Error e) |
2128 | + { |
2129 | + error ("Failed to create cache directory: %s", e.message); |
2130 | + } |
2131 | + |
2132 | + blacklist_tracker = new BlacklistTracker (); |
2133 | + thumbnailer = new Thumbnailer (cache_directory); |
2134 | + locate = new Locate (cache_directory, videos_folder); |
2135 | + |
2136 | + search_in_global = true; |
2137 | + sources.add_option ("local", _("My Videos"), null); |
2138 | + provides_personal_content = true; |
2139 | + |
2140 | + GLib.Timeout.add_seconds (REFRESH_TIMEOUT, refresh_results); |
2141 | + |
2142 | + search_changed.connect ((search, search_type, cancellable) => |
2143 | + { |
2144 | + dispatch_search (search, search_type, cancellable); |
2145 | + }); |
2146 | + |
2147 | + filters_changed.connect (on_filters_changed); |
2148 | + sources.notify["filtering"].connect (on_filters_changed); |
2149 | + preview_uri.connect ((uri) => |
2150 | + { |
2151 | + return generate_preview_for_uri (uri); |
2152 | + }); |
2153 | + } |
2154 | + |
2155 | + private bool refresh_results () |
2156 | + { |
2157 | + debug ("Queuing new search because of timeout"); |
2158 | + queue_search_changed (Unity.SearchType.DEFAULT); |
2159 | + return true; |
2160 | + } |
2161 | + |
2162 | + private void on_filters_changed () |
2163 | + { |
2164 | + queue_search_changed (Unity.SearchType.DEFAULT); |
2165 | + } |
2166 | + |
2167 | + private async void dispatch_search (LensSearch search, SearchType search_type, Cancellable cancellable) |
2168 | + { |
2169 | + var search_string = search.search_string.strip (); |
2170 | + var search_status = search; |
2171 | + var model = search.results_model; |
2172 | + debug ("Search changed to '%s'", search_string); |
2173 | + |
2174 | + if (source_activated ("local")) |
2175 | + { |
2176 | + if (search_type == Unity.SearchType.GLOBAL) |
2177 | + { |
2178 | + if (search_string == "") |
2179 | + { |
2180 | + model.clear (); |
2181 | + if (search != null) |
2182 | + search_status.finished (); |
2183 | + debug ("Global view without search string : hide"); |
2184 | + } |
2185 | + else |
2186 | + { |
2187 | + update_results_model (search_string, model, "global", cancellable, search_status); |
2188 | + } |
2189 | + } |
2190 | + else |
2191 | + { |
2192 | + if (search_string == null || search_string == "") |
2193 | + { |
2194 | + try |
2195 | + { |
2196 | + zg_call (cancellable, search_status); |
2197 | + } |
2198 | + catch (GLib.Error e) |
2199 | + { |
2200 | + warning ("Failed to call zeitgeist: %s", e.message); |
2201 | + } |
2202 | + } |
2203 | + else |
2204 | + { |
2205 | + update_results_model (search_string, model, "lens", cancellable, search_status); |
2206 | + } |
2207 | + } |
2208 | + } |
2209 | + } |
2210 | + |
2211 | + private void update_results_model (string search_string, Dee.Model model, string cat, Cancellable? cancellable, LensSearch search, bool clear_model = true) |
2212 | + { |
2213 | + var home_folder = GLib.Environment.get_home_dir (); |
2214 | + |
2215 | + if (videos_folder != home_folder) |
2216 | + { |
2217 | + locate.updatedb (); |
2218 | + } |
2219 | + |
2220 | + var result_list = locate.run_locate (search_string, thumbnailer, video_filter); |
2221 | + if (result_list != null) |
2222 | + { |
2223 | + GLib.Idle.add (() => |
2224 | + { |
2225 | + result_list.sort ((GLib.CompareFunc?)sort_alpha); |
2226 | + add_results (search, model, cat, cancellable, result_list, search_string, clear_model); |
2227 | + return false; |
2228 | + }); |
2229 | + } |
2230 | + } |
2231 | + |
2232 | + internal void add_results (LensSearch search_status, Dee.Model model, string cat, Cancellable? cancellable, Gee.ArrayList<VideoFile?> result_list, string search, bool clear_model) |
2233 | + { |
2234 | + if (cancellable != null && !cancellable.is_cancelled ()) |
2235 | + { |
2236 | + if (clear_model) |
2237 | + search_status.results_model.clear (); |
2238 | + |
2239 | + foreach (var video in result_list) |
2240 | + { |
2241 | + results_model.append (video.uri, video.icon, video.category, "text/html", video.title, video.comment, video.uri); |
2242 | + } |
2243 | + |
2244 | + if (search_status != null) |
2245 | + { |
2246 | + debug ("Search finished"); |
2247 | + search_status.finished (); |
2248 | + } |
2249 | + } |
2250 | + } |
2251 | + |
2252 | + internal static int sort_alpha (VideoFile a, VideoFile b) |
2253 | + { |
2254 | + return a.lc_title.collate (b.lc_title); |
2255 | + } |
2256 | + |
2257 | + private async void zg_call (Cancellable? cancellable, LensSearch search_status) throws Error |
2258 | + { |
2259 | + bool active = sources.get_option ("local").active; |
2260 | + bool filtering = sources.filtering; |
2261 | + string uri = active && filtering ? "file:*" : "*"; |
2262 | + |
2263 | + var time_range = new Zeitgeist.TimeRange.to_now (); |
2264 | + var event_template = new Zeitgeist.Event (); |
2265 | + var subject = new Zeitgeist.Subject.full (uri, Zeitgeist.NFO_VIDEO, "", "", "", "", ""); |
2266 | + event_template.add_subject (subject); |
2267 | + |
2268 | + var templates = new PtrArray.sized (1); |
2269 | + templates.add ((event_template as GLib.Object).ref()); |
2270 | + var results = yield Zeitgeist.Log.get_default ().find_events (time_range, templates, Zeitgeist.StorageState.ANY, MAX_ZG_EVENTS, Zeitgeist.ResultType.MOST_RECENT_SUBJECTS, cancellable); |
2271 | + process_zg_events (results, cancellable, search_status); |
2272 | + } |
2273 | + |
2274 | + internal bool video_filter (string path) |
2275 | + { |
2276 | + try |
2277 | + { |
2278 | + if (Utils.is_video (path) && !Utils.is_hidden (path)) |
2279 | + { |
2280 | + var file = File.new_for_path (path); |
2281 | + if (!is_blacklisted(file.get_uri ())) |
2282 | + return true; |
2283 | + } |
2284 | + } |
2285 | + catch (Error e) |
2286 | + { |
2287 | + warning ("Failed to get properties of '%s': %s", path, e.message); |
2288 | + } |
2289 | + return false; |
2290 | + } |
2291 | + |
2292 | + internal void process_zg_events (Zeitgeist.ResultSet events, Cancellable cancellable, LensSearch search_status) |
2293 | + { |
2294 | + var result_list = new Gee.ArrayList<VideoFile?> (); |
2295 | + |
2296 | + foreach (var event in events) |
2297 | + { |
2298 | + if (cancellable.is_cancelled ()) |
2299 | + return; |
2300 | + |
2301 | + var event_uri = event.get_subject (0).get_uri (); |
2302 | + if (event_uri.has_prefix ("file://")) |
2303 | + { |
2304 | + try |
2305 | + { |
2306 | + // If the file is local, we use the same methods |
2307 | + // as other result items. |
2308 | + var file = GLib.File.new_for_uri (event_uri); |
2309 | + var path = file.get_path (); |
2310 | + if (video_filter (path)) |
2311 | + { |
2312 | + var finfo = file.query_info (GLib.FileAttribute.STANDARD_DISPLAY_NAME, GLib.FileQueryInfoFlags.NONE, null); |
2313 | + VideoFile video = VideoFile () |
2314 | + { |
2315 | + title = finfo.get_display_name (), |
2316 | + lc_title = finfo.get_display_name ().down (), |
2317 | + comment = "", |
2318 | + uri = event_uri, |
2319 | + icon = thumbnailer.get_icon (path), |
2320 | + category = CAT_INDEX_MY_VIDEOS |
2321 | + }; |
2322 | + result_list.add (video); |
2323 | + } |
2324 | + } |
2325 | + catch (GLib.Error e) |
2326 | + { |
2327 | + warning ("Failed to access file '%s': %s", event_uri, e.message); |
2328 | + } |
2329 | + } |
2330 | + else if (event_uri.has_prefix ("http")) |
2331 | + { |
2332 | + // If the file is distant, we take |
2333 | + // all we need from Zeitgeist |
2334 | + // this one can be any unicode string: |
2335 | + VideoFile video = VideoFile () |
2336 | + { |
2337 | + title = event.get_subject (0).get_text (), |
2338 | + comment = "", |
2339 | + uri = event_uri, |
2340 | + icon = event.get_subject (0).get_storage (), |
2341 | + category = CAT_INDEX_ONLINE |
2342 | + }; |
2343 | + result_list.add (video); |
2344 | + } |
2345 | + } |
2346 | + |
2347 | + search_status.results_model.clear (); |
2348 | + foreach (var video in result_list) |
2349 | + { |
2350 | + results_model.append (video.uri, video.icon, video.category, "text/html", video.title, video.comment, video.uri); |
2351 | + } |
2352 | + |
2353 | + update_results_model ("", search_status.results_model, "lens", cancellable, search_status, false); |
2354 | + } |
2355 | + |
2356 | + private Unity.ActivationResponse show_in_folder (string uri) |
2357 | + { |
2358 | + try |
2359 | + { |
2360 | + Unity.Extras.show_in_folder (uri); |
2361 | + return new Unity.ActivationResponse (Unity.HandledType.HIDE_DASH); |
2362 | + } |
2363 | + catch (GLib.Error e) |
2364 | + { |
2365 | + warning ("Failed to show in folder '%s': %s", uri, e.message); |
2366 | + } |
2367 | + return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED); |
2368 | + } |
2369 | + |
2370 | + private Preview? generate_preview_for_uri (string uri) |
2371 | + { |
2372 | + debug ("Preview uri: %s", uri); |
2373 | + |
2374 | + Unity.Preview preview = null; |
2375 | + var model = results_model; |
2376 | + var iter = model.get_first_iter (); |
2377 | + while (!model.is_last (iter)) |
2378 | + { |
2379 | + if (model.get_string (iter, 0) == uri) |
2380 | + { |
2381 | + var title = model.get_string (iter, 4); |
2382 | + string subtitle = ""; |
2383 | + int64 file_size = 0; |
2384 | + bool local_video = uri.has_prefix ("file://"); |
2385 | + |
2386 | + if (local_video) |
2387 | + { |
2388 | + var file = GLib.File.new_for_uri (uri); |
2389 | + try |
2390 | + { |
2391 | + var finfo = file.query_info (GLib.FileAttribute.TIME_MODIFIED + "," + GLib.FileAttribute.STANDARD_SIZE, GLib.FileQueryInfoFlags.NONE, null); |
2392 | + file_size = finfo.get_size (); |
2393 | + var tval = finfo.get_modification_time (); |
2394 | + var dt = new DateTime.from_timeval_local (tval); |
2395 | + subtitle = dt.format ("%x, %X"); |
2396 | + } |
2397 | + catch (Error e) |
2398 | + { |
2399 | + // empty subtitle |
2400 | + } |
2401 | + } |
2402 | + var desc = model.get_string (iter, 5); |
2403 | + |
2404 | + preview = new Unity.MoviePreview (title, subtitle, desc, null); |
2405 | + preview.closed.connect (on_preview_closed); |
2406 | + var play_video = new Unity.PreviewAction ("play", _("Play"), null); |
2407 | + preview.add_action (play_video); |
2408 | + preview.image_source_uri = model.get_string (iter, 1); |
2409 | + |
2410 | + if (local_video) |
2411 | + { |
2412 | + var show_folder = new Unity.PreviewAction ("show-in-folder", _("Show in Folder"), null); |
2413 | + show_folder.activated.connect (show_in_folder); |
2414 | + preview.add_action (show_folder); |
2415 | + } |
2416 | + |
2417 | + // we may get remote uris from zeitgeist - fetch details for local files only |
2418 | + if (local_video) |
2419 | + { |
2420 | + var async_preview = new Unity.AsyncPreview (); |
2421 | + preview.image_source_uri = uri; |
2422 | + |
2423 | + if (preview_player == null) |
2424 | + preview_player = new Unity.Extras.PreviewPlayer (); |
2425 | + |
2426 | + preview_player.video_properties.begin (uri, (obj, res) => |
2427 | + { |
2428 | + try |
2429 | + { |
2430 | + var props = preview_player.video_properties.end (res); |
2431 | + if ("width" in props && "height" in props && "codec" in props) |
2432 | + { |
2433 | + var width = props["width"].get_uint32 (); |
2434 | + var height = props["height"].get_uint32 (); |
2435 | + var codec = props["codec"].get_string (); |
2436 | + string dimensions = "%u*%u".printf (width, height); |
2437 | + if (width > 0 && height > 0) |
2438 | + { |
2439 | + var gcd = Utils.gcd (width, height); |
2440 | + dimensions += ", %u:%u".printf (width / gcd, height / gcd); |
2441 | + } |
2442 | + |
2443 | + preview.add_info (new Unity.InfoHint ("format", _("Format"), null, codec)); |
2444 | + preview.add_info (new Unity.InfoHint ("dimensions", _("Dimensions"), null, dimensions)); |
2445 | + preview.add_info (new Unity.InfoHint ("size", _("Size"), null, GLib.format_size (file_size))); |
2446 | + } |
2447 | + } |
2448 | + catch (Error e) |
2449 | + { |
2450 | + warning ("Couldn't get video details: %s", e.message); |
2451 | + } |
2452 | + async_preview.preview_ready (preview); |
2453 | + }); |
2454 | + return async_preview; |
2455 | + } |
2456 | + break; |
2457 | + } |
2458 | + iter = model.next (iter); |
2459 | + } |
2460 | + |
2461 | + if (preview == null) |
2462 | + warning ("Couldn't find model row for requested preview uri: %s", uri); |
2463 | + |
2464 | + return preview; |
2465 | + } |
2466 | + |
2467 | + private bool is_blacklisted (string uri) |
2468 | + { |
2469 | + foreach (var blacklisted_uri in blacklist_tracker.get_blacklisted_uris ()) |
2470 | + { |
2471 | + if (uri.has_prefix (blacklisted_uri)) |
2472 | + return true; |
2473 | + } |
2474 | + return false; |
2475 | + } |
2476 | + |
2477 | + private void on_preview_closed (Unity.Preview preview) |
2478 | + { |
2479 | + if (preview_player != null) |
2480 | + { |
2481 | + try |
2482 | + { |
2483 | + preview_player.close (); |
2484 | + } |
2485 | + catch (Error e) |
2486 | + { |
2487 | + warning ("Failed to close preview player: %s", e.message); |
2488 | + } |
2489 | + } |
2490 | + } |
2491 | + |
2492 | + private bool source_activated (string source) |
2493 | + { |
2494 | + // Return the state of a sources filter option |
2495 | + var active = sources.get_option (source).active; |
2496 | + var filtering = sources.filtering; |
2497 | + return (active && filtering) || (!active && !filtering); |
2498 | + } |
2499 | + |
2500 | + } |
2501 | +} |
2502 | |
2503 | === added file 'src/thumbnailer.vala' |
2504 | --- src/thumbnailer.vala 1970-01-01 00:00:00 +0000 |
2505 | +++ src/thumbnailer.vala 2013-02-19 17:19:01 +0000 |
2506 | @@ -0,0 +1,115 @@ |
2507 | +/* |
2508 | + * Copyright (C) 2012 Canonical Ltd |
2509 | + * |
2510 | + * This program is free software: you can redistribute it and/or modify |
2511 | + * it under the terms of the GNU General Public License version 3 as |
2512 | + * published by the Free Software Foundation. |
2513 | + * |
2514 | + * This program is distributed in the hope that it will be useful, |
2515 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2516 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2517 | + * GNU General Public License for more details. |
2518 | + * |
2519 | + * You should have received a copy of the GNU General Public License |
2520 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2521 | + * |
2522 | + * Authored by Pawel Stolowski <pawel.stolowski@canonical.com> |
2523 | + * based on python code by David Calle <davidc@framli.eu> |
2524 | + * |
2525 | + */ |
2526 | + |
2527 | +namespace Unity.VideoLens |
2528 | +{ |
2529 | + public class Thumbnailer |
2530 | + { |
2531 | + private static const int MAX_THUMBNAILERS = 3; |
2532 | + |
2533 | + private string videos_folder; |
2534 | + private string cache_directory; |
2535 | + private Gee.LinkedList<GLib.Pid?> thumbnailer_pids; |
2536 | + |
2537 | + public Thumbnailer (string cache_dir) |
2538 | + { |
2539 | + cache_directory = cache_dir; |
2540 | + videos_folder = GLib.Environment.get_user_special_dir (GLib.UserDirectory.VIDEOS); |
2541 | + thumbnailer_pids = new Gee.LinkedList<GLib.Pid?> (); |
2542 | + } |
2543 | + |
2544 | + public string get_icon (string video_file) |
2545 | + { |
2546 | + /* This method checks several locations for a video thumbnail. |
2547 | + |
2548 | + 1) <filename>.jpg file in the same folder (not activated for now) |
2549 | + 2) Nautilus thumbnails |
2550 | + 3) Cached thumbnails generated by the scope |
2551 | + |
2552 | + If nothing has been found, it tries to generate a thumbnail with Totem, |
2553 | + stores and uses it. |
2554 | + If the generation fails or is slow, it fallbacks to the standard video |
2555 | + icon. |
2556 | + */ |
2557 | + var file = GLib.File.new_for_path (video_file); |
2558 | + var video_path = file.get_path (); |
2559 | + |
2560 | + string? icon_path = null; |
2561 | + |
2562 | + // check if nautilus thumbnail is available |
2563 | + try |
2564 | + { |
2565 | + var finfo = file.query_info (GLib.FileAttribute.THUMBNAIL_PATH + "," + GLib.FileAttribute.THUMBNAILING_FAILED, GLib.FileQueryInfoFlags.NONE); |
2566 | + if (finfo.get_attribute_boolean (GLib.FileAttribute.THUMBNAILING_FAILED)) |
2567 | + return "video"; |
2568 | + else |
2569 | + icon_path = finfo.get_attribute_as_string (GLib.FileAttribute.THUMBNAIL_PATH); |
2570 | + } |
2571 | + catch (GLib.Error e) |
2572 | + { |
2573 | + // silently ignore |
2574 | + } |
2575 | + |
2576 | + if (icon_path == null || icon_path == "") |
2577 | + { |
2578 | + string check_path = cache_directory + "/" + video_path.replace (videos_folder, "").replace ("/", "_"); |
2579 | + |
2580 | + // check for thumbnail generated by this scope |
2581 | + if (Utils.is_regular_file (check_path)) |
2582 | + return check_path; |
2583 | + |
2584 | + create_thumbnail (video_path, check_path); |
2585 | + |
2586 | + // FIXME: this is not reliable, since we spawn thumbnailer async |
2587 | + if (Utils.is_regular_file (check_path)) |
2588 | + return check_path; |
2589 | + } |
2590 | + |
2591 | + if (icon_path == null || icon_path == "") |
2592 | + icon_path = "video"; |
2593 | + |
2594 | + return icon_path; |
2595 | + } |
2596 | + |
2597 | + private void thumbnailer_process_watch (Pid pid, int status) |
2598 | + { |
2599 | + thumbnailer_pids.remove (pid); |
2600 | + } |
2601 | + |
2602 | + public void create_thumbnail (string video_path, string thumbnail_path) |
2603 | + { |
2604 | + if (thumbnailer_pids.size < MAX_THUMBNAILERS) |
2605 | + { |
2606 | + GLib.Pid pid; |
2607 | + string[] args = {"/usr/bin/totem-video-thumbnailer", video_path, thumbnail_path}; |
2608 | + try |
2609 | + { |
2610 | + GLib.Process.spawn_async (null, args, null, GLib.SpawnFlags.DO_NOT_REAP_CHILD, null, out pid); |
2611 | + GLib.ChildWatch.add (pid, thumbnailer_process_watch); |
2612 | + thumbnailer_pids.add (pid); |
2613 | + } |
2614 | + catch (Error e) |
2615 | + { |
2616 | + warning ("Failed to spawn thumbailer: %s", e.message); |
2617 | + } |
2618 | + } |
2619 | + } |
2620 | + } |
2621 | +} |
2622 | \ No newline at end of file |
2623 | |
2624 | === added file 'src/ubuntu-video-search.vala' |
2625 | --- src/ubuntu-video-search.vala 1970-01-01 00:00:00 +0000 |
2626 | +++ src/ubuntu-video-search.vala 2013-02-19 17:19:01 +0000 |
2627 | @@ -0,0 +1,229 @@ |
2628 | +/* |
2629 | + * Copyright (C) 2012 Canonical Ltd |
2630 | + * |
2631 | + * This program is free software: you can redistribute it and/or modify |
2632 | + * it under the terms of the GNU General Public License version 3 as |
2633 | + * published by the Free Software Foundation. |
2634 | + * |
2635 | + * This program is distributed in the hope that it will be useful, |
2636 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2637 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2638 | + * GNU General Public License for more details. |
2639 | + * |
2640 | + * You should have received a copy of the GNU General Public License |
2641 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2642 | + * |
2643 | + * Authored by Pawel Stolowski <pawel.stolowski@canonical.com> |
2644 | + * |
2645 | + */ |
2646 | + |
2647 | +namespace Unity.VideoLens.UbuntuVideoSearch |
2648 | +{ |
2649 | + private static const string SERVER = "http://videosearch.ubuntu.com/v0"; |
2650 | + |
2651 | + public static string sources_uri () |
2652 | + { |
2653 | + return SERVER + "/sources"; |
2654 | + } |
2655 | + |
2656 | + public static string recommendations_uri () |
2657 | + { |
2658 | + return SERVER + "/search?q=&sources=Amazon"; |
2659 | + } |
2660 | + |
2661 | + public static string build_search_uri (string query, Gee.ArrayList<string>? sources) |
2662 | + { |
2663 | + var uri = new StringBuilder (); |
2664 | + uri.append (SERVER); |
2665 | + uri.append ("/search?q="); |
2666 | + uri.append (Uri.escape_string (query, "", false)); |
2667 | + uri.append ("&split=true"); |
2668 | + if (sources != null && sources.size>0) |
2669 | + { |
2670 | + uri.append ("&sources="); |
2671 | + uri.append (sources[0]); |
2672 | + for (int i=1; i<sources.size; i++) |
2673 | + { |
2674 | + uri.append (","); |
2675 | + uri.append (sources[i]); |
2676 | + } |
2677 | + } |
2678 | + return uri.str; |
2679 | + } |
2680 | + |
2681 | + /** |
2682 | + * Parses JSON data returned by search requests, returns list of videos. |
2683 | + */ |
2684 | + public Gee.ArrayList<RemoteVideoFile?>? process_search_results (string json_data, bool is_treat_yourself) throws Error |
2685 | + { |
2686 | + var parser = new Json.Parser (); |
2687 | + |
2688 | + parser.load_from_data (json_data); |
2689 | + var root = parser.get_root (); |
2690 | + |
2691 | + if (root.get_node_type () == Json.NodeType.OBJECT) |
2692 | + { |
2693 | + var records = root.get_object ().get_array_member ("other"); |
2694 | + var results = process_results_array (records, CAT_INDEX_ONLINE); |
2695 | + records = root.get_object ().get_array_member ("treats"); |
2696 | + results.add_all (process_results_array (records, CAT_INDEX_MORE)); |
2697 | + return results; |
2698 | + } |
2699 | + else |
2700 | + { |
2701 | + var records = root.get_array (); |
2702 | + var results = process_results_array (records, is_treat_yourself ? CAT_INDEX_MORE : CAT_INDEX_ONLINE); |
2703 | + return results; |
2704 | + } |
2705 | + } |
2706 | + |
2707 | + /** |
2708 | + * Joins elements of JSON array of strings. |
2709 | + */ |
2710 | + internal string join_array (Json.Array array, string separator) |
2711 | + { |
2712 | + var res = new StringBuilder (); |
2713 | + var len = array.get_length (); |
2714 | + array.foreach_element ((a, index, node) => |
2715 | + { |
2716 | + res.append (node.get_string ()); |
2717 | + if (index < len - 1) |
2718 | + res.append (separator); |
2719 | + }); |
2720 | + return res.str; |
2721 | + } |
2722 | + |
2723 | + public string[] json_array_to_str_array (Json.Array array) |
2724 | + { |
2725 | + string [] res = {}; |
2726 | + array.foreach_element ((a, index, node) => |
2727 | + { |
2728 | + res += node.get_string (); |
2729 | + }); |
2730 | + return res; |
2731 | + } |
2732 | + |
2733 | + public RemoteVideoDetails process_details_results (string json_data) throws Error |
2734 | + { |
2735 | + var parser = new Json.Parser (); |
2736 | + parser.load_from_data (json_data); |
2737 | + var details = parser.get_root ().get_object (); |
2738 | + |
2739 | + var video = RemoteVideoDetails (); |
2740 | + video.title = details.get_string_member ("title"); |
2741 | + video.image = details.get_string_member ("image"); |
2742 | + video.description = details.get_string_member ("description"); |
2743 | + video.source = details.get_string_member ("source"); |
2744 | + |
2745 | + if (details.has_member ("release_date")) |
2746 | + { |
2747 | + // v1 spec states release_date will be YYYY-MM-DD |
2748 | + var release_date = GLib.Time (); |
2749 | + if (release_date.strptime (details.get_string_member ("release_date"), "%Y-%m-%d") != null) |
2750 | + { |
2751 | + video.release_date = release_date.format ("%Y"); |
2752 | + } |
2753 | + else |
2754 | + { |
2755 | + warning ("Failed to parse release_date: '%s'", details.get_string_member ("release_date")); |
2756 | + } |
2757 | + } |
2758 | + |
2759 | + video.price = details.has_member ("formatted_price") ? details.get_string_member ("formatted_price") : ""; |
2760 | + |
2761 | + video.duration = 0; |
2762 | + if (details.has_member ("duration")) |
2763 | + video.duration = int.parse (details.get_string_member ("duration")) / 60; |
2764 | + |
2765 | + video.directors = {}; |
2766 | + if (details.has_member ("directors")) |
2767 | + { |
2768 | + var directors = details.get_array_member ("directors"); |
2769 | + video.directors = json_array_to_str_array (directors); |
2770 | + } |
2771 | + |
2772 | + if (details.has_member ("starring")) |
2773 | + { |
2774 | + var starring = details.get_array_member ("starring"); |
2775 | + video.starring = join_array (starring, ", "); |
2776 | + } |
2777 | + |
2778 | + video.genres = {}; |
2779 | + if (details.has_member ("genres")) |
2780 | + { |
2781 | + var genres = details.get_array_member ("genres"); |
2782 | + video.genres = json_array_to_str_array (genres); |
2783 | + } |
2784 | + |
2785 | + if (details.has_member ("uploaded_by")) |
2786 | + video.uploaded_by = details.get_string_member ("uploaded_by"); |
2787 | + |
2788 | + if (details.has_member ("date_uploaded")) |
2789 | + video.date_uploaded = details.get_string_member ("date_uploaded"); |
2790 | + |
2791 | + return video; |
2792 | + } |
2793 | + |
2794 | + /** |
2795 | + * Parses JSON data returned by 'sources' query, returns list of sources (e.g. "Amazon", "Youtube" etc.) |
2796 | + */ |
2797 | + public Gee.ArrayList<string>? process_sources_results (string json_data) throws Error |
2798 | + { |
2799 | + var parser = new Json.Parser (); |
2800 | + |
2801 | + parser.load_from_data (json_data); |
2802 | + var sources_array = parser.get_root ().get_array (); |
2803 | + var results = new Gee.ArrayList<string> (null); |
2804 | + |
2805 | + sources_array.foreach_element ((array, index, node) => |
2806 | + { |
2807 | + results.add (node.get_string ()); |
2808 | + }); |
2809 | + |
2810 | + return results; |
2811 | + } |
2812 | + |
2813 | + /** |
2814 | + * Converts JSON results array to list of RemoteVideoFile records. |
2815 | + */ |
2816 | + internal Gee.ArrayList<RemoteVideoFile?> process_results_array (Json.Array results, int category) |
2817 | + { |
2818 | + var videos = new Gee.ArrayList<RemoteVideoFile?> (); |
2819 | + |
2820 | + results.foreach_element ((array, index, node) => |
2821 | + { |
2822 | + try |
2823 | + { |
2824 | + var video = RemoteVideoFile (); |
2825 | + var rec = node.get_object (); |
2826 | + video.uri = rec.get_string_member ("url"); |
2827 | + video.title = rec.get_string_member ("title"); |
2828 | + video.icon = rec.get_string_member ("img"); |
2829 | + video.comment = rec.get_string_member ("source"); |
2830 | + video.details_uri = (rec.has_member ("details") ? rec.get_string_member ("details") : ""); |
2831 | + video.category = category; |
2832 | + video.price = null; |
2833 | + |
2834 | + if (category == CAT_INDEX_MORE) |
2835 | + { |
2836 | + string price = null; |
2837 | + if (rec.has_member ("formatted_price")) |
2838 | + { |
2839 | + price = rec.get_string_member ("formatted_price"); |
2840 | + } |
2841 | + if (price == null || price == "free") |
2842 | + { |
2843 | + price = _("Free"); |
2844 | + } |
2845 | + video.price = price; |
2846 | + } |
2847 | + videos.add (video); |
2848 | + } |
2849 | + catch (Error e) |
2850 | + { |
2851 | + warning ("Malformed result, skipping: %s", e.message); |
2852 | + } |
2853 | + }); |
2854 | + return videos; |
2855 | + } |
2856 | +} |
2857 | \ No newline at end of file |
2858 | |
2859 | === removed file 'src/unity-lens-video' |
2860 | --- src/unity-lens-video 2012-10-18 15:05:16 +0000 |
2861 | +++ src/unity-lens-video 1970-01-01 00:00:00 +0000 |
2862 | @@ -1,543 +0,0 @@ |
2863 | -#! /usr/bin/python |
2864 | -# -*- coding: utf-8 -*- |
2865 | - |
2866 | -# Copyright (c) 2011 David Calle <davidc@framli.eu> |
2867 | - |
2868 | -# This program is free software: you can redistribute it and/or modify |
2869 | -# it under the terms of the GNU General Public License as published by |
2870 | -# the Free Software Foundation, either version 3 of the License, or |
2871 | -# (at your option) any later version. |
2872 | - |
2873 | -# This program is distributed in the hope that it will be useful, |
2874 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2875 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2876 | -# GNU General Public License for more details. |
2877 | - |
2878 | -# You should have received a copy of the GNU General Public License |
2879 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2880 | - |
2881 | -"""The unity video lens. Indexing videos in ~/Videos and generating thumbnails if needed.""" |
2882 | - |
2883 | -import gettext |
2884 | -import locale |
2885 | -import os |
2886 | -import sys |
2887 | -from zeitgeist.client import ZeitgeistClient |
2888 | -from zeitgeist import client, datamodel |
2889 | -import time |
2890 | -import fractions |
2891 | -import dbus |
2892 | - |
2893 | -#pylint: disable=E0611 |
2894 | -from gi.repository import ( |
2895 | - GLib, |
2896 | - GObject, |
2897 | - Gio, |
2898 | - Unity, |
2899 | - Dee |
2900 | -) |
2901 | -#pylint: enable=E0611 |
2902 | - |
2903 | -APP_NAME = "unity-lens-video" |
2904 | -LOCAL_PATH = "/usr/share/locale/" |
2905 | - |
2906 | -gettext.bindtextdomain(APP_NAME, LOCAL_PATH) |
2907 | -gettext.textdomain(APP_NAME) |
2908 | -_ = gettext.gettext |
2909 | - |
2910 | -# Translatable strings |
2911 | -CAT_ONCOMPUTER = _("My Videos") |
2912 | -CAT_ONLINE = _("Online") |
2913 | -CAT_GLOBAL = _("Videos") |
2914 | -CAT_RECENT = _("Recently Viewed") |
2915 | -CAT_MORE_SUGGESTIONS = _("More suggestions") |
2916 | -HINT = _("Search Videos") |
2917 | -SOURCES = _("Sources") |
2918 | -LOCAL_VIDEOS = _("My Videos") |
2919 | - |
2920 | -CAT_INDEX_MY_VIDEOS = 0 |
2921 | -CAT_INDEX_ONLINE = 1 |
2922 | -CAT_INDEX_MORE = 2 |
2923 | - |
2924 | -BUS_NAME = "net.launchpad.lens.video" |
2925 | -FOLDER = GLib.get_user_special_dir(GLib.USER_DIRECTORY_VIDEOS) |
2926 | -HOME_FOLDER = GLib.get_home_dir() |
2927 | -CACHE = "%s/unity-lens-video" % GLib.get_user_cache_dir() |
2928 | -DB = "videos.db" |
2929 | -Q = [] |
2930 | -Q_MAX = 3 |
2931 | -try: |
2932 | - ZG = ZeitgeistClient() |
2933 | -except: |
2934 | - raise SystemExit(1) |
2935 | - |
2936 | -REFRESH_TIMEOUT = 300 |
2937 | -PREVIEW_PLAYER_DBUS_NAME = "com.canonical.Unity.Lens.Music.PreviewPlayer" |
2938 | -PREVIEW_PLAYER_DBUS_PATH = "/com/canonical/Unity/Lens/Music/PreviewPlayer" |
2939 | -PREVIEW_PLAYER_DBUS_IFACE = PREVIEW_PLAYER_DBUS_NAME |
2940 | - |
2941 | -# pylint: disable=R0903 |
2942 | -class Daemon: |
2943 | - |
2944 | - """Creation of a lens with a local scope.""" |
2945 | - |
2946 | - def __init__(self): |
2947 | - #Create the lens |
2948 | - self._lens = Unity.Lens.new("/net/launchpad/lens/video", "video") |
2949 | - self._lens.props.search_hint = HINT |
2950 | - self._lens.props.visible = True |
2951 | - self._lens.props.search_in_global = True |
2952 | - self._lens.props.sources_display_name = SOURCES |
2953 | - |
2954 | - svg_dir = "/usr/share/icons/unity-icon-theme/places/svg/" |
2955 | - cats = [] |
2956 | - cats.append(Unity.Category.new(CAT_ONCOMPUTER, |
2957 | - Gio.ThemedIcon.new(svg_dir + "group-videos.svg"), |
2958 | - Unity.CategoryRenderer.VERTICAL_TILE)) |
2959 | - cats.append(Unity.Category.new(CAT_ONLINE, |
2960 | - Gio.ThemedIcon.new(svg_dir + "group-internet.svg"), |
2961 | - Unity.CategoryRenderer.VERTICAL_TILE)) |
2962 | - cats.append(Unity.Category.new(CAT_MORE_SUGGESTIONS, |
2963 | - Gio.ThemedIcon.new(svg_dir + "group-treat-yourself.svg"), |
2964 | - Unity.CategoryRenderer.VERTICAL_TILE)) |
2965 | - self._lens.props.categories = cats |
2966 | - |
2967 | - filters = [] |
2968 | - self._lens.props.filters = filters |
2969 | - |
2970 | - self.bus = dbus.SessionBus() |
2971 | - |
2972 | - # Create the scope |
2973 | - self._scope = Unity.Scope.new("/net/launchpad/lens/video/main") |
2974 | - self._scope.search_in_global = True |
2975 | - self._scope.props.sources.add_option('local', LOCAL_VIDEOS, None) |
2976 | - self._scope.props.provides_personal_content=True |
2977 | - self._scope.connect("search-changed", self.on_search_changed) |
2978 | - self._scope.connect("filters-changed",self.on_filtering_changed) |
2979 | - self._scope.props.sources.connect("notify::filtering", |
2980 | - self.on_filtering_changed) |
2981 | - self._scope.connect('preview-uri', self.on_preview_uri) |
2982 | - self._lens.add_local_scope(self._scope) |
2983 | - self._lens.export() |
2984 | - |
2985 | - GLib.timeout_add_seconds(REFRESH_TIMEOUT, self.refresh_results) |
2986 | - self._scope.queue_search_changed(Unity.SearchType.DEFAULT) |
2987 | - |
2988 | - def refresh_results(self, *_): |
2989 | - """Update the results on a timeout.""" |
2990 | - print "Queuing new search because of timeout" |
2991 | - self._scope.queue_search_changed(Unity.SearchType.DEFAULT) |
2992 | - return True |
2993 | - |
2994 | - def on_filtering_changed(self, *_): |
2995 | - """Run another search when a filter change is notified.""" |
2996 | - self._scope.queue_search_changed(Unity.SearchType.DEFAULT) |
2997 | - |
2998 | - def source_activated(self, source): |
2999 | - """Return the state of a sources filter option.""" |
3000 | - active = self._scope.props.sources.get_option(source).props.active |
3001 | - filtering = self._scope.props.sources.props.filtering |
3002 | - if (active and filtering) or (not active and not filtering): |
3003 | - return True |
3004 | - else: |
3005 | - return False |
3006 | - |
3007 | - def on_preview_closed(self, preview): |
3008 | - try: |
3009 | - player = self.bus.get_object (PREVIEW_PLAYER_DBUS_NAME, PREVIEW_PLAYER_DBUS_PATH) |
3010 | - dbus.Interface (player, PREVIEW_PLAYER_DBUS_IFACE).Close() |
3011 | - except Exception as e: |
3012 | - print "Failed to send close signal to preview player:", e |
3013 | - |
3014 | - def on_preview_uri(self, scope, uri): |
3015 | - """Preview request handler""" |
3016 | - preview = None |
3017 | - model = self._scope.props.results_model |
3018 | - iter = model.get_first_iter() |
3019 | - while not model.is_last(iter): |
3020 | - if model.get_string(iter, 0) == uri: |
3021 | - title = model.get_string(iter, 4); |
3022 | - try: |
3023 | - subtitle = time.strftime("%x, %X", time.localtime(os.path.getmtime(GLib.filename_from_uri(uri, None)))) |
3024 | - except: |
3025 | - # Instead of empty, maybe the date/time of the zg event? |
3026 | - subtitle = '' |
3027 | - desc = model.get_string(iter, 5); |
3028 | - preview = Unity.MoviePreview.new(title, subtitle, desc, None) |
3029 | - preview.connect('closed', self.on_preview_closed) |
3030 | - |
3031 | - # we may get remote uris from zeitgeist - fetch details for local files only |
3032 | - if uri.startswith("file://"): |
3033 | - local_video = True |
3034 | - preview.props.image_source_uri = uri |
3035 | - try: |
3036 | - player = self.bus.get_object (PREVIEW_PLAYER_DBUS_NAME, PREVIEW_PLAYER_DBUS_PATH) |
3037 | - props = dbus.Interface (player, PREVIEW_PLAYER_DBUS_IFACE).VideoProperties(uri) |
3038 | - width = props['width'] |
3039 | - height = props['height'] |
3040 | - codec = props['codec'] |
3041 | - dimensions = str(width) + "*" + str(height) |
3042 | - if width > 0 and height > 0: |
3043 | - gcd = fractions.gcd(width, height) |
3044 | - dimensions += ", " + str(width / gcd) + ":" + str(height / gcd) |
3045 | - preview.add_info(Unity.InfoHint.new("format", _("Format"), None, codec)) |
3046 | - preview.add_info(Unity.InfoHint.new("dimensions", _("Dimensions"), None, dimensions)) |
3047 | - preview.add_info(Unity.InfoHint.new("size", _("Size"), None, GLib.format_size(os.path.getsize(GLib.filename_from_uri(uri, None))))) |
3048 | - except Exception as e: |
3049 | - print "Couldn't get video details", e |
3050 | - else: |
3051 | - local_video = False |
3052 | - preview.props.image_source_uri = model.get_string(iter, 1) |
3053 | - play_video = Unity.PreviewAction.new("play", _("Play"), None) |
3054 | - preview.add_action(play_video) |
3055 | - if local_video: |
3056 | - show_folder = Unity.PreviewAction.new("show-in-folder", _("Show in Folder"), None) |
3057 | - show_folder.connect('activated', self.show_in_folder) |
3058 | - preview.add_action(show_folder) |
3059 | - break |
3060 | - iter = model.next(iter) |
3061 | - if preview == None: |
3062 | - print "Couldn't find model row for requested preview uri:", uri |
3063 | - return preview |
3064 | - |
3065 | - def show_in_folder(self, scope, uri): |
3066 | - """ Open folder that contains given video """ |
3067 | - file = Gio.file_new_for_uri (uri) |
3068 | - parent = file.get_parent() |
3069 | - if parent == None: |
3070 | - parent = Gio.file_new_for_path("/") |
3071 | - try: |
3072 | - Gio.app_info_launch_default_for_uri(parent.get_uri(), None) |
3073 | - except Exception as e: |
3074 | - print "Couldn't launch default app for uri", parent.get_uri(), e |
3075 | - return Unity.ActivationResponse(handled=Unity.HandledType.NOT_HANDLED) |
3076 | - return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH) |
3077 | - |
3078 | - def on_search_changed(self, scope, search, search_type, cancellable): |
3079 | - """On a new search, differentiate between lens view |
3080 | - and global search before updating the model""" |
3081 | - search_status = search |
3082 | - search_string = search.props.search_string.strip() |
3083 | - print "Search changed to \"%s\"" % search_string |
3084 | - model = search.props.results_model |
3085 | - if self.source_activated('local'): |
3086 | - if search_type is Unity.SearchType.GLOBAL: |
3087 | - if search_string == '': |
3088 | - model.clear () |
3089 | - if search_status: |
3090 | - search_status.finished () |
3091 | - print "Global view without search string : hide" |
3092 | - |
3093 | - else: |
3094 | - self.update_results_model(search_string, model, 'global', cancellable, search_status) |
3095 | - else: |
3096 | - if not search_string: |
3097 | - # this will call update_results_model as well |
3098 | - self.zg_call(cancellable, search_status) |
3099 | - else: |
3100 | - self.update_results_model(search_string, model, 'lens', cancellable, search_status) |
3101 | - else: |
3102 | - model.clear() |
3103 | - if search_status: |
3104 | - search_status.finished () |
3105 | - |
3106 | - def update_results_model(self, search, model, cat, cancellable, search_status, clear_model=True): |
3107 | - """Check for the existence of the cache folder, create it if needed, |
3108 | - and run the search method.""" |
3109 | - if not Gio.file_new_for_path(CACHE).query_exists(None): |
3110 | - Gio.file_new_for_path(CACHE).make_directory(None) |
3111 | - if FOLDER != HOME_FOLDER: |
3112 | - try: |
3113 | - GLib.spawn_async(['/usr/bin/updatedb', '-o', CACHE+'/'+DB, |
3114 | - '-l', '0', '-U', FOLDER]) |
3115 | - except GLib.GError: |
3116 | - print "Can't create the database, will retry." |
3117 | - |
3118 | - if self.is_file(CACHE+'/'+DB): |
3119 | - try: |
3120 | - results = GLib.spawn_sync(None, |
3121 | - ['/usr/bin/locate', |
3122 | - '-id', CACHE+'/'+DB, |
3123 | - FOLDER+'*'+search.replace (" ","*")+'*' ], |
3124 | - None, 0, None, None) |
3125 | - except GLib.GError: |
3126 | - results = None |
3127 | - else: |
3128 | - # spawn_sync returns bool, stdout, stderr, exit_status |
3129 | - if results[3] == 0: |
3130 | - results = results[1] |
3131 | - else: |
3132 | - results = None |
3133 | - else: |
3134 | - results = None |
3135 | - result_list = [] |
3136 | - blacklist = self.get_blacklist () |
3137 | - if results: |
3138 | - video_counter = 0 |
3139 | - print len(results.split('\n')) |
3140 | - for video in results.split('\n'): |
3141 | - if video_counter < 100: |
3142 | - if self.is_video(video) and not self.is_hidden(video, blacklist): |
3143 | - video_counter+= 1 |
3144 | - title = self.get_name(video) |
3145 | - comment = '' |
3146 | - uri = 'file://%s' % video |
3147 | - icon = self.get_icon(video) |
3148 | - if title: |
3149 | - item = [] |
3150 | - item.append(title) |
3151 | - item.append(comment) |
3152 | - item.append(uri) |
3153 | - item.append(icon) |
3154 | - result_list.append(item) |
3155 | - result_list = self.sort_alpha(result_list) |
3156 | - |
3157 | - GLib.idle_add(self.add_results, search_status, model, cat, cancellable, result_list, search, clear_model) |
3158 | - |
3159 | - |
3160 | - def add_results (self, search_status=None, model=None, |
3161 | - cat=None, cancellable=None, result_list=[], search=None, |
3162 | - clear_model=None): |
3163 | - result_sets = [] |
3164 | - |
3165 | - if cancellable and not cancellable.is_cancelled(): |
3166 | - if cat == 'global': |
3167 | - # Create only one result set for the Global search |
3168 | - result_sets.append({'category':CAT_INDEX_MY_VIDEOS, 'results':result_list}) |
3169 | - else: |
3170 | - result_sets.append({'category':CAT_INDEX_MY_VIDEOS, 'results':result_list}) |
3171 | - if clear_model: model.clear() |
3172 | - for result_set in result_sets: |
3173 | - cat = result_set['category'] |
3174 | - for i in result_set['results']: |
3175 | - title = str(i[0]) |
3176 | - comment = str(i[1]) |
3177 | - uri = str(i[2]) |
3178 | - dnd_uri = str(i[2]) |
3179 | - icon_hint = str(i[3]) |
3180 | - model.append(uri, icon_hint, cat, "text/html", |
3181 | - title, comment, dnd_uri) |
3182 | - if search_status: |
3183 | - print "Search finished" |
3184 | - search_status.finished() |
3185 | - else: |
3186 | - print "Search cancelled" |
3187 | - return False |
3188 | - |
3189 | - def is_file(self, uri): |
3190 | - """Check if the file is an actual file""" |
3191 | - g_file = Gio.file_new_for_path(uri) |
3192 | - if g_file.query_exists(None): |
3193 | - file_type = g_file.query_file_type(Gio.FileQueryInfoFlags.NONE, |
3194 | - None) |
3195 | - if file_type is Gio.FileType.REGULAR: |
3196 | - return True |
3197 | - |
3198 | - def get_blacklist(self): |
3199 | - """Get zeitgeist blacklist""" |
3200 | - bt_list = [] |
3201 | - try: |
3202 | - iface = client.ZeitgeistDBusInterface() |
3203 | - except: |
3204 | - print "Unable to connect to Zeitgeist, won't handle blacklists." |
3205 | - iface = None |
3206 | - if iface: |
3207 | - blacklist = iface.get_extension("Blacklist", "blacklist") |
3208 | - bt_list = blacklist.GetTemplates () |
3209 | - return bt_list |
3210 | - |
3211 | - def is_hidden(self, uri, blacklist): |
3212 | - """Check if the file is hidden""" |
3213 | - g_file = Gio.file_new_for_path(uri) |
3214 | - hidden = g_file.query_info( |
3215 | - Gio.FILE_ATTRIBUTE_STANDARD_IS_HIDDEN, |
3216 | - Gio.FileQueryInfoFlags.NONE, |
3217 | - None).get_attribute_boolean('standard::is-hidden') |
3218 | - blacklisted = False |
3219 | - for bt in blacklist: |
3220 | - bt = bt.replace('dir-/', '/').encode("utf-8") |
3221 | - if uri.startswith(bt): |
3222 | - blacklisted = True |
3223 | - if hidden or uri.find('/.') > -1 or blacklisted: |
3224 | - return True |
3225 | - |
3226 | - def sort_alpha(self, results): |
3227 | - """Sort results in several ways, depending on the category""" |
3228 | - results.sort(key=lambda x: x[0].lower(), reverse=False) |
3229 | - return results |
3230 | - |
3231 | - def is_video(self, uri): |
3232 | - """Check if the file is a video""" |
3233 | - if self.is_file(uri): |
3234 | - g_file = Gio.file_new_for_path(uri) |
3235 | - content_type = g_file.query_info('standard::content-type', |
3236 | - Gio.FileQueryInfoFlags.NONE, |
3237 | - None).get_content_type() |
3238 | - if 'video' in content_type: |
3239 | - return True |
3240 | - |
3241 | - def get_name(self, uri): |
3242 | - """Get the display name of the file""" |
3243 | - g_file = Gio.file_new_for_path(uri) |
3244 | - name = g_file.query_info( |
3245 | - Gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, |
3246 | - Gio.FileQueryInfoFlags.NONE, |
3247 | - None) |
3248 | - display_name = name.get_attribute_as_string( |
3249 | - Gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME) |
3250 | - return display_name |
3251 | - |
3252 | - def get_icon(self, uri): |
3253 | - """This method checks several locations for a video thumbnail. |
3254 | - |
3255 | - 1) <filename>.jpg file in the same folder (not activated for now) |
3256 | - 1) Nautilus thumbnails |
3257 | - 2) Cached thumbnails generated by the scope |
3258 | - |
3259 | - If nothing has been found, it tries to generate a thumbnail with Totem, |
3260 | - stores and uses it. |
3261 | - If the generation fails or is slow, it fallbacks to the standard video |
3262 | - icon. |
3263 | - """ |
3264 | - icon_path = None |
3265 | - g_file = Gio.file_new_for_path(uri) |
3266 | - video_path = g_file.get_path() |
3267 | - thumb_name = video_path.replace(FOLDER, '').replace('/', '_') |
3268 | - icon_check = '%s/thumb_%s.png' % (CACHE, thumb_name) |
3269 | - # if not icon_path: |
3270 | - # print 'Check for local cover' |
3271 | - # local_cover = uri.replace('.%s' % uri.split('.')[-1], '.jpg') |
3272 | - # if self.is_file(local_cover): |
3273 | - # icon_path = local_cover |
3274 | - if not icon_path: |
3275 | - icon = g_file.query_info( |
3276 | - ','.join((Gio.FILE_ATTRIBUTE_THUMBNAIL_PATH, |
3277 | - Gio.FILE_ATTRIBUTE_THUMBNAILING_FAILED)), |
3278 | - Gio.FileQueryInfoFlags.NONE, |
3279 | - None) |
3280 | - if icon.get_attribute_boolean(Gio.FILE_ATTRIBUTE_THUMBNAILING_FAILED): |
3281 | - icon_path = 'video' |
3282 | - else: |
3283 | - icon_path = icon.get_attribute_as_string( |
3284 | - Gio.FILE_ATTRIBUTE_THUMBNAIL_PATH) |
3285 | - if not icon_path: |
3286 | - if self.is_file(icon_check): |
3287 | - icon_path = icon_check |
3288 | - if not icon_path: |
3289 | - # Check if processes can be removed from the thumbnailing queue |
3290 | - for process in Q: |
3291 | - if not Gio.file_new_for_path( |
3292 | - "/proc/"+str(process)).query_exists(None): |
3293 | - Q.remove(process) |
3294 | - if len(Q) < Q_MAX: |
3295 | - try: |
3296 | - p = GLib.spawn_async(['/usr/bin/totem-video-thumbnailer', |
3297 | - video_path, icon_check]) |
3298 | - Q.append(p[0]) |
3299 | - if self.is_file(icon_check): |
3300 | - icon_path = icon_check |
3301 | - except: |
3302 | - print "Warning : the file may have been removed." |
3303 | - if not icon_path: |
3304 | - icon_path = 'video' |
3305 | - return icon_path |
3306 | - |
3307 | - def zg_call (self, cancellable, search_status): |
3308 | - active = self._scope.props.sources.get_option("local").props.active |
3309 | - filtering = self._scope.props.sources.props.filtering |
3310 | - if active and filtering: |
3311 | - uri = "file:*" |
3312 | - else: |
3313 | - uri = "*" |
3314 | - time_range = datamodel.TimeRange.until_now () |
3315 | - max_amount_results = 24 |
3316 | - event_template = datamodel.Event() |
3317 | - interpretation = datamodel.Interpretation.VIDEO |
3318 | - event_template.append_subject( |
3319 | - datamodel.Subject.new_for_values(uri=uri,interpretation=interpretation)) |
3320 | - def wrap(callback): |
3321 | - def wrapped(events): |
3322 | - callback(events, cancellable, search_status) |
3323 | - |
3324 | - return wrapped |
3325 | - |
3326 | - ZG.find_events_for_templates( |
3327 | - [event_template, ], |
3328 | - wrap(self.progress_zg_events), |
3329 | - timerange = time_range, |
3330 | - storage_state = datamodel.StorageState.Any, |
3331 | - num_events = max_amount_results, |
3332 | - result_type = datamodel.ResultType.MostRecentSubjects |
3333 | - ) |
3334 | - |
3335 | - def progress_zg_events(self, events, cancellable, search_status): |
3336 | - if cancellable.is_cancelled(): return |
3337 | - blacklist = self.get_blacklist () |
3338 | - result_list = [] |
3339 | - for event in events: |
3340 | - item = [] |
3341 | - uri = event.get_subjects()[0].uri |
3342 | - if uri.startswith('file://'): |
3343 | - # If the file is local, we use the same methods |
3344 | - # as other result items. |
3345 | - g_file = Gio.file_new_for_uri(uri) |
3346 | - path = g_file.get_path() |
3347 | - if self.is_video(path) and not self.is_hidden(path, blacklist): |
3348 | - item.append(self.get_name(path)) |
3349 | - item.append('') |
3350 | - item.append(uri.encode("utf-8")) |
3351 | - item.append(self.get_icon(path)) |
3352 | - item.append(CAT_INDEX_MY_VIDEOS) |
3353 | - result_list.append(item) |
3354 | - elif uri.startswith('http'): |
3355 | - # If the file is distant, we take |
3356 | - # all we need from Zeitgeist |
3357 | - # this one can be any unicode string: |
3358 | - item.append(event.get_subjects()[0].text.encode("utf-8")) |
3359 | - item.append('') |
3360 | - # these two *should* be ascii, but it can't hurt to be safe |
3361 | - item.append(event.get_subjects()[0].uri.encode("utf-8")) |
3362 | - item.append(event.get_subjects()[0].storage.encode("utf-8")) |
3363 | - item.append(CAT_INDEX_ONLINE) |
3364 | - result_list.append(item) |
3365 | - search_status.props.results_model.clear () |
3366 | - for i in result_list: |
3367 | - title = str(i[0]) |
3368 | - comment = str(i[1]) |
3369 | - uri = str(i[2]) |
3370 | - dnd_uri = str(i[2]) |
3371 | - icon_hint = str(i[3]) |
3372 | - category = i[4] |
3373 | - self._scope.props.results_model.append(uri, icon_hint, |
3374 | - category, "text/html", title, comment, dnd_uri) |
3375 | - |
3376 | - self.update_results_model("", search_status.props.results_model, 'lens', cancellable, search_status, False) |
3377 | - |
3378 | -# pylint: enable=R0903 |
3379 | - |
3380 | -def main(): |
3381 | - """Connect to the session bus, exit if there is a running instance.""" |
3382 | - try: |
3383 | - session_bus_connection = Gio.bus_get_sync(Gio.BusType.SESSION, None) |
3384 | - session_bus = Gio.DBusProxy.new_sync(session_bus_connection, 0, None, |
3385 | - 'org.freedesktop.DBus', |
3386 | - '/org/freedesktop/DBus', |
3387 | - 'org.freedesktop.DBus', None) |
3388 | - result = session_bus.call_sync('RequestName', |
3389 | - GLib.Variant("(su)", (BUS_NAME, 0x4)), |
3390 | - 0, -1, None) |
3391 | - |
3392 | - # Unpack variant response with signature "(u)". 1 means we got it. |
3393 | - result = result.unpack()[0] |
3394 | - |
3395 | - if result != 1: |
3396 | - print >> sys.stderr, "Failed to own name %s. Bailing out." % BUS_NAME |
3397 | - raise SystemExit(1) |
3398 | - except: |
3399 | - raise SystemExit(1) |
3400 | - |
3401 | - daemon = Daemon() |
3402 | - GObject.MainLoop().run() |
3403 | - |
3404 | -if __name__ == "__main__": |
3405 | - main() |
3406 | |
3407 | === added file 'src/utils.vala' |
3408 | --- src/utils.vala 1970-01-01 00:00:00 +0000 |
3409 | +++ src/utils.vala 2013-02-19 17:19:01 +0000 |
3410 | @@ -0,0 +1,77 @@ |
3411 | +/* |
3412 | + * Copyright (C) 2012 Canonical Ltd |
3413 | + * |
3414 | + * This program is free software: you can redistribute it and/or modify |
3415 | + * it under the terms of the GNU General Public License version 3 as |
3416 | + * published by the Free Software Foundation. |
3417 | + * |
3418 | + * This program is distributed in the hope that it will be useful, |
3419 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3420 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3421 | + * GNU General Public License for more details. |
3422 | + * |
3423 | + * You should have received a copy of the GNU General Public License |
3424 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3425 | + * |
3426 | + * Authored by Pawel Stolowski <pawel.stolowski@canonical.com> |
3427 | + * based on python code by David Calle <davidc@framli.eu> |
3428 | + */ |
3429 | + |
3430 | +namespace Unity.Utils |
3431 | +{ |
3432 | + public bool is_regular_file (string path) |
3433 | + { |
3434 | + var file = GLib.File.new_for_path (path); |
3435 | + if (file.query_exists (null)) |
3436 | + return file.query_file_type (GLib.FileQueryInfoFlags.NONE, null) == GLib.FileType.REGULAR; |
3437 | + return false; |
3438 | + } |
3439 | + |
3440 | + public bool is_video (string path) throws Error |
3441 | + { |
3442 | + var file = GLib.File.new_for_path (path); |
3443 | + if (file.query_exists (null)) |
3444 | + { |
3445 | + if (file.query_file_type (GLib.FileQueryInfoFlags.NONE, null) == GLib.FileType.REGULAR) |
3446 | + { |
3447 | + var content_type = file.query_info ("standard::content-type", GLib.FileQueryInfoFlags.NONE, null).get_content_type (); |
3448 | + return content_type.contains ("video"); |
3449 | + } |
3450 | + } |
3451 | + return false; |
3452 | + } |
3453 | + |
3454 | + private bool is_hidden (string path) throws Error |
3455 | + { |
3456 | + var file = GLib.File.new_for_path (path); |
3457 | + return file.query_info (GLib.FileAttribute.STANDARD_IS_HIDDEN, GLib.FileQueryInfoFlags.NONE, null).get_is_hidden (); |
3458 | + } |
3459 | + |
3460 | + public string get_name (string path) throws Error |
3461 | + { |
3462 | + var file = GLib.File.new_for_path (path); |
3463 | + var finfo = file.query_info (GLib.FileAttribute.STANDARD_DISPLAY_NAME, GLib.FileQueryInfoFlags.NONE, null); |
3464 | + return finfo.get_attribute_as_string (GLib.FileAttribute.STANDARD_DISPLAY_NAME); |
3465 | + } |
3466 | + |
3467 | + public uint gcd (uint a, uint b) |
3468 | + requires (a > 0 && b > 0) |
3469 | + ensures (result > 0) |
3470 | + { |
3471 | + for (;;) |
3472 | + { |
3473 | + if (a > b) |
3474 | + { |
3475 | + a = a % b; |
3476 | + if (a == 0) |
3477 | + return b; |
3478 | + } |
3479 | + else |
3480 | + { |
3481 | + b = b % a; |
3482 | + if (b == 0) |
3483 | + return a; |
3484 | + } |
3485 | + } |
3486 | + } |
3487 | +} |
3488 | \ No newline at end of file |
3489 | |
3490 | === added file 'src/video-file.vala' |
3491 | --- src/video-file.vala 1970-01-01 00:00:00 +0000 |
3492 | +++ src/video-file.vala 2013-02-19 17:19:01 +0000 |
3493 | @@ -0,0 +1,59 @@ |
3494 | +/* |
3495 | + * Copyright (C) 2012 Canonical Ltd |
3496 | + * |
3497 | + * This program is free software: you can redistribute it and/or modify |
3498 | + * it under the terms of the GNU General Public License version 3 as |
3499 | + * published by the Free Software Foundation. |
3500 | + * |
3501 | + * This program is distributed in the hope that it will be useful, |
3502 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3503 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3504 | + * GNU General Public License for more details. |
3505 | + * |
3506 | + * You should have received a copy of the GNU General Public License |
3507 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3508 | + * |
3509 | + * Authored by Pawel Stolowski <pawel.stolowski@canonical.com> |
3510 | + * |
3511 | + */ |
3512 | + |
3513 | +namespace Unity.VideoLens |
3514 | +{ |
3515 | + public struct VideoFile |
3516 | + { |
3517 | + string title; |
3518 | + string lc_title; |
3519 | + string comment; |
3520 | + string uri; |
3521 | + string icon; |
3522 | + int category; |
3523 | + } |
3524 | + |
3525 | + public struct RemoteVideoFile |
3526 | + { |
3527 | + string title; |
3528 | + string comment; |
3529 | + string uri; |
3530 | + string icon; |
3531 | + string details_uri; |
3532 | + string price; |
3533 | + int category; |
3534 | + } |
3535 | + |
3536 | +public struct RemoteVideoDetails |
3537 | + { |
3538 | + string title; |
3539 | + string description; |
3540 | + string uri; |
3541 | + string image; |
3542 | + string source; |
3543 | + string release_date; |
3544 | + int duration; |
3545 | + string[] directors; |
3546 | + string starring; |
3547 | + string[] genres; |
3548 | + string uploaded_by; |
3549 | + string date_uploaded; |
3550 | + string price; |
3551 | + } |
3552 | +} |
3553 | \ No newline at end of file |
3554 | |
3555 | === added directory 'tests/unit' |
3556 | === added file 'tests/unit/Makefile.am' |
3557 | --- tests/unit/Makefile.am 1970-01-01 00:00:00 +0000 |
3558 | +++ tests/unit/Makefile.am 2013-02-19 17:19:01 +0000 |
3559 | @@ -0,0 +1,120 @@ |
3560 | +include $(top_srcdir)/Makefile.decl |
3561 | + |
3562 | +check_PROGRAMS = \ |
3563 | + test-locate \ |
3564 | + test-utils \ |
3565 | + test-ubuntu-video-search \ |
3566 | + $(NULL) |
3567 | + |
3568 | +TEST_PROGS += $(check_PROGRAMS) |
3569 | + |
3570 | +AM_VALAFLAGS = \ |
3571 | + --pkg dee-1.0 \ |
3572 | + --pkg unity \ |
3573 | + --pkg unity-extras \ |
3574 | + --pkg gio-2.0 \ |
3575 | + --pkg gio-unix-2.0 \ |
3576 | + --pkg json-glib-1.0 \ |
3577 | + --pkg glib-2.0 \ |
3578 | + --pkg zeitgeist-1.0 \ |
3579 | + --pkg libsoup-gnome-2.4 \ |
3580 | + --pkg libsoup-2.4 \ |
3581 | + --vapidir $(srcdir) \ |
3582 | + --vapidir $(top_srcdir)/vapi \ |
3583 | + --target-glib=2.26 \ |
3584 | + $(MAINTAINER_VALAFLAGS) \ |
3585 | + $(NULL) |
3586 | + |
3587 | +LDADD = $(LENS_DAEMON_LIBS) \ |
3588 | + $(test_libs) |
3589 | + |
3590 | +AM_CPPFLAGS = \ |
3591 | + $(LENS_DAEMON_CFLAGS) \ |
3592 | + -I$(srcdir) \ |
3593 | + -I$(top_srcdir)/src \ |
3594 | + -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \ |
3595 | + $(COVERAGE_CFLAGS) \ |
3596 | + $(NULL) |
3597 | + |
3598 | +AM_LDFLAGS = $(COVERAGE_LDFLAGS) |
3599 | + |
3600 | +if !ENABLE_C_WARNINGS |
3601 | + AM_CPPFLAGS += -w |
3602 | +endif |
3603 | + |
3604 | +test_locate_VALASOURCES = \ |
3605 | + test-locate.vala \ |
3606 | + thumbnailer-mock.vala \ |
3607 | + config-tests.vala \ |
3608 | + $(top_srcdir)/src/locate.vala \ |
3609 | + $(top_srcdir)/src/utils.vala \ |
3610 | + $(top_srcdir)/src/video-file.vala \ |
3611 | + $(NULL) |
3612 | + |
3613 | +test_utils_VALASOURCES = \ |
3614 | + test-utils.vala \ |
3615 | + config-tests.vala \ |
3616 | + $(top_srcdir)/src/utils.vala \ |
3617 | + $(NULL) |
3618 | + |
3619 | +test_ubuntu_video_search_VALASOURCES = \ |
3620 | + test-ubuntu-video-search.vala \ |
3621 | + config-tests.vala \ |
3622 | + $(top_srcdir)/src/remote-scope-globals.vala \ |
3623 | + $(top_srcdir)/src/ubuntu-video-search.vala \ |
3624 | + $(top_srcdir)/src/video-file.vala \ |
3625 | + $(top_srcdir)/src/remote-scope.vala \ |
3626 | + $(top_srcdir)/src/remote-uri.vala \ |
3627 | + $(top_srcdir)/src/utils.vala \ |
3628 | + $(NULL) |
3629 | + |
3630 | +nodist_test_locate_SOURCES = $(test_locate_VALASOURCES:.vala=.c) |
3631 | + |
3632 | +nodist_test_utils_SOURCES = $(test_utils_VALASOURCES:.vala=.c) |
3633 | + |
3634 | +nodist_test_ubuntu_video_search_SOURCES = $(test_ubuntu_video_search_VALASOURCES:.vala=.c) |
3635 | + |
3636 | +CLEANFILES = *.stamp \ |
3637 | + *.c \ |
3638 | + $(NULL) |
3639 | + |
3640 | +EXTRA_DIST = \ |
3641 | + $(test_locate_VALASOURCES) \ |
3642 | + $(test_utils_VALASOURCES) \ |
3643 | + $(test_ubuntu_video_search_VALASOURCES) \ |
3644 | + data/videosearch_input1.txt \ |
3645 | + data/videosearch_input2.txt \ |
3646 | + data/videosearch_details1.txt \ |
3647 | + data/video1.avi \ |
3648 | + data/video2.avi \ |
3649 | + data/video3.avi \ |
3650 | + data/video1.mpg \ |
3651 | + data/video2.mpg \ |
3652 | + data/video3.mpg \ |
3653 | + $(NULL) |
3654 | + |
3655 | +BUILT_SOURCES = \ |
3656 | + test-locate.vala.stamp \ |
3657 | + test-utils.vala.stamp \ |
3658 | + test-ubuntu-video-search.vala.stamp \ |
3659 | + $(NULL) |
3660 | + |
3661 | +test-locate.vala.stamp: $(test_locate_VALASOURCES) |
3662 | + $(AM_V_GEN)$(VALAC) -C $(AM_VALAFLAGS) $(VALAFLAGS) $^ |
3663 | + @touch $@ |
3664 | + |
3665 | +test-utils.vala.stamp: $(test_utils_VALASOURCES) |
3666 | + $(AM_V_GEN)$(VALAC) -C $(AM_VALAFLAGS) $(VALAFLAGS) $^ |
3667 | + @touch $@ |
3668 | + |
3669 | +test-ubuntu-video-search.vala.stamp: $(test_ubuntu_video_search_VALASOURCES) |
3670 | + $(AM_V_GEN)$(VALAC) -C $(AM_VALAFLAGS) $(VALAFLAGS) $^ |
3671 | + @touch $@ |
3672 | + |
3673 | +# START HEADLESS TESTS |
3674 | +if ENABLE_HEADLESS_TESTS |
3675 | +test-headless: |
3676 | + $(XVFB) make test-nonrecursive; \ |
3677 | + sleep 1; |
3678 | +endif |
3679 | +# END HEADLESS TESTS |
3680 | |
3681 | === added file 'tests/unit/config-tests.vala.in' |
3682 | --- tests/unit/config-tests.vala.in 1970-01-01 00:00:00 +0000 |
3683 | +++ tests/unit/config-tests.vala.in 2013-02-19 17:19:01 +0000 |
3684 | @@ -0,0 +1,6 @@ |
3685 | +namespace Config { |
3686 | + const string TESTDATADIR = "@abs_top_srcdir@/tests/unit/data"; |
3687 | + const string LOCALEDIR = "@DATADIR@/locale"; |
3688 | + const string PACKAGE = "@PACKAGE@"; |
3689 | + const string VERSION = "@VERSION@"; |
3690 | +} |
3691 | |
3692 | === added directory 'tests/unit/data' |
3693 | === added file 'tests/unit/data/video1.avi' |
3694 | === added file 'tests/unit/data/video1.mpg' |
3695 | === added file 'tests/unit/data/video2.avi' |
3696 | === added file 'tests/unit/data/video2.mpg' |
3697 | === added file 'tests/unit/data/video3.avi' |
3698 | === added file 'tests/unit/data/video3.mpg' |
3699 | === added file 'tests/unit/data/videosearch_details1.txt' |
3700 | --- tests/unit/data/videosearch_details1.txt 1970-01-01 00:00:00 +0000 |
3701 | +++ tests/unit/data/videosearch_details1.txt 2013-02-19 17:19:01 +0000 |
3702 | @@ -0,0 +1,26 @@ |
3703 | +{ |
3704 | + "browser_url": "http://browserurl1", |
3705 | + "genres": [ |
3706 | + "genre1", |
3707 | + "genre2" |
3708 | + ], |
3709 | + "description": "a movie", |
3710 | + "title": "title1", |
3711 | + "price": 1, |
3712 | + "source": "source1", |
3713 | + "directors": [ |
3714 | + "director1", |
3715 | + "director2" |
3716 | + ], |
3717 | + "details": "http://detailsurl1", |
3718 | + "starring": [ |
3719 | + "star1", |
3720 | + "star2" |
3721 | + ], |
3722 | + "uploaded_by": "uploader1", |
3723 | + "duration": "5477", |
3724 | + "release_date": "2010-04-01T07:00:00.000Z", |
3725 | + "image": "http://image1", |
3726 | + "date_uploaded": "2012-06-06T21:55:12.000Z", |
3727 | + "formatted_price": "$1" |
3728 | +} |
3729 | |
3730 | === added file 'tests/unit/data/videosearch_input1.txt' |
3731 | --- tests/unit/data/videosearch_input1.txt 1970-01-01 00:00:00 +0000 |
3732 | +++ tests/unit/data/videosearch_input1.txt 2013-02-19 17:19:01 +0000 |
3733 | @@ -0,0 +1,44 @@ |
3734 | +{ |
3735 | + "other": [ |
3736 | + { |
3737 | + "url": "http://url0", |
3738 | + "source": "source0", |
3739 | + "img": "http://image0", |
3740 | + "title": "title0" |
3741 | + }, |
3742 | + { |
3743 | + "url": "http://url1", |
3744 | + "source": "source1", |
3745 | + "details": "http://details1", |
3746 | + "img": "http://image1", |
3747 | + "title": "title1" |
3748 | + } |
3749 | + ], |
3750 | + "treats": [ |
3751 | + { |
3752 | + "img": "http://image2", |
3753 | + "title": "title2", |
3754 | + "url": "http://url2", |
3755 | + "price": 1, |
3756 | + "source": "source2", |
3757 | + "details": "http://details2", |
3758 | + "formatted_price": "1 USD" |
3759 | + }, |
3760 | + { |
3761 | + "img": "http://image3", |
3762 | + "title": "title3", |
3763 | + "url": "http://url3", |
3764 | + "price": 0, |
3765 | + "source": "source3", |
3766 | + "details": "http://details3", |
3767 | + "formatted_price": "free" |
3768 | + }, |
3769 | + { |
3770 | + "img": "http://image4", |
3771 | + "title": "title4", |
3772 | + "url": "http://url4", |
3773 | + "source": "source4", |
3774 | + "details": "http://details4" |
3775 | + } |
3776 | + ] |
3777 | +} |
3778 | |
3779 | === added file 'tests/unit/data/videosearch_input2.txt' |
3780 | --- tests/unit/data/videosearch_input2.txt 1970-01-01 00:00:00 +0000 |
3781 | +++ tests/unit/data/videosearch_input2.txt 2013-02-19 17:19:01 +0000 |
3782 | @@ -0,0 +1,17 @@ |
3783 | +[ |
3784 | + { |
3785 | + "url": "http://url0", |
3786 | + "source": "source0", |
3787 | + "img": "http://image0", |
3788 | + "title": "title0" |
3789 | + }, |
3790 | + { |
3791 | + "url": "http://url1", |
3792 | + "source": "source1", |
3793 | + "details": "http://details1", |
3794 | + "img": "http://image1", |
3795 | + "title": "title1", |
3796 | + "price": 1, |
3797 | + "formatted_price": "1 USD" |
3798 | + } |
3799 | +] |
3800 | |
3801 | === added file 'tests/unit/test-locate.vala' |
3802 | --- tests/unit/test-locate.vala 1970-01-01 00:00:00 +0000 |
3803 | +++ tests/unit/test-locate.vala 2013-02-19 17:19:01 +0000 |
3804 | @@ -0,0 +1,57 @@ |
3805 | +/* |
3806 | + * Copyright (C) 2012 Canonical Ltd |
3807 | + * |
3808 | + * This program is free software: you can redistribute it and/or modify |
3809 | + * it under the terms of the GNU General Public License version 3 as |
3810 | + * published by the Free Software Foundation. |
3811 | + * |
3812 | + * This program is distributed in the hope that it will be useful, |
3813 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3814 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3815 | + * GNU General Public License for more details. |
3816 | + * |
3817 | + * You should have received a copy of the GNU General Public License |
3818 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3819 | + * |
3820 | + * Authored by Pawel Stolowski <pawel.stolowski@canonical.com> |
3821 | + */ |
3822 | + |
3823 | +namespace Unity.VideoLens |
3824 | +{ |
3825 | + private Thumbnailer thumbnailer; |
3826 | + private Locate locate; |
3827 | + private string video_dir; |
3828 | + private string user_videos_folder; |
3829 | + |
3830 | + public static int main (string[] args) |
3831 | + { |
3832 | + video_dir = Config.TESTDATADIR; |
3833 | + user_videos_folder = GLib.Environment.get_user_special_dir (GLib.UserDirectory.VIDEOS); |
3834 | + |
3835 | + locate = new Locate ("cache", video_dir); |
3836 | + thumbnailer = new Thumbnailer (); |
3837 | + |
3838 | + Test.init (ref args); |
3839 | + Test.add_data_func ("/Locate/ParseLocateResults", test_parse); |
3840 | + Test.add_data_func ("/Locate/LocateQueryString", test_query_string); |
3841 | + Test.run (); |
3842 | + return 0; |
3843 | + } |
3844 | + |
3845 | + internal static void test_parse () |
3846 | + { |
3847 | + string input = @"$video_dir/video1.avi\n$video_dir/video2.avi\n$video_dir/video1.mpg\n$video_dir/video2.mpg\n"; |
3848 | + var results = locate.parse_locate_results (input, 1000, thumbnailer, (path) => { return true; }); |
3849 | + assert (results.size == 4); |
3850 | + |
3851 | + /* Filter out all results */ |
3852 | + results = locate.parse_locate_results (input, 1000, thumbnailer, (path) => { return false; }); |
3853 | + assert (results.size == 0); |
3854 | + } |
3855 | + |
3856 | + internal static void test_query_string () |
3857 | + { |
3858 | + assert (locate.locate_query_string ("foo") == video_dir + "*foo*"); |
3859 | + assert (locate.locate_query_string ("foo bar") == video_dir + "*foo*bar*"); |
3860 | + } |
3861 | +} |
3862 | \ No newline at end of file |
3863 | |
3864 | === added file 'tests/unit/test-ubuntu-video-search.vala' |
3865 | --- tests/unit/test-ubuntu-video-search.vala 1970-01-01 00:00:00 +0000 |
3866 | +++ tests/unit/test-ubuntu-video-search.vala 2013-02-19 17:19:01 +0000 |
3867 | @@ -0,0 +1,329 @@ |
3868 | +/* |
3869 | + * Copyright (C) 2012 Canonical Ltd |
3870 | + * |
3871 | + * This program is free software: you can redistribute it and/or modify |
3872 | + * it under the terms of the GNU General Public License version 3 as |
3873 | + * published by the Free Software Foundation. |
3874 | + * |
3875 | + * This program is distributed in the hope that it will be useful, |
3876 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3877 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3878 | + * GNU General Public License for more details. |
3879 | + * |
3880 | + * You should have received a copy of the GNU General Public License |
3881 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3882 | + * |
3883 | + * Authored by Pawel Stolowski <pawel.stolowski@canonical.com> |
3884 | + * |
3885 | + */ |
3886 | + |
3887 | +namespace Unity.VideoLens |
3888 | +{ |
3889 | + public static int main (string[] args) |
3890 | + { |
3891 | + /* Sort up locale to get translations but also sorting and |
3892 | + * punctuation right */ |
3893 | + GLib.Intl.textdomain (Config.PACKAGE); |
3894 | + GLib.Intl.bindtextdomain (Config.PACKAGE, Config.LOCALEDIR); |
3895 | + GLib.Intl.bind_textdomain_codeset (Config.PACKAGE, "UTF-8"); |
3896 | + GLib.Intl.setlocale(GLib.LocaleCategory.ALL, ""); |
3897 | + |
3898 | + Test.init (ref args); |
3899 | + Test.add_data_func ("/UbuntuVideoSearch/ProcessSourcesResults", test_process_sources_results); |
3900 | + Test.add_data_func ("/UbuntuVideoSearch/ProcessInvalidSourcesResults", test_process_invalid_sources_results); |
3901 | + Test.add_data_func ("/UbuntuVideoSearch/BuildSearchUri", test_build_search_uri); |
3902 | + Test.add_data_func ("/UbuntuVideoSearch/ProcessInvalidSearchResultsJson", test_invalid_search_results_json); |
3903 | + Test.add_data_func ("/UbuntuVideoSearch/ProcessSearchResults", test_process_search_results); |
3904 | + Test.add_data_func ("/UbuntuVideoSearch/ProcessSearchResultsTreatYourself", test_process_search_no_split_ty_category); |
3905 | + Test.add_data_func ("/UbuntuVideoSearch/ProcessSearchResultsOnline", test_process_search_no_split_online_category); |
3906 | + Test.add_data_func ("/UbuntuVideoSearch/JoinArray", test_join_array); |
3907 | + Test.add_data_func ("/UbuntuVideoSearch/ProcessInvalidDetailsResults", test_process_invalid_details_results); |
3908 | + Test.add_data_func ("/UbuntuVideoSearch/ProcessDetailsResults", test_process_details_results); |
3909 | + |
3910 | + Test.run (); |
3911 | + |
3912 | + return 0; |
3913 | + } |
3914 | + |
3915 | + internal static void test_process_sources_results () |
3916 | + { |
3917 | + assert (UbuntuVideoSearch.process_sources_results ("[]").size == 0); |
3918 | + |
3919 | + var sources = UbuntuVideoSearch.process_sources_results ("[\"Foo\", \"Bar\"]"); |
3920 | + assert (sources.size == 2); |
3921 | + assert (sources[0] == "Foo"); |
3922 | + assert (sources[1] == "Bar"); |
3923 | + } |
3924 | + |
3925 | + internal static void test_process_invalid_sources_results () |
3926 | + { |
3927 | + bool got_excp = false; |
3928 | + try |
3929 | + { |
3930 | + UbuntuVideoSearch.process_sources_results (";"); |
3931 | + } |
3932 | + catch (Error e) |
3933 | + { |
3934 | + got_excp = true; |
3935 | + } |
3936 | + assert (got_excp == true); |
3937 | + } |
3938 | + |
3939 | + internal static void test_build_search_uri () |
3940 | + { |
3941 | + // test null sources |
3942 | + assert (UbuntuVideoSearch.build_search_uri ("foo", null) == "http://videosearch.ubuntu.com/v0/search?q=foo&split=true"); |
3943 | + |
3944 | + // test empty sources list |
3945 | + var sources = new Gee.ArrayList<string> (null); |
3946 | + assert (UbuntuVideoSearch.build_search_uri ("foo", sources) == "http://videosearch.ubuntu.com/v0/search?q=foo&split=true"); |
3947 | + |
3948 | + // test non-empty sources and uri escaping |
3949 | + sources.add ("Amazon"); |
3950 | + sources.add ("BBC"); |
3951 | + assert (UbuntuVideoSearch.build_search_uri ("foo!", sources) == "http://videosearch.ubuntu.com/v0/search?q=foo%21&split=true&sources=Amazon,BBC"); |
3952 | + } |
3953 | + |
3954 | + internal static void test_invalid_search_results_json () |
3955 | + { |
3956 | + // ignore warnings |
3957 | + Test.log_set_fatal_handler (() => { return false; }); |
3958 | + |
3959 | + bool got_excp = false; |
3960 | + try |
3961 | + { |
3962 | + UbuntuVideoSearch.process_search_results (",", false); |
3963 | + } |
3964 | + catch (Error e) |
3965 | + { |
3966 | + got_excp = true; |
3967 | + } |
3968 | + assert (got_excp == true); |
3969 | + } |
3970 | + |
3971 | + /** |
3972 | + * Test search results of search query with 'split=true' flag, resulting in search results |
3973 | + * being placed in CAT_INDEX_MORE (if from 'treats' results group) and CAT_INDEX_ONLINE category (if from 'other' group). |
3974 | + */ |
3975 | + internal static void test_process_search_results () |
3976 | + { |
3977 | + string datadir = Config.TESTDATADIR; |
3978 | + uint8[] contents; |
3979 | + assert (File.new_for_path (@"$datadir/videosearch_input1.txt").load_contents (null, out contents, null) == true); |
3980 | + |
3981 | + var results = UbuntuVideoSearch.process_search_results ((string)contents, false); |
3982 | + assert (results.size == 5); |
3983 | + |
3984 | + bool got_video[5] = {false, false, false, false, false}; |
3985 | + |
3986 | + // verify that all expected records are there |
3987 | + foreach (var res in results) |
3988 | + { |
3989 | + if (res.title == "title0") |
3990 | + { |
3991 | + got_video[0] = true; |
3992 | + assert (res.comment == "source0"); |
3993 | + assert (res.uri == "http://url0"); |
3994 | + assert (res.icon == "http://image0"); |
3995 | + assert (res.details_uri == ""); |
3996 | + assert (res.price == null); |
3997 | + assert (res.category == CAT_INDEX_ONLINE); |
3998 | + } |
3999 | + else if (res.title == "title1") |
4000 | + { |
4001 | + got_video[1] = true; |
4002 | + assert (res.comment == "source1"); |
4003 | + assert (res.uri == "http://url1"); |
4004 | + assert (res.icon == "http://image1"); |
4005 | + assert (res.details_uri == "http://details1"); |
4006 | + assert (res.price == null); |
4007 | + assert (res.category == CAT_INDEX_ONLINE); |
4008 | + } |
4009 | + else if (res.title == "title2") |
4010 | + { |
4011 | + got_video[2] = true; |
4012 | + assert (res.comment == "source2"); |
4013 | + assert (res.uri == "http://url2"); |
4014 | + assert (res.icon == "http://image2"); |
4015 | + assert (res.details_uri == "http://details2"); |
4016 | + assert (res.price == "1 USD"); |
4017 | + assert (res.category == CAT_INDEX_MORE); |
4018 | + } |
4019 | + else if (res.title == "title3") |
4020 | + { |
4021 | + got_video[3] = true; |
4022 | + assert (res.comment == "source3"); |
4023 | + assert (res.uri == "http://url3"); |
4024 | + assert (res.icon == "http://image3"); |
4025 | + assert (res.details_uri == "http://details3"); |
4026 | + assert (res.price == _("Free")); |
4027 | + assert (res.category == CAT_INDEX_MORE); |
4028 | + } |
4029 | + else if (res.title == "title4") |
4030 | + { |
4031 | + got_video[4] = true; |
4032 | + assert (res.comment == "source4"); |
4033 | + assert (res.uri == "http://url4"); |
4034 | + assert (res.icon == "http://image4"); |
4035 | + assert (res.details_uri == "http://details4"); |
4036 | + assert (res.price == _("Free")); // null price of video4 turned into 'free' for 'Treat yourself category' |
4037 | + assert (res.category == CAT_INDEX_MORE); |
4038 | + } |
4039 | + else |
4040 | + { |
4041 | + assert (1 == 0); // this shouldn't happen |
4042 | + } |
4043 | + } |
4044 | + |
4045 | + assert (got_video[0] == true); |
4046 | + assert (got_video[1] == true); |
4047 | + assert (got_video[2] == true); |
4048 | + assert (got_video[3] == true); |
4049 | + assert (got_video[4] == true); |
4050 | + } |
4051 | + |
4052 | + /** |
4053 | + * Test search results of search query with no 'split' option and is_treat_yourself=true, resulting in all search results |
4054 | + * being placed in CAT_INDEX_MORE category. |
4055 | + */ |
4056 | + internal static void test_process_search_no_split_ty_category () |
4057 | + { |
4058 | + string datadir = Config.TESTDATADIR; |
4059 | + uint8[] contents; |
4060 | + assert (File.new_for_path (@"$datadir/videosearch_input2.txt").load_contents (null, out contents, null) == true); |
4061 | + |
4062 | + var results = UbuntuVideoSearch.process_search_results ((string)contents, /* is_treat_yourself */ true); |
4063 | + assert (results.size == 2); |
4064 | + |
4065 | + bool got_video[2] = {false, false}; |
4066 | + |
4067 | + // verify that all expected records are there |
4068 | + foreach (var res in results) |
4069 | + { |
4070 | + if (res.title == "title0") |
4071 | + { |
4072 | + got_video[0] = true; |
4073 | + assert (res.comment == "source0"); |
4074 | + assert (res.uri == "http://url0"); |
4075 | + assert (res.icon == "http://image0"); |
4076 | + assert (res.details_uri == ""); |
4077 | + assert (res.price == _("Free")); |
4078 | + assert (res.category == CAT_INDEX_MORE); |
4079 | + } |
4080 | + else if (res.title == "title1") |
4081 | + { |
4082 | + got_video[1] = true; |
4083 | + assert (res.comment == "source1"); |
4084 | + assert (res.uri == "http://url1"); |
4085 | + assert (res.icon == "http://image1"); |
4086 | + assert (res.details_uri == "http://details1"); |
4087 | + assert (res.price == "1 USD"); |
4088 | + assert (res.category == CAT_INDEX_MORE); |
4089 | + } |
4090 | + else |
4091 | + { |
4092 | + assert (1 == 0); // this shouldn't happen |
4093 | + } |
4094 | + } |
4095 | + |
4096 | + assert (got_video[0] == true); |
4097 | + assert (got_video[1] == true); |
4098 | + } |
4099 | + |
4100 | + /** |
4101 | + * Test search results of search query with no 'split' option and is_treat_yourself=false, resulting in all search results |
4102 | + * being placed in CAT_INDEX_ONLINE |
4103 | + */ |
4104 | + internal static void test_process_search_no_split_online_category () |
4105 | + { |
4106 | + string datadir = Config.TESTDATADIR; |
4107 | + uint8[] contents; |
4108 | + assert (File.new_for_path (@"$datadir/videosearch_input2.txt").load_contents (null, out contents, null) == true); |
4109 | + |
4110 | + var results = UbuntuVideoSearch.process_search_results ((string)contents, /* is_treat_yourself */ false); |
4111 | + assert (results.size == 2); |
4112 | + |
4113 | + bool got_video[2] = {false, false}; |
4114 | + |
4115 | + // verify that all expected records are there |
4116 | + foreach (var res in results) |
4117 | + { |
4118 | + if (res.title == "title0") |
4119 | + { |
4120 | + got_video[0] = true; |
4121 | + assert (res.comment == "source0"); |
4122 | + assert (res.uri == "http://url0"); |
4123 | + assert (res.icon == "http://image0"); |
4124 | + assert (res.details_uri == ""); |
4125 | + assert (res.price == null); |
4126 | + assert (res.category == CAT_INDEX_ONLINE); |
4127 | + } |
4128 | + else if (res.title == "title1") |
4129 | + { |
4130 | + got_video[1] = true; |
4131 | + assert (res.comment == "source1"); |
4132 | + assert (res.uri == "http://url1"); |
4133 | + assert (res.icon == "http://image1"); |
4134 | + assert (res.details_uri == "http://details1"); |
4135 | + assert (res.price == null); |
4136 | + assert (res.category == CAT_INDEX_ONLINE); |
4137 | + } |
4138 | + else |
4139 | + { |
4140 | + assert (1 == 0); // this shouldn't happen |
4141 | + } |
4142 | + } |
4143 | + |
4144 | + assert (got_video[0] == true); |
4145 | + assert (got_video[1] == true); |
4146 | + } |
4147 | + |
4148 | + internal static void test_join_array () |
4149 | + { |
4150 | + var array = new Json.Array (); |
4151 | + array.add_string_element ("abc"); |
4152 | + array.add_string_element ("def"); |
4153 | + |
4154 | + assert (UbuntuVideoSearch.join_array (array, ", ") == "abc, def"); |
4155 | + } |
4156 | + |
4157 | + internal static void test_process_invalid_details_results () |
4158 | + { |
4159 | + bool got_excp = false; |
4160 | + try |
4161 | + { |
4162 | + UbuntuVideoSearch.process_details_results (","); |
4163 | + } |
4164 | + catch (Error e) |
4165 | + { |
4166 | + got_excp = true; |
4167 | + } |
4168 | + assert (got_excp == true); |
4169 | + } |
4170 | + |
4171 | + internal static void test_process_details_results () |
4172 | + { |
4173 | + string datadir = Config.TESTDATADIR; |
4174 | + uint8[] contents; |
4175 | + assert (File.new_for_path (@"$datadir/videosearch_details1.txt").load_contents (null, out contents, null) == true); |
4176 | + |
4177 | + var video = UbuntuVideoSearch.process_details_results ((string)contents); |
4178 | + |
4179 | + assert (video.title == "title1"); |
4180 | + assert (video.description == "a movie"); |
4181 | + assert (video.image == "http://image1"); |
4182 | + assert (video.directors.length == 2); |
4183 | + assert (video.directors[0] == "director1"); |
4184 | + assert (video.directors[1] == "director2"); |
4185 | + assert (video.duration == 91); |
4186 | + assert (video.genres.length == 2); |
4187 | + assert (video.genres[0] == "genre1"); |
4188 | + assert (video.genres[1] == "genre2"); |
4189 | + assert (video.starring == "star1, star2"); |
4190 | + assert (video.source == "source1"); |
4191 | + assert (video.release_date == "2010"); |
4192 | + assert (video.uploaded_by == "uploader1"); |
4193 | + assert (video.price == "$1"); |
4194 | + assert (video.date_uploaded == "2012-06-06T21:55:12.000Z"); |
4195 | + } |
4196 | +} |
4197 | |
4198 | === added file 'tests/unit/test-utils.vala' |
4199 | --- tests/unit/test-utils.vala 1970-01-01 00:00:00 +0000 |
4200 | +++ tests/unit/test-utils.vala 2013-02-19 17:19:01 +0000 |
4201 | @@ -0,0 +1,60 @@ |
4202 | +/* |
4203 | + * Copyright (C) 2012 Canonical Ltd |
4204 | + * |
4205 | + * This program is free software: you can redistribute it and/or modify |
4206 | + * it under the terms of the GNU General Public License version 3 as |
4207 | + * published by the Free Software Foundation. |
4208 | + * |
4209 | + * This program is distributed in the hope that it will be useful, |
4210 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4211 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4212 | + * GNU General Public License for more details. |
4213 | + * |
4214 | + * You should have received a copy of the GNU General Public License |
4215 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4216 | + * |
4217 | + * Authored by Pawel Stolowski <pawel.stolowski@canonical.com> |
4218 | + */ |
4219 | + |
4220 | +namespace Unity.VideoLens |
4221 | +{ |
4222 | + public static int main (string[] args) |
4223 | + { |
4224 | + Test.init (ref args); |
4225 | + Test.add_data_func ("/Utils/Gcd", test_gcd); |
4226 | + Test.add_data_func ("/Utils/IsRegularFile", test_is_regular_file); |
4227 | + Test.add_data_func ("/Utils/GetName", test_get_name); |
4228 | + Test.add_data_func ("/Utils/IsVideo", test_is_video); |
4229 | + Test.run (); |
4230 | + return 0; |
4231 | + } |
4232 | + |
4233 | + internal static void test_gcd () |
4234 | + { |
4235 | + assert (Utils.gcd (1, 1) == 1); |
4236 | + assert (Utils.gcd (2, 1) == 1); |
4237 | + assert (Utils.gcd (10, 2) == 2); |
4238 | + assert (Utils.gcd (2, 10) == 2); |
4239 | + assert (Utils.gcd (20, 15) == 5); |
4240 | + } |
4241 | + |
4242 | + internal static void test_is_regular_file () |
4243 | + { |
4244 | + assert (Utils.is_regular_file ("/etc/passwd") == true); |
4245 | + assert (Utils.is_regular_file ("/dev/null") == false); |
4246 | + assert (Utils.is_regular_file ("/non-existing-file") == false); |
4247 | + } |
4248 | + |
4249 | + internal static void test_get_name () |
4250 | + { |
4251 | + assert (Utils.get_name ("/etc/passwd") == "passwd"); |
4252 | + } |
4253 | + |
4254 | + internal static void test_is_video () |
4255 | + { |
4256 | + var video_dir = Config.TESTDATADIR; |
4257 | + |
4258 | + assert (Utils.is_video ("/etc/passwd") == false); |
4259 | + assert (Utils.is_video (@"$video_dir/video1.avi") == true); |
4260 | + } |
4261 | +} |
4262 | \ No newline at end of file |
4263 | |
4264 | === added file 'tests/unit/thumbnailer-mock.vala' |
4265 | --- tests/unit/thumbnailer-mock.vala 1970-01-01 00:00:00 +0000 |
4266 | +++ tests/unit/thumbnailer-mock.vala 2013-02-19 17:19:01 +0000 |
4267 | @@ -0,0 +1,29 @@ |
4268 | +/* |
4269 | + * Copyright (C) 2012 Canonical Ltd |
4270 | + * |
4271 | + * This program is free software: you can redistribute it and/or modify |
4272 | + * it under the terms of the GNU General Public License version 3 as |
4273 | + * published by the Free Software Foundation. |
4274 | + * |
4275 | + * This program is distributed in the hope that it will be useful, |
4276 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4277 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4278 | + * GNU General Public License for more details. |
4279 | + * |
4280 | + * You should have received a copy of the GNU General Public License |
4281 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4282 | + * |
4283 | + * Authored by Pawel Stolowski <pawel.stolowski@canonical.com> |
4284 | + * |
4285 | + */ |
4286 | + |
4287 | +namespace Unity.VideoLens |
4288 | +{ |
4289 | + public class Thumbnailer |
4290 | + { |
4291 | + public string get_icon (string video_file) |
4292 | + { |
4293 | + return "mock-icon"; |
4294 | + } |
4295 | + } |
4296 | +} |
4297 | \ No newline at end of file |
4298 | |
4299 | === added directory 'vapi' |
4300 | === added file 'vapi/Makefile.am' |
4301 | --- vapi/Makefile.am 1970-01-01 00:00:00 +0000 |
4302 | +++ vapi/Makefile.am 2013-02-19 17:19:01 +0000 |
4303 | @@ -0,0 +1,5 @@ |
4304 | +NULL = |
4305 | +BUILT_SOURCES = |
4306 | +CLEANFILES = |
4307 | +EXTRA_DIST = libsoup-gnome-2.4.vapi |
4308 | + |
4309 | |
4310 | === added file 'vapi/libsoup-gnome-2.4.vapi' |
4311 | --- vapi/libsoup-gnome-2.4.vapi 1970-01-01 00:00:00 +0000 |
4312 | +++ vapi/libsoup-gnome-2.4.vapi 2013-02-19 17:19:01 +0000 |
4313 | @@ -0,0 +1,8 @@ |
4314 | +[CCode (cprefix = "Soup", gir_namespace = "SoupGNOME", gir_version = "2.4", lower_case_cprefix = "soup_")] |
4315 | +namespace SoupGNOME { |
4316 | + [CCode (cheader_filename = "libsoup/soup-gnome.h", type_id = "soup_proxy_resolver_gnome_get_type ()")] |
4317 | + public class ProxyResolverGNOME : Soup.ProxyResolverDefault, Soup.ProxyURIResolver, Soup.SessionFeature { |
4318 | + [CCode (has_construct_function = false)] |
4319 | + protected ProxyResolverGNOME (); |
4320 | + } |
4321 | +} |
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) => { search_ async.begin (search, search_type, cancellable);
1149 + update_
1150 + });
Parse error, the brackets don't match indentation :P (present in multiple lambda instances)
1158 + preferences. notify[ "remote- content- search" ].connect ((obj, pspec) => { changed (SearchType. GLOBAL) ;
1159 + queue_search_
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)