package free.yhc.netmbuddy.share; import android.database.SQLException; import android.graphics.Bitmap; import android.support.annotation.NonNull; import org.json.JSONException; import java.io.IOException; import java.net.ConnectException; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import free.yhc.baselib.Logger; import free.yhc.baselib.async.HelperHandler; import free.yhc.baselib.async.Task; import free.yhc.baselib.async.TmTask; import free.yhc.baselib.async.TmTaskGroup; import free.yhc.abaselib.util.ImgUtil; import free.yhc.netmbuddy.core.PolicyConstant; import free.yhc.netmbuddy.core.TaskManager; import free.yhc.netmbuddy.db.ColPlaylist; import free.yhc.netmbuddy.db.DB; import free.yhc.netmbuddy.db.DMVideo; import free.yhc.netmbuddy.utils.Util; import free.yhc.netmbuddy.utils.YTUtil; public class ImportPlaylistTask extends TmTask<ImportJob.Result> { private static final boolean DBG = Logger.DBG_DEFAULT; private static final Logger P = Logger.create(ImportPlaylistTask.class, Logger.LOGLV_DEFAULT); private final DataModel.Playlist mPl; private final AtomicReference<TmTaskGroup> mTmtg = new AtomicReference<>(null); /////////////////////////////////////////////////////////////////////////// // // // /////////////////////////////////////////////////////////////////////////// static class ImportVideoTask extends TmTask<Void> { private final long _mPlid; // playlist id to import to private final DataModel.Video _mV; // Video JSON Object. private final AtomicInteger _mSuccess; private final AtomicInteger _mFail; ImportVideoTask(DataModel.Video video, long plid, AtomicInteger success, AtomicInteger fail) { _mPlid = plid; _mV = video; _mSuccess = success; _mFail = fail; } @Override protected Void doAsync() throws JSONException, IOException { Boolean success = false; try { if (!YTUtil.verifyYoutubeVideoId(_mV.ytvid)) throw new JSONException(""); DMVideo v = new DMVideo(); v.ytvid = _mV.ytvid; if (!YTUtil.fillYtDataAndThumbnail(v)) throw new ConnectException(); v.setPreferenceData(_mV.volume, _mV.bookmarks); v.setPreferenceDataIfNotSet(PolicyConstant.DEFAULT_VIDEO_VOLUME, ""); if (DB.Err.NO_ERR != DB.get().insertVideoToPlaylist(_mPlid, v)) throw new UnsupportedOperationException(); success = true; return null; } finally { if (success) _mSuccess.incrementAndGet(); else _mFail.incrementAndGet(); } } } /////////////////////////////////////////////////////////////////////////// // // // /////////////////////////////////////////////////////////////////////////// private static String getUniqueSharePlaylistTitle(String shareTitle) { DB db = DB.get(); String baseTitle = shareTitle + "- share"; int i = 0; //noinspection StatementWithEmptyBody while (db.containsPlaylist(baseTitle + i++)); return baseTitle + --i; } /////////////////////////////////////////////////////////////////////////// // // // /////////////////////////////////////////////////////////////////////////// @Override protected void onEarlyCancel(boolean started, Object param) { super.onEarlyCancel(started, param); TmTaskGroup ttg = mTmtg.get(); if (null != ttg) ttg.cancel(); } /////////////////////////////////////////////////////////////////////////// // // // /////////////////////////////////////////////////////////////////////////// protected ImportPlaylistTask(@NonNull DataModel.Playlist pl) { mPl = pl; } /////////////////////////////////////////////////////////////////////////// // // // /////////////////////////////////////////////////////////////////////////// @NonNull public static ImportPlaylistTask create(@NonNull DataModel.Playlist pl) { return new ImportPlaylistTask(pl); } /** */ @Override @NonNull protected ImportJob.Result doAsync() throws SQLException, InterruptedException { final DB db = DB.get(); final ImportJob.Result ir = new ImportJob.Result(); /* Create jobs to run in parallel. *********************************/ String title = getUniqueSharePlaylistTitle(mPl.title); final long plid = db.insertPlaylist(title); if (plid < 0) throw new SQLException(); TmTask[] tasks = new TmTask[mPl.videos.length + 1]; int ti = 0; TmTask<Void> t = new TmTask<Void>() { @Override protected Void doAsync() throws JSONException, IOException { if (!Util.isValidValue(mPl.thumbnail_ytvid)) return null; Bitmap bm = YTUtil.getYtThumbnail(mPl.thumbnail_ytvid); if (null == bm) return null; byte[] bmdata = ImgUtil.compressToJpeg(bm); db.updatePlaylist(plid, new ColPlaylist[] { ColPlaylist.THUMBNAIL, ColPlaylist.THUMBNAIL_YTVID }, new Object[] { bmdata, mPl.thumbnail_ytvid }); bm.recycle(); // Ignore if fail to load thumbnail - it's very minor for usecase. return null; } }; tasks[ti++] = t; for (DataModel.Video v : mPl.videos) tasks[ti++] = new ImportVideoTask(v, plid, ir.success, ir.fail); TmTaskGroup.Builder<TmTaskGroup.Builder> ttgb = new TmTaskGroup.Builder<>(TaskManager.get()); ttgb.setTasks(tasks); TmTaskGroup ttg = ttgb.create(); ttg.addEventListener(HelperHandler.get(), new TmTaskGroup.EventListener<TmTaskGroup, Task>() { @Override public void onProgressInit(@NonNull TmTaskGroup ttg, long maxProgress) { ImportPlaylistTask.this.publishProgressInit(maxProgress); } @Override public void onProgress(@NonNull TmTaskGroup ttg, long progress) { ImportPlaylistTask.this.publishProgress(progress); } }); mTmtg.set(ttg); if (isCancel()) throw new InterruptedException(); // Below code is NOT run after 'onEarlyCancel()'. try { ttg.startSync(); } catch (InterruptedException e) { throw e; } catch (Exception e) { // Exception is NOT expected here! P.bug(); } return ir; } }